diff --git a/.clang-format b/.clang-format index 1e62ea116..c710347e7 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,14 @@ --- BasedOnStyle: Chromium -IndentWidth: '2' -TabWidth: '2' -UseTab: Always +IndentWidth: '4' +SortIncludes: 'false' +AllowAllArgumentsOnNextLine: 'true' +ColumnLimit: 0 +Cpp11BracedListStyle: 'true' +AllowShortLoopsOnASingleLine: 'true' +AllowShortIfStatementsOnASingleLine: 'true' +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 ... diff --git a/.github/workflows/check_formatting.yml b/.github/workflows/check_formatting.yml index 6a891d6c5..7f35d5c94 100644 --- a/.github/workflows/check_formatting.yml +++ b/.github/workflows/check_formatting.yml @@ -1,12 +1,17 @@ name: Check formatting -on: #[push, pull_request] To be enabled once formatting is fixed in repo - workflow_dispatch: +on: [push, pull_request] -jobs: +jobs: check_date: runs-on: ubuntu-latest name: Check formatting + strategy: + matrix: + path: + - "firmware/common" + - "firmware/application" + - "firmware/baseband" steps: - uses: actions/checkout@v2 - name: print latest_commit @@ -14,5 +19,5 @@ jobs: - name: clang-format Check uses: jidicula/clang-format-action@v4.11.0 with: - check-path: "firmware" + check-path: ${{ matrix.path }} fallback-style: Chromium diff --git a/firmware/application/app_settings.cpp b/firmware/application/app_settings.cpp index 9a04d1478..1e1870f19 100644 --- a/firmware/application/app_settings.cpp +++ b/firmware/application/app_settings.cpp @@ -32,84 +32,80 @@ namespace std { int app_settings::load(std::string application, AppSettings* settings) { - - if (portapack::persistent_memory::load_app_settings()) { - file_path = folder+"/"+application+".ini"; - - auto error = settings_file.open(file_path); - if (!error.is_valid()) { - auto error = settings_file.read(file_content, std::min((int)settings_file.size(), MAX_FILE_CONTENT_SIZE)); - - settings->baseband_bandwidth=std::app_settings::read_long_long(file_content, "baseband_bandwidth="); - settings->channel_bandwidth=std::app_settings::read_long_long(file_content, "channel_bandwidth="); - settings->lna=std::app_settings::read_long_long(file_content, "lna="); - settings->modulation=std::app_settings::read_long_long(file_content, "modulation="); - settings->rx_amp=std::app_settings::read_long_long(file_content, "rx_amp="); - settings->rx_frequency=std::app_settings::read_long_long(file_content, "rx_frequency="); - settings->sampling_rate=std::app_settings::read_long_long(file_content, "sampling_rate="); - settings->vga=std::app_settings::read_long_long(file_content, "vga="); - settings->tx_amp=std::app_settings::read_long_long(file_content, "tx_amp="); - settings->tx_frequency=std::app_settings::read_long_long(file_content, "tx_frequency="); - settings->tx_gain=std::app_settings::read_long_long(file_content, "tx_gain="); - settings->step=std::app_settings::read_long_long(file_content, "step="); - settings->modulation=std::app_settings::read_long_long(file_content, "modulation="); - settings->am_config_index=std::app_settings::read_long_long(file_content, "am_config_index="); - settings->nbfm_config_index=std::app_settings::read_long_long(file_content, "nbfm_config_index="); - settings->wfm_config_index=std::app_settings::read_long_long(file_content, "wfm_config_index="); - settings->squelch=std::app_settings::read_long_long(file_content, "squelch="); - - rc = SETTINGS_OK; - } - else rc = SETTINGS_UNABLE_TO_LOAD; - } - else rc = SETTINGS_DISABLED; - return(rc); + if (portapack::persistent_memory::load_app_settings()) { + file_path = folder + "/" + application + ".ini"; + + auto error = settings_file.open(file_path); + if (!error.is_valid()) { + auto error = settings_file.read(file_content, std::min((int)settings_file.size(), MAX_FILE_CONTENT_SIZE)); + + settings->baseband_bandwidth = std::app_settings::read_long_long(file_content, "baseband_bandwidth="); + settings->channel_bandwidth = std::app_settings::read_long_long(file_content, "channel_bandwidth="); + settings->lna = std::app_settings::read_long_long(file_content, "lna="); + settings->modulation = std::app_settings::read_long_long(file_content, "modulation="); + settings->rx_amp = std::app_settings::read_long_long(file_content, "rx_amp="); + settings->rx_frequency = std::app_settings::read_long_long(file_content, "rx_frequency="); + settings->sampling_rate = std::app_settings::read_long_long(file_content, "sampling_rate="); + settings->vga = std::app_settings::read_long_long(file_content, "vga="); + settings->tx_amp = std::app_settings::read_long_long(file_content, "tx_amp="); + settings->tx_frequency = std::app_settings::read_long_long(file_content, "tx_frequency="); + settings->tx_gain = std::app_settings::read_long_long(file_content, "tx_gain="); + settings->step = std::app_settings::read_long_long(file_content, "step="); + settings->modulation = std::app_settings::read_long_long(file_content, "modulation="); + settings->am_config_index = std::app_settings::read_long_long(file_content, "am_config_index="); + settings->nbfm_config_index = std::app_settings::read_long_long(file_content, "nbfm_config_index="); + settings->wfm_config_index = std::app_settings::read_long_long(file_content, "wfm_config_index="); + settings->squelch = std::app_settings::read_long_long(file_content, "squelch="); + + rc = SETTINGS_OK; + } else + rc = SETTINGS_UNABLE_TO_LOAD; + } else + rc = SETTINGS_DISABLED; + return (rc); } int app_settings::save(std::string application, AppSettings* settings) { - - if (portapack::persistent_memory::save_app_settings()) { - file_path = folder+"/"+application+".ini"; - make_new_directory(folder); - - auto error = settings_file.create(file_path); - if (!error.is_valid()) { - // Save common setting - settings_file.write_line("baseband_bandwidth="+to_string_dec_uint(portapack::receiver_model.baseband_bandwidth())); - settings_file.write_line("channel_bandwidth="+to_string_dec_uint(portapack::transmitter_model.channel_bandwidth())); - settings_file.write_line("lna="+to_string_dec_uint(portapack::receiver_model.lna())); - settings_file.write_line("rx_amp="+to_string_dec_uint(portapack::receiver_model.rf_amp())); - settings_file.write_line("sampling_rate="+to_string_dec_uint(portapack::receiver_model.sampling_rate())); - settings_file.write_line("tx_amp="+to_string_dec_uint(portapack::transmitter_model.rf_amp())); - settings_file.write_line("tx_gain="+to_string_dec_uint(portapack::transmitter_model.tx_gain())); - settings_file.write_line("vga="+to_string_dec_uint(portapack::receiver_model.vga())); - // Save other settings from struct - settings_file.write_line("rx_frequency="+to_string_dec_uint(settings->rx_frequency)); - settings_file.write_line("tx_frequency="+to_string_dec_uint(settings->tx_frequency)); - settings_file.write_line("step="+to_string_dec_uint(settings->step)); - settings_file.write_line("modulation="+to_string_dec_uint(settings->modulation)); - settings_file.write_line("am_config_index="+to_string_dec_uint(settings->am_config_index)); - settings_file.write_line("nbfm_config_index="+to_string_dec_uint(settings->nbfm_config_index)); - settings_file.write_line("wfm_config_index="+to_string_dec_uint(settings->wfm_config_index)); - settings_file.write_line("squelch="+to_string_dec_uint(settings->squelch)); - - rc = SETTINGS_OK; - } - else rc = SETTINGS_UNABLE_TO_SAVE; - } - else rc = SETTINGS_DISABLED; - return(rc); + if (portapack::persistent_memory::save_app_settings()) { + file_path = folder + "/" + application + ".ini"; + make_new_directory(folder); + + auto error = settings_file.create(file_path); + if (!error.is_valid()) { + // Save common setting + settings_file.write_line("baseband_bandwidth=" + to_string_dec_uint(portapack::receiver_model.baseband_bandwidth())); + settings_file.write_line("channel_bandwidth=" + to_string_dec_uint(portapack::transmitter_model.channel_bandwidth())); + settings_file.write_line("lna=" + to_string_dec_uint(portapack::receiver_model.lna())); + settings_file.write_line("rx_amp=" + to_string_dec_uint(portapack::receiver_model.rf_amp())); + settings_file.write_line("sampling_rate=" + to_string_dec_uint(portapack::receiver_model.sampling_rate())); + settings_file.write_line("tx_amp=" + to_string_dec_uint(portapack::transmitter_model.rf_amp())); + settings_file.write_line("tx_gain=" + to_string_dec_uint(portapack::transmitter_model.tx_gain())); + settings_file.write_line("vga=" + to_string_dec_uint(portapack::receiver_model.vga())); + // Save other settings from struct + settings_file.write_line("rx_frequency=" + to_string_dec_uint(settings->rx_frequency)); + settings_file.write_line("tx_frequency=" + to_string_dec_uint(settings->tx_frequency)); + settings_file.write_line("step=" + to_string_dec_uint(settings->step)); + settings_file.write_line("modulation=" + to_string_dec_uint(settings->modulation)); + settings_file.write_line("am_config_index=" + to_string_dec_uint(settings->am_config_index)); + settings_file.write_line("nbfm_config_index=" + to_string_dec_uint(settings->nbfm_config_index)); + settings_file.write_line("wfm_config_index=" + to_string_dec_uint(settings->wfm_config_index)); + settings_file.write_line("squelch=" + to_string_dec_uint(settings->squelch)); + + rc = SETTINGS_OK; + } else + rc = SETTINGS_UNABLE_TO_SAVE; + } else + rc = SETTINGS_DISABLED; + return (rc); } - long long int app_settings::read_long_long(char* file_content, const char* setting_text) { - auto position = strstr(file_content, (char *)setting_text); - if (position) { - position += strlen((char *)setting_text); - setting_value = strtoll(position, nullptr, 10); - } - return(setting_value); + auto position = strstr(file_content, (char*)setting_text); + if (position) { + position += strlen((char*)setting_text); + setting_value = strtoll(position, nullptr, 10); + } + return (setting_value); } - } /* namespace std */ diff --git a/firmware/application/app_settings.hpp b/firmware/application/app_settings.hpp index 8f6835efd..a620600ef 100644 --- a/firmware/application/app_settings.hpp +++ b/firmware/application/app_settings.hpp @@ -24,7 +24,6 @@ #ifndef __APP_SETTINGS_H__ #define __APP_SETTINGS_H__ - #include #include #include @@ -32,61 +31,49 @@ #include "file.hpp" #include "string_format.hpp" - namespace std { class app_settings { - - - -public: - -#define SETTINGS_OK 0 // settings found -#define SETTINGS_UNABLE_TO_LOAD -1 // settings (file) not found -#define SETTINGS_UNABLE_TO_SAVE -2 // unable to save settings -#define SETTINGS_DISABLED -3 // load/save settings disabled in settings - - - - struct AppSettings { - uint32_t baseband_bandwidth; - uint32_t channel_bandwidth; - uint8_t lna; - uint8_t modulation; - uint8_t rx_amp; - uint32_t rx_frequency; - uint32_t sampling_rate; - uint8_t tx_amp; - uint32_t tx_frequency; - uint8_t tx_gain; - uint8_t vga; - uint32_t step; - uint8_t am_config_index; - uint8_t nbfm_config_index; - uint8_t wfm_config_index; - uint8_t squelch; - }; - - int load(std::string application, AppSettings* settings); - int save(std::string application, AppSettings* settings); - - -private: - + public: +#define SETTINGS_OK 0 // settings found +#define SETTINGS_UNABLE_TO_LOAD -1 // settings (file) not found +#define SETTINGS_UNABLE_TO_SAVE -2 // unable to save settings +#define SETTINGS_DISABLED -3 // load/save settings disabled in settings + + struct AppSettings { + uint32_t baseband_bandwidth; + uint32_t channel_bandwidth; + uint8_t lna; + uint8_t modulation; + uint8_t rx_amp; + uint32_t rx_frequency; + uint32_t sampling_rate; + uint8_t tx_amp; + uint32_t tx_frequency; + uint8_t tx_gain; + uint8_t vga; + uint32_t step; + uint8_t am_config_index; + uint8_t nbfm_config_index; + uint8_t wfm_config_index; + uint8_t squelch; + }; + + int load(std::string application, AppSettings* settings); + int save(std::string application, AppSettings* settings); + + private: #define MAX_FILE_CONTENT_SIZE 1000 - char file_content[MAX_FILE_CONTENT_SIZE] = {}; - std::string file_path = ""; - std::string folder = "SETTINGS"; - int rc = SETTINGS_OK; - File settings_file { }; - long long int setting_value {} ; - - long long int read_long_long(char* file_content, const char* setting_text); - - -}; // class app_settings -} // namespace std + char file_content[MAX_FILE_CONTENT_SIZE] = {}; + std::string file_path = ""; + std::string folder = "SETTINGS"; + int rc = SETTINGS_OK; + File settings_file{}; + long long int setting_value{}; + long long int read_long_long(char* file_content, const char* setting_text); +}; // class app_settings +} // namespace std -#endif/*__APP_SETTINGS_H__*/ +#endif /*__APP_SETTINGS_H__*/ diff --git a/firmware/application/apps/acars_app.cpp b/firmware/application/apps/acars_app.cpp index 39302b1e1..d69dccb4b 100644 --- a/firmware/application/apps/acars_app.cpp +++ b/firmware/application/apps/acars_app.cpp @@ -32,118 +32,116 @@ using namespace acars; #include "utility.hpp" void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequency) { - (void)frequency; - std::string entry { }; //= "Raw: F:" + to_string_dec_uint(frequency) + "Hz "; - entry.reserve(256); - - // Raw hex dump of all the bytes - //for (size_t c = 0; c < packet.length(); c += 32) - // entry += to_string_hex(packet.read(c, 32), 8) + " "; - - for (size_t c = 0; c < 256; c += 32) - entry += to_string_bin(packet.read(c, 32), 32); - - log_file.write_entry(packet.received_at(), entry); + (void)frequency; + std::string entry{}; //= "Raw: F:" + to_string_dec_uint(frequency) + "Hz "; + entry.reserve(256); + + // Raw hex dump of all the bytes + // for (size_t c = 0; c < packet.length(); c += 32) + // entry += to_string_hex(packet.read(c, 32), 8) + " "; + + for (size_t c = 0; c < 256; c += 32) + entry += to_string_bin(packet.read(c, 32), 32); + + log_file.write_entry(packet.received_at(), entry); } /*void ACARSLogger::log_decoded( - const acars::Packet& packet, - const std::string text) { - - log_file.write_entry(packet.timestamp(), text); + const acars::Packet& packet, + const std::string text) { + + log_file.write_entry(packet.timestamp(), text); }*/ namespace ui { void ACARSAppView::update_freq(rf::Frequency f) { - set_target_frequency(f); - portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ? + set_target_frequency(f); + portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ? } ACARSAppView::ACARSAppView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_acars); - - add_children({ - &rssi, - &channel, - &field_rf_amp, - &field_lna, - &field_vga, - &field_frequency, - &check_log, - &console - }); - - receiver_model.set_sampling_rate(2457600); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.enable(); - - field_frequency.set_value(receiver_model.tuning_frequency()); - update_freq(receiver_model.tuning_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - check_log.set_value(logging); - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - }; - - logger = std::make_unique(); - if (logger) - logger->append(LOG_ROOT_DIR "/ACARS.TXT"); + baseband::run_image(portapack::spi_flash::image_tag_acars); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &check_log, + &console}); + + receiver_model.set_sampling_rate(2457600); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.enable(); + + field_frequency.set_value(receiver_model.tuning_frequency()); + update_freq(receiver_model.tuning_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + logger = std::make_unique(); + if (logger) + logger->append(LOG_ROOT_DIR "/ACARS.TXT"); } ACARSAppView::~ACARSAppView() { - receiver_model.disable(); - baseband::shutdown(); + receiver_model.disable(); + baseband::shutdown(); } void ACARSAppView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void ACARSAppView::on_packet(const acars::Packet& packet) { - std::string console_info; - - /*if (!packet.is_valid()) { - console_info = to_string_datetime(packet.received_at(), HMS); - console_info += " INVALID"; - - console.writeln(console_info); - } else { - console_info = to_string_datetime(packet.received_at(), HMS); - console_info += ":" + to_string_bin(packet.read(0, 32), 32); - //console_info += " REG:" + packet.registration_number(); - - console.writeln(console_info); - }*/ - - packet_counter++; - if (packet_counter % 10 == 0) - console.writeln("Block #" + to_string_dec_uint(packet_counter)); - - // Log raw data whatever it contains - if (logger && logging) - logger->log_raw_data(packet, target_frequency()); + std::string console_info; + + /*if (!packet.is_valid()) { + console_info = to_string_datetime(packet.received_at(), HMS); + console_info += " INVALID"; + + console.writeln(console_info); + } else { + console_info = to_string_datetime(packet.received_at(), HMS); + console_info += ":" + to_string_bin(packet.read(0, 32), 32); + //console_info += " REG:" + packet.registration_number(); + + console.writeln(console_info); + }*/ + + packet_counter++; + if (packet_counter % 10 == 0) + console.writeln("Block #" + to_string_dec_uint(packet_counter)); + + // Log raw data whatever it contains + if (logger && logging) + logger->log_raw_data(packet, target_frequency()); } void ACARSAppView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - receiver_model.set_tuning_frequency(new_value); + target_frequency_ = new_value; + receiver_model.set_tuning_frequency(new_value); } uint32_t ACARSAppView::target_frequency() const { - return target_frequency_; + return target_frequency_; } } /* namespace ui */ diff --git a/firmware/application/apps/acars_app.hpp b/firmware/application/apps/acars_app.hpp index 08fc7f76a..2075f4c46 100644 --- a/firmware/application/apps/acars_app.hpp +++ b/firmware/application/apps/acars_app.hpp @@ -32,85 +32,78 @@ #include "acars_packet.hpp" class ACARSLogger { -public: - Optional append(const std::string& filename) { - return log_file.append(filename); - } - - void log_raw_data(const acars::Packet& packet, const uint32_t frequency); - //void log_decoded(const acars::Packet& packet, const std::string text); - -private: - LogFile log_file { }; + public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const acars::Packet& packet, const uint32_t frequency); + // void log_decoded(const acars::Packet& packet, const std::string text); + + private: + LogFile log_file{}; }; namespace ui { class ACARSAppView : public View { -public: - ACARSAppView(NavigationView& nav); - ~ACARSAppView(); - - void focus() override; - - std::string title() const override { return "ACARS (WIP)"; }; - -private: - bool logging { false }; - uint32_t packet_counter { 0 }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 8 }, - }; - Checkbox check_log { - { 22 * 8, 21 }, - 3, - "LOG", - true - }; - - Console console { - { 0, 3 * 16, 240, 256 } - }; - - std::unique_ptr logger { }; - - uint32_t target_frequency_ { }; - - void update_freq(rf::Frequency f); - - void on_packet(const acars::Packet& packet); - - uint32_t target_frequency() const; - void set_target_frequency(const uint32_t new_value); - - MessageHandlerRegistration message_handler_packet { - Message::ID::ACARSPacket, - [this](Message* const p) { - const auto message = static_cast(p); - const acars::Packet packet { message->packet }; - this->on_packet(packet); - } - }; - + public: + ACARSAppView(NavigationView& nav); + ~ACARSAppView(); + + void focus() override; + + std::string title() const override { return "ACARS (WIP)"; }; + + private: + bool logging{false}; + uint32_t packet_counter{0}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 8}, + }; + Checkbox check_log{ + {22 * 8, 21}, + 3, + "LOG", + true}; + + Console console{ + {0, 3 * 16, 240, 256}}; + + std::unique_ptr logger{}; + + uint32_t target_frequency_{}; + + void update_freq(rf::Frequency f); + + void on_packet(const acars::Packet& packet); + + uint32_t target_frequency() const; + void set_target_frequency(const uint32_t new_value); + + MessageHandlerRegistration message_handler_packet{ + Message::ID::ACARSPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const acars::Packet packet{message->packet}; + this->on_packet(packet); + }}; }; } /* namespace ui */ -#endif/*__ACARS_APP_H__*/ +#endif /*__ACARS_APP_H__*/ diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 49fac752b..992435ca9 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -35,440 +35,451 @@ namespace ais { namespace format { static std::string latlon_abs_normalized(const int32_t normalized, const char suffixes[2]) { - const auto suffix = suffixes[(normalized < 0) ? 0 : 1]; - const uint32_t normalized_abs = std::abs(normalized); - const uint32_t t = (normalized_abs * 5) / 3; - const uint32_t degrees = t / (100 * 10000); - const uint32_t fraction = t % (100 * 10000); - return to_string_dec_uint(degrees) + "." + to_string_dec_uint(fraction, 6, '0') + suffix; + const auto suffix = suffixes[(normalized < 0) ? 0 : 1]; + const uint32_t normalized_abs = std::abs(normalized); + const uint32_t t = (normalized_abs * 5) / 3; + const uint32_t degrees = t / (100 * 10000); + const uint32_t fraction = t % (100 * 10000); + return to_string_dec_uint(degrees) + "." + to_string_dec_uint(fraction, 6, '0') + suffix; } static std::string latlon(const Latitude latitude, const Longitude longitude) { - if( latitude.is_valid() && longitude.is_valid() ) { - return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE"); - } else if( latitude.is_not_available() && longitude.is_not_available() ) { - return "not available"; - } else { - return "invalid"; - } + if (latitude.is_valid() && longitude.is_valid()) { + return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE"); + } else if (latitude.is_not_available() && longitude.is_not_available()) { + return "not available"; + } else { + return "invalid"; + } } static float latlon_float(const int32_t normalized) { - return ((((float) normalized) * 5) / 3) / (100 * 10000); + return ((((float)normalized) * 5) / 3) / (100 * 10000); } static std::string mmsi( - const ais::MMSI& mmsi -) { - return to_string_dec_uint(mmsi, 9, '0'); // MMSI is always is always 9 characters pre-padded with zeros + const ais::MMSI& mmsi) { + return to_string_dec_uint(mmsi, 9, '0'); // MMSI is always is always 9 characters pre-padded with zeros } - static std::string mid( - const ais::MMSI& mmsi -) { - std::database db; - std::string mid_code = ""; - std::database::MidDBRecord mid_record = {}; - int return_code = 0; - - // Try getting the country name from mids.db using MID code for given MMSI - mid_code = to_string_dec_uint(mmsi, 9, ' ').substr(0, 3); - return_code = db.retrieve_mid_record(&mid_record, mid_code); - switch(return_code) { - case DATABASE_RECORD_FOUND: return mid_record.country; - case DATABASE_NOT_FOUND: return "No mids.db file"; - default: return ""; - } -} - -static std::string text(const std::string &text) { - size_t end = text.find_last_not_of("@"); + const ais::MMSI& mmsi) { + std::database db; + std::string mid_code = ""; + std::database::MidDBRecord mid_record = {}; + int return_code = 0; + + // Try getting the country name from mids.db using MID code for given MMSI + mid_code = to_string_dec_uint(mmsi, 9, ' ').substr(0, 3); + return_code = db.retrieve_mid_record(&mid_record, mid_code); + switch (return_code) { + case DATABASE_RECORD_FOUND: + return mid_record.country; + case DATABASE_NOT_FOUND: + return "No mids.db file"; + default: + return ""; + } +} + +static std::string text(const std::string& text) { + size_t end = text.find_last_not_of("@"); return (end == std::string::npos) ? "" : text.substr(0, end + 1); - } static std::string navigational_status(const unsigned int value) { - switch(value) { - case 0: return "under way w/engine"; - case 1: return "at anchor"; - case 2: return "not under command"; - case 3: return "restricted maneuv"; - case 4: return "constrained draught"; - case 5: return "moored"; - case 6: return "aground"; - case 7: return "fishing"; - case 8: return "sailing"; - case 9: case 10: case 13: return "reserved"; - case 11: return "towing astern"; - case 12: return "towing ahead/along"; - case 14: return "SART/MOB/EPIRB"; - case 15: return "undefined"; - default: return "unknown"; - } + switch (value) { + case 0: + return "under way w/engine"; + case 1: + return "at anchor"; + case 2: + return "not under command"; + case 3: + return "restricted maneuv"; + case 4: + return "constrained draught"; + case 5: + return "moored"; + case 6: + return "aground"; + case 7: + return "fishing"; + case 8: + return "sailing"; + case 9: + case 10: + case 13: + return "reserved"; + case 11: + return "towing astern"; + case 12: + return "towing ahead/along"; + case 14: + return "SART/MOB/EPIRB"; + case 15: + return "undefined"; + default: + return "unknown"; + } } static std::string rate_of_turn(const RateOfTurn value) { - switch(value) { - case -128: return "not available"; - case -127: return "left >5 deg/30sec"; - case 0: return "0 deg/min"; - case 127: return "right >5 deg/30sec"; - default: - { - std::string result = (value < 0) ? "left " : "right "; - const float value_deg_sqrt = value / 4.733f; - const int32_t value_deg = value_deg_sqrt * value_deg_sqrt; - result += to_string_dec_uint(value_deg); - result += " deg/min"; - return result; - } - } + switch (value) { + case -128: + return "not available"; + case -127: + return "left >5 deg/30sec"; + case 0: + return "0 deg/min"; + case 127: + return "right >5 deg/30sec"; + default: { + std::string result = (value < 0) ? "left " : "right "; + const float value_deg_sqrt = value / 4.733f; + const int32_t value_deg = value_deg_sqrt * value_deg_sqrt; + result += to_string_dec_uint(value_deg); + result += " deg/min"; + return result; + } + } } static std::string speed_over_ground(const SpeedOverGround value) { - if( value == 1023 ) { - return "not available"; - } else if( value == 1022 ) { - return ">= 102.2 knots"; - } else { - return to_string_dec_uint(value / 10) + "." + to_string_dec_uint(value % 10, 1) + " knots"; - } + if (value == 1023) { + return "not available"; + } else if (value == 1022) { + return ">= 102.2 knots"; + } else { + return to_string_dec_uint(value / 10) + "." + to_string_dec_uint(value % 10, 1) + " knots"; + } } static std::string course_over_ground(const CourseOverGround value) { - if( value > 3600 ) { - return "invalid"; - } else if( value == 3600 ) { - return "not available"; - } else { - return to_string_dec_uint(value / 10) + "." + to_string_dec_uint(value % 10, 1) + " deg"; - } + if (value > 3600) { + return "invalid"; + } else if (value == 3600) { + return "not available"; + } else { + return to_string_dec_uint(value / 10) + "." + to_string_dec_uint(value % 10, 1) + " deg"; + } } static std::string true_heading(const TrueHeading value) { - if( value == 511 ) { - return "not available"; - } else if( value > 359 ) { - return "invalid"; - } else { - return to_string_dec_uint(value) + " deg"; - } + if (value == 511) { + return "not available"; + } else if (value > 359) { + return "invalid"; + } else { + return to_string_dec_uint(value) + " deg"; + } } } /* namespace format */ } /* namespace ais */ void AISLogger::on_packet(const ais::Packet& packet) { - // TODO: Unstuff here, not in baseband! - std::string entry; - entry.reserve((packet.length() + 3) / 4); + // TODO: Unstuff here, not in baseband! + std::string entry; + entry.reserve((packet.length() + 3) / 4); - for(size_t i=0; i= 10) ? ('W' + nibble) : ('0' + nibble); - } + for (size_t i = 0; i < packet.length(); i += 4) { + const auto nibble = packet.read(i, 4); + entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble); + } - log_file.write_entry(packet.received_at(), entry); + log_file.write_entry(packet.received_at(), entry); } void AISRecentEntry::update(const ais::Packet& packet) { - received_count++; - - switch(packet.message_id()) { - case 1: - case 2: - case 3: - navigational_status = packet.read(38, 4); - last_position.rate_of_turn = packet.read(42, 8); - last_position.speed_over_ground = packet.read(50, 10); - last_position.timestamp = packet.received_at(); - last_position.latitude = packet.latitude(89); - last_position.longitude = packet.longitude(61); - last_position.course_over_ground = packet.read(116, 12); - last_position.true_heading = packet.read(128, 9); - break; - - case 4: - last_position.timestamp = packet.received_at(); - last_position.latitude = packet.latitude(107); - last_position.longitude = packet.longitude(79); - break; - - case 5: - call_sign = packet.text(70, 7); - name = packet.text(112, 20); - destination = packet.text(302, 20); - break; - - case 18: - last_position.timestamp = packet.received_at(); - last_position.speed_over_ground = packet.read(46, 10); - last_position.latitude = packet.latitude(85); - last_position.longitude = packet.longitude(57); - last_position.course_over_ground = packet.read(112, 12); - last_position.true_heading = packet.read(124, 9); - break; - - case 21: - name = packet.text(43, 20); - last_position.timestamp = packet.received_at(); - last_position.latitude = packet.latitude(192); - last_position.longitude = packet.longitude(164); - break; - - case 24: - switch (packet.read(38, 2)) - { - case 0: - name = packet.text(40, 20); - break; - - case 1: - call_sign = packet.text(90, 7); - break; - - default: - break; - } - - break; - - default: - break; - } + received_count++; + + switch (packet.message_id()) { + case 1: + case 2: + case 3: + navigational_status = packet.read(38, 4); + last_position.rate_of_turn = packet.read(42, 8); + last_position.speed_over_ground = packet.read(50, 10); + last_position.timestamp = packet.received_at(); + last_position.latitude = packet.latitude(89); + last_position.longitude = packet.longitude(61); + last_position.course_over_ground = packet.read(116, 12); + last_position.true_heading = packet.read(128, 9); + break; + + case 4: + last_position.timestamp = packet.received_at(); + last_position.latitude = packet.latitude(107); + last_position.longitude = packet.longitude(79); + break; + + case 5: + call_sign = packet.text(70, 7); + name = packet.text(112, 20); + destination = packet.text(302, 20); + break; + + case 18: + last_position.timestamp = packet.received_at(); + last_position.speed_over_ground = packet.read(46, 10); + last_position.latitude = packet.latitude(85); + last_position.longitude = packet.longitude(57); + last_position.course_over_ground = packet.read(112, 12); + last_position.true_heading = packet.read(124, 9); + break; + + case 21: + name = packet.text(43, 20); + last_position.timestamp = packet.received_at(); + last_position.latitude = packet.latitude(192); + last_position.longitude = packet.longitude(164); + break; + + case 24: + switch (packet.read(38, 2)) { + case 0: + name = packet.text(40, 20); + break; + + case 1: + call_sign = packet.text(90, 7); + break; + + default: + break; + } + + break; + + default: + break; + } } namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - std::string line = ais::format::mmsi(entry.mmsi) + " "; - if( !entry.name.empty() ) { - line += ais::format::text(entry.name); - } else { - line += ais::format::text(entry.call_sign); - } - - line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.location(), style, line); -} - -AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { - add_children({ - &button_done, - &button_see_map, - }); - - button_done.on_select = [this](const ui::Button&) { - if( this->on_close ) { - this->on_close(); - } - }; + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + std::string line = ais::format::mmsi(entry.mmsi) + " "; + if (!entry.name.empty()) { + line += ais::format::text(entry.name); + } else { + line += ais::format::text(entry.call_sign); + } - button_see_map.on_select = [this, &nav](Button&) { - geomap_view = nav.push( - ais::format::text(entry_.name), - 0, - GeoPos::alt_unit::METERS, - ais::format::latlon_float(entry_.last_position.latitude.normalized()), - ais::format::latlon_float(entry_.last_position.longitude.normalized()), - entry_.last_position.true_heading, - [this]() { - send_updates = false; - }); - send_updates = true; - - - }; + line.resize(target_rect.width() / 8, ' '); + painter.draw_string(target_rect.location(), style, line); } - -AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailView&Entry) : View() -{ +AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { + add_children({ + &button_done, + &button_see_map, + }); + + button_done.on_select = [this](const ui::Button&) { + if (this->on_close) { + this->on_close(); + } + }; + + button_see_map.on_select = [this, &nav](Button&) { + geomap_view = nav.push( + ais::format::text(entry_.name), + 0, + GeoPos::alt_unit::METERS, + ais::format::latlon_float(entry_.last_position.latitude.normalized()), + ais::format::latlon_float(entry_.last_position.longitude.normalized()), + entry_.last_position.true_heading, + [this]() { + send_updates = false; + }); + send_updates = true; + }; +} + +AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailView& Entry) + : View() { (void)Entry; } -AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry) -{ +AISRecentEntryDetailView& AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView& Entry) { (void)Entry; return *this; } void AISRecentEntryDetailView::update_position() { - if (send_updates) - geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0); + if (send_updates) + geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0); } void AISRecentEntryDetailView::focus() { - button_done.focus(); + button_done.focus(); } Rect AISRecentEntryDetailView::draw_field( - Painter& painter, - const Rect& draw_rect, - const Style& style, - const std::string& label, - const std::string& value -) { - const int label_length_max = 4; + Painter& painter, + const Rect& draw_rect, + const Style& style, + const std::string& label, + const std::string& value) { + const int label_length_max = 4; - painter.draw_string(Point { draw_rect.left(), draw_rect.top() }, style, label); - painter.draw_string(Point { draw_rect.left() + (label_length_max + 1) * 8, draw_rect.top() }, style, value); + painter.draw_string(Point{draw_rect.left(), draw_rect.top()}, style, label); + painter.draw_string(Point{draw_rect.left() + (label_length_max + 1) * 8, draw_rect.top()}, style, value); - return { draw_rect.left(), draw_rect.top() + draw_rect.height(), draw_rect.width(), draw_rect.height() }; + return {draw_rect.left(), draw_rect.top() + draw_rect.height(), draw_rect.width(), draw_rect.height()}; } void AISRecentEntryDetailView::paint(Painter& painter) { - View::paint(painter); + View::paint(painter); - const auto s = style(); - const auto rect = screen_rect(); + const auto s = style(); + const auto rect = screen_rect(); - auto field_rect = Rect { rect.left(), rect.top() + 16, rect.width(), 16 }; + auto field_rect = Rect{rect.left(), rect.top() + 16, rect.width(), 16}; - field_rect = draw_field(painter, field_rect, s, "MMSI", ais::format::mmsi(entry_.mmsi)); - field_rect = draw_field(painter, field_rect, s, "Ctry", ais::format::mid(entry_.mmsi)); - field_rect = draw_field(painter, field_rect, s, "Name", ais::format::text(entry_.name)); - field_rect = draw_field(painter, field_rect, s, "Call", ais::format::text(entry_.call_sign)); - field_rect = draw_field(painter, field_rect, s, "Dest", ais::format::text(entry_.destination)); - field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp)); - field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude)); - field_rect = draw_field(painter, field_rect, s, "Stat", ais::format::navigational_status(entry_.navigational_status)); - field_rect = draw_field(painter, field_rect, s, "RoT ", ais::format::rate_of_turn(entry_.last_position.rate_of_turn)); - field_rect = draw_field(painter, field_rect, s, "SoG ", ais::format::speed_over_ground(entry_.last_position.speed_over_ground)); - field_rect = draw_field(painter, field_rect, s, "CoG ", ais::format::course_over_ground(entry_.last_position.course_over_ground)); - field_rect = draw_field(painter, field_rect, s, "Head", ais::format::true_heading(entry_.last_position.true_heading)); - field_rect = draw_field(painter, field_rect, s, "Rx #", to_string_dec_uint(entry_.received_count)); + field_rect = draw_field(painter, field_rect, s, "MMSI", ais::format::mmsi(entry_.mmsi)); + field_rect = draw_field(painter, field_rect, s, "Ctry", ais::format::mid(entry_.mmsi)); + field_rect = draw_field(painter, field_rect, s, "Name", ais::format::text(entry_.name)); + field_rect = draw_field(painter, field_rect, s, "Call", ais::format::text(entry_.call_sign)); + field_rect = draw_field(painter, field_rect, s, "Dest", ais::format::text(entry_.destination)); + field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp)); + field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude)); + field_rect = draw_field(painter, field_rect, s, "Stat", ais::format::navigational_status(entry_.navigational_status)); + field_rect = draw_field(painter, field_rect, s, "RoT ", ais::format::rate_of_turn(entry_.last_position.rate_of_turn)); + field_rect = draw_field(painter, field_rect, s, "SoG ", ais::format::speed_over_ground(entry_.last_position.speed_over_ground)); + field_rect = draw_field(painter, field_rect, s, "CoG ", ais::format::course_over_ground(entry_.last_position.course_over_ground)); + field_rect = draw_field(painter, field_rect, s, "Head", ais::format::true_heading(entry_.last_position.true_heading)); + field_rect = draw_field(painter, field_rect, s, "Rx #", to_string_dec_uint(entry_.received_count)); } void AISRecentEntryDetailView::set_entry(const AISRecentEntry& entry) { - entry_ = entry; - set_dirty(); -} - -AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } { - baseband::run_image(portapack::spi_flash::image_tag_ais); - - add_children({ - &label_channel, - &options_channel, - &field_rf_amp, - &field_lna, - &field_vga, - &rssi, - &channel, - &recent_entries_view, - &recent_entry_detail_view, - }); - - - // load app settings - auto rc = settings.load("rx_ais", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - target_frequency_ = app_settings.rx_frequency; - } - else target_frequency_ = initial_target_frequency; - - recent_entry_detail_view.hidden(true); - - receiver_model.set_tuning_frequency(tuning_frequency()); - receiver_model.set_sampling_rate(sampling_rate); - receiver_model.set_baseband_bandwidth(baseband_bandwidth); - receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - - options_channel.on_change = [this](size_t, OptionsField::value_t v) { - this->on_frequency_changed(v); - }; - options_channel.set_by_value(target_frequency()); - - recent_entries_view.on_select = [this](const AISRecentEntry& entry) { - this->on_show_detail(entry); - }; - recent_entry_detail_view.on_close = [this]() { - this->on_show_list(); - }; - - logger = std::make_unique(); - if( logger ) { - logger->append( LOG_ROOT_DIR "/AIS.TXT" ); - } + entry_ = entry; + set_dirty(); +} + +AISAppView::AISAppView(NavigationView& nav) + : nav_{nav} { + baseband::run_image(portapack::spi_flash::image_tag_ais); + + add_children({ + &label_channel, + &options_channel, + &field_rf_amp, + &field_lna, + &field_vga, + &rssi, + &channel, + &recent_entries_view, + &recent_entry_detail_view, + }); + + // load app settings + auto rc = settings.load("rx_ais", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + target_frequency_ = app_settings.rx_frequency; + } else + target_frequency_ = initial_target_frequency; + + recent_entry_detail_view.hidden(true); + + receiver_model.set_tuning_frequency(tuning_frequency()); + receiver_model.set_sampling_rate(sampling_rate); + receiver_model.set_baseband_bandwidth(baseband_bandwidth); + receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. + + options_channel.on_change = [this](size_t, OptionsField::value_t v) { + this->on_frequency_changed(v); + }; + options_channel.set_by_value(target_frequency()); + + recent_entries_view.on_select = [this](const AISRecentEntry& entry) { + this->on_show_detail(entry); + }; + recent_entry_detail_view.on_close = [this]() { + this->on_show_list(); + }; + + logger = std::make_unique(); + if (logger) { + logger->append(LOG_ROOT_DIR "/AIS.TXT"); + } } AISAppView::~AISAppView() { + // save app settings + app_settings.rx_frequency = target_frequency_; + settings.save("rx_ais", &app_settings); - // save app settings - app_settings.rx_frequency = target_frequency_; - settings.save("rx_ais", &app_settings); - - receiver_model.disable(); // to switch off all, including DC bias. + receiver_model.disable(); // to switch off all, including DC bias. - baseband::shutdown(); + baseband::shutdown(); } void AISAppView::focus() { - options_channel.focus(); + options_channel.focus(); } void AISAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - const Rect content_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - recent_entries_view.set_parent_rect(content_rect); - recent_entry_detail_view.set_parent_rect(content_rect); + View::set_parent_rect(new_parent_rect); + const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + recent_entries_view.set_parent_rect(content_rect); + recent_entry_detail_view.set_parent_rect(content_rect); } void AISAppView::on_packet(const ais::Packet& packet) { - if( logger ) { - logger->on_packet(packet); - } + if (logger) { + logger->on_packet(packet); + } - auto& entry = ::on_packet(recent, packet.source_id()); - entry.update(packet); - recent_entries_view.set_dirty(); + auto& entry = ::on_packet(recent, packet.source_id()); + entry.update(packet); + recent_entries_view.set_dirty(); - // TODO: Crude hack, should be a more formal listener arrangement... - if( entry.key() == recent_entry_detail_view.entry().key() ) { - recent_entry_detail_view.set_entry(entry); - recent_entry_detail_view.update_position(); - } + // TODO: Crude hack, should be a more formal listener arrangement... + if (entry.key() == recent_entry_detail_view.entry().key()) { + recent_entry_detail_view.set_entry(entry); + recent_entry_detail_view.update_position(); + } } void AISAppView::on_show_list() { - recent_entries_view.hidden(false); - recent_entry_detail_view.hidden(true); - recent_entries_view.focus(); + recent_entries_view.hidden(false); + recent_entry_detail_view.hidden(true); + recent_entries_view.focus(); } void AISAppView::on_show_detail(const AISRecentEntry& entry) { - recent_entries_view.hidden(true); - recent_entry_detail_view.hidden(false); - recent_entry_detail_view.set_entry(entry); - recent_entry_detail_view.focus(); + recent_entries_view.hidden(true); + recent_entry_detail_view.hidden(false); + recent_entry_detail_view.set_entry(entry); + recent_entry_detail_view.focus(); } void AISAppView::on_frequency_changed(const uint32_t new_target_frequency) { - set_target_frequency(new_target_frequency); + set_target_frequency(new_target_frequency); } void AISAppView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - radio::set_tuning_frequency(tuning_frequency()); + target_frequency_ = new_value; + radio::set_tuning_frequency(tuning_frequency()); } uint32_t AISAppView::target_frequency() const { - return target_frequency_; + return target_frequency_; } uint32_t AISAppView::tuning_frequency() const { - return target_frequency() - (sampling_rate / 4); + return target_frequency() - (sampling_rate / 4); } } /* namespace ui */ diff --git a/firmware/application/apps/ais_app.hpp b/firmware/application/apps/ais_app.hpp index ec1a1e147..8b119f641 100644 --- a/firmware/application/apps/ais_app.hpp +++ b/firmware/application/apps/ais_app.hpp @@ -50,64 +50,62 @@ using namespace lpc43xx; #include "recent_entries.hpp" struct AISPosition { - rtc::RTC timestamp { }; - ais::Latitude latitude { }; - ais::Longitude longitude { }; - ais::RateOfTurn rate_of_turn { -128 }; - ais::SpeedOverGround speed_over_ground { 1023 }; - ais::CourseOverGround course_over_ground { 3600 }; - ais::TrueHeading true_heading { 511 }; + rtc::RTC timestamp{}; + ais::Latitude latitude{}; + ais::Longitude longitude{}; + ais::RateOfTurn rate_of_turn{-128}; + ais::SpeedOverGround speed_over_ground{1023}; + ais::CourseOverGround course_over_ground{3600}; + ais::TrueHeading true_heading{511}; }; struct AISRecentEntry { - using Key = ais::MMSI; - - static constexpr Key invalid_key = 0xffffffff; - - ais::MMSI mmsi; - std::string name; - std::string call_sign; - std::string destination; - AISPosition last_position; - size_t received_count; - int8_t navigational_status; - - AISRecentEntry( - ) : AISRecentEntry { 0 } - { - } - - AISRecentEntry( - const ais::MMSI& mmsi - ) : mmsi { mmsi }, - name { }, - call_sign { }, - destination { }, - last_position { }, - received_count { 0 }, - navigational_status { -1 } - { - } - - Key key() const { - return mmsi; - } - - void update(const ais::Packet& packet); + using Key = ais::MMSI; + + static constexpr Key invalid_key = 0xffffffff; + + ais::MMSI mmsi; + std::string name; + std::string call_sign; + std::string destination; + AISPosition last_position; + size_t received_count; + int8_t navigational_status; + + AISRecentEntry() + : AISRecentEntry{0} { + } + + AISRecentEntry( + const ais::MMSI& mmsi) + : mmsi{mmsi}, + name{}, + call_sign{}, + destination{}, + last_position{}, + received_count{0}, + navigational_status{-1} { + } + + Key key() const { + return mmsi; + } + + void update(const ais::Packet& packet); }; using AISRecentEntries = RecentEntries; class AISLogger { -public: - Optional append(const std::filesystem::path& filename) { - return log_file.append(filename); - } - - void on_packet(const ais::Packet& packet); - -private: - LogFile log_file { }; + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + + void on_packet(const ais::Packet& packet); + + private: + LogFile log_file{}; }; namespace ui { @@ -115,142 +113,132 @@ namespace ui { using AISRecentEntriesView = RecentEntriesView; class AISRecentEntryDetailView : public View { -public: - std::function on_close { }; - - AISRecentEntryDetailView(NavigationView& nav); - - void set_entry(const AISRecentEntry& new_entry); - const AISRecentEntry& entry() const { return entry_; }; - - void update_position(); - void focus() override; - void paint(Painter&) override; - - AISRecentEntryDetailView(const AISRecentEntryDetailView&Entry); - AISRecentEntryDetailView &operator=(const AISRecentEntryDetailView&Entry); - -private: - AISRecentEntry entry_ { }; - - Button button_done { - { 125, 224, 96, 24 }, - "Done" - }; - Button button_see_map { - { 19, 224, 96, 24 }, - "See on map" - }; - GeoMapView* geomap_view { nullptr }; - bool send_updates { false }; - - Rect draw_field( - Painter& painter, - const Rect& draw_rect, - const Style& style, - const std::string& label, - const std::string& value - ); + public: + std::function on_close{}; + + AISRecentEntryDetailView(NavigationView& nav); + + void set_entry(const AISRecentEntry& new_entry); + const AISRecentEntry& entry() const { return entry_; }; + + void update_position(); + void focus() override; + void paint(Painter&) override; + + AISRecentEntryDetailView(const AISRecentEntryDetailView& Entry); + AISRecentEntryDetailView& operator=(const AISRecentEntryDetailView& Entry); + + private: + AISRecentEntry entry_{}; + + Button button_done{ + {125, 224, 96, 24}, + "Done"}; + Button button_see_map{ + {19, 224, 96, 24}, + "See on map"}; + GeoMapView* geomap_view{nullptr}; + bool send_updates{false}; + + Rect draw_field( + Painter& painter, + const Rect& draw_rect, + const Style& style, + const std::string& label, + const std::string& value); }; class AISAppView : public View { -public: - AISAppView(NavigationView& nav); - ~AISAppView(); - - void set_parent_rect(const Rect new_parent_rect) override; + public: + AISAppView(NavigationView& nav); + ~AISAppView(); - // Prevent painting of region covered entirely by a child. - // TODO: Add flag to View that specifies view does not need to be cleared before painting. - void paint(Painter&) override { }; + void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; + // Prevent painting of region covered entirely by a child. + // TODO: Add flag to View that specifies view does not need to be cleared before painting. + void paint(Painter&) override{}; - std::string title() const override { return "AIS RX"; }; + void focus() override; -private: - static constexpr uint32_t initial_target_frequency = 162025000; - static constexpr uint32_t sampling_rate = 2457600; - static constexpr uint32_t baseband_bandwidth = 1750000; + std::string title() const override { return "AIS RX"; }; + private: + static constexpr uint32_t initial_target_frequency = 162025000; + static constexpr uint32_t sampling_rate = 2457600; + static constexpr uint32_t baseband_bandwidth = 1750000; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; - NavigationView& nav_; + NavigationView& nav_; - AISRecentEntries recent { }; - std::unique_ptr logger { }; + AISRecentEntries recent{}; + std::unique_ptr logger{}; - const RecentEntriesColumns columns { { - { "MMSI", 9 }, - { "Name/Call", 20 }, - } }; - AISRecentEntriesView recent_entries_view { columns, recent }; - AISRecentEntryDetailView recent_entry_detail_view { nav_ }; + const RecentEntriesColumns columns{{ + {"MMSI", 9}, + {"Name/Call", 20}, + }}; + AISRecentEntriesView recent_entries_view{columns, recent}; + AISRecentEntryDetailView recent_entry_detail_view{nav_}; - static constexpr auto header_height = 1 * 16; + static constexpr auto header_height = 1 * 16; - Text label_channel { - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - "Ch" - }; + Text label_channel{ + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + "Ch"}; - OptionsField options_channel { - { 3 * 8, 0 * 16 }, - 3, - { - { "87B", 161975000 }, - { "88B", 162025000 }, - } - }; + OptionsField options_channel{ + {3 * 8, 0 * 16}, + 3, + { + {"87B", 161975000}, + {"88B", 162025000}, + }}; - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; - MessageHandlerRegistration message_handler_packet { - Message::ID::AISPacket, - [this](Message* const p) { - const auto message = static_cast(p); - const ais::Packet packet { message->packet }; - if( packet.is_valid() ) { - this->on_packet(packet); - } - } - }; + MessageHandlerRegistration message_handler_packet{ + Message::ID::AISPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const ais::Packet packet{message->packet}; + if (packet.is_valid()) { + this->on_packet(packet); + } + }}; - uint32_t target_frequency_ = initial_target_frequency; + uint32_t target_frequency_ = initial_target_frequency; - void on_packet(const ais::Packet& packet); - void on_show_list(); - void on_show_detail(const AISRecentEntry& entry); + void on_packet(const ais::Packet& packet); + void on_show_list(); + void on_show_detail(const AISRecentEntry& entry); - void on_frequency_changed(const uint32_t new_target_frequency); + void on_frequency_changed(const uint32_t new_target_frequency); - uint32_t target_frequency() const; - void set_target_frequency(const uint32_t new_value); + uint32_t target_frequency() const; + void set_target_frequency(const uint32_t new_value); - uint32_t tuning_frequency() const; + uint32_t tuning_frequency() const; }; } /* namespace ui */ -#endif/*__AIS_APP_H__*/ +#endif /*__AIS_APP_H__*/ diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index d1a779810..ce5f01ead 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -43,433 +43,438 @@ namespace ui { /* AMOptionsView *********************************************************/ AMOptionsView::AMOptionsView( - const Rect parent_rect, const Style* const style -) : View { parent_rect } -{ - set_style(style); - - add_children({ - &label_config, - &options_config, - }); - - freqman_set_bandwidth_option( AM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config - options_config.set_selected_index(receiver_model.am_configuration()); - options_config.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_am_configuration(n); - }; + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + add_children({ + &label_config, + &options_config, + }); + + freqman_set_bandwidth_option(AM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config + options_config.set_selected_index(receiver_model.am_configuration()); + options_config.on_change = [this](size_t n, OptionsField::value_t) { + receiver_model.set_am_configuration(n); + }; } /* NBFMOptionsView *******************************************************/ NBFMOptionsView::NBFMOptionsView( - const Rect parent_rect, const Style* const style -) : View { parent_rect } -{ - set_style(style); - - add_children({ - &label_config, - &options_config, - &text_squelch, - &field_squelch - }); - - freqman_set_bandwidth_option( NFM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config - options_config.set_selected_index(receiver_model.nbfm_configuration()); - options_config.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_nbfm_configuration(n); - }; - - field_squelch.set_value(receiver_model.squelch_level()); - field_squelch.on_change = [this](int32_t v) { - receiver_model.set_squelch_level(v); - }; + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + add_children({&label_config, + &options_config, + &text_squelch, + &field_squelch}); + + freqman_set_bandwidth_option(NFM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config + options_config.set_selected_index(receiver_model.nbfm_configuration()); + options_config.on_change = [this](size_t n, OptionsField::value_t) { + receiver_model.set_nbfm_configuration(n); + }; + + field_squelch.set_value(receiver_model.squelch_level()); + field_squelch.on_change = [this](int32_t v) { + receiver_model.set_squelch_level(v); + }; } /* WFMOptionsView *******************************************************/ WFMOptionsView::WFMOptionsView( - const Rect parent_rect, const Style* const style -) : View { parent_rect } -{ - set_style(style); - - add_children({ - &label_config, - &options_config, - }); - - freqman_set_bandwidth_option( WFM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config - options_config.set_selected_index(receiver_model.wfm_configuration()); - options_config.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_wfm_configuration(n); - }; + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + add_children({ + &label_config, + &options_config, + }); + + freqman_set_bandwidth_option(WFM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config + options_config.set_selected_index(receiver_model.wfm_configuration()); + options_config.on_change = [this](size_t n, OptionsField::value_t) { + receiver_model.set_wfm_configuration(n); + }; } - /* SPECOptionsView *******************************************************/ SPECOptionsView::SPECOptionsView( - AnalogAudioView* view, const Rect parent_rect, const Style* const style -) : View { parent_rect } -{ - set_style(style); - - add_children({ - &label_config, - &options_config, - &text_speed, - &field_speed - }); - - options_config.set_selected_index(view->get_spec_bw_index()); - options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) { - view->set_spec_bw(n, bw); - }; - - field_speed.set_value(view->get_spec_trigger()); - field_speed.on_change = [this, view](int32_t v) { - view->set_spec_trigger(v); - }; + AnalogAudioView* view, + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + add_children({&label_config, + &options_config, + &text_speed, + &field_speed}); + + options_config.set_selected_index(view->get_spec_bw_index()); + options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) { + view->set_spec_bw(n, bw); + }; + + field_speed.set_value(view->get_spec_trigger()); + field_speed.on_change = [this, view](int32_t v) { + view->set_spec_trigger(v); + }; } /* AnalogAudioView *******************************************************/ AnalogAudioView::AnalogAudioView( - NavigationView& nav -) : nav_ (nav) -{ - add_children({ - &rssi, - &channel, - &audio, - &field_frequency, - &field_lna, - &field_vga, - &options_modulation, - &field_volume, - &text_ctcss, - &record_view, - &waterfall - }); - - // Set on_change before initialising the field - field_frequency.on_change = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - }; - - // load app settings - auto rc = settings.load("rx_audio", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - receiver_model.set_rf_amp(app_settings.rx_amp); - // field_frequency.set_value(app_settings.rx_frequency); - receiver_model.set_configuration_without_init(static_cast(app_settings.modulation), app_settings.step, app_settings.am_config_index, app_settings.nbfm_config_index, app_settings.wfm_config_index, app_settings.squelch); - } - field_frequency.set_value(receiver_model.tuning_frequency()); - - //Filename Datetime and Frequency - record_view.set_filename_date_frequency(true); - - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency.on_show_options = [this]() { - this->on_show_options_frequency(); - }; - - field_lna.on_show_options = [this]() { - this->on_show_options_rf_gain(); - }; - - field_vga.on_show_options = [this]() { - this->on_show_options_rf_gain(); - }; - - const auto modulation = receiver_model.modulation(); - options_modulation.set_by_value(toUType(modulation)); - - options_modulation.on_change = [this](size_t, OptionsField::value_t v) { - this->on_modulation_changed(static_cast(v)); - }; - options_modulation.on_show_options = [this]() { - this->on_show_options_modulation(); - }; - - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - record_view.on_error = [&nav](std::string message) { - nav.display_modal("Error", message); - }; - - waterfall.on_select = [this](int32_t offset) { - field_frequency.set_value(receiver_model.tuning_frequency() + offset); - }; - - audio::output::start(); - - update_modulation(static_cast(modulation)); - on_modulation_changed(static_cast(modulation)); + NavigationView& nav) + : nav_(nav) { + add_children({&rssi, + &channel, + &audio, + &field_frequency, + &field_lna, + &field_vga, + &options_modulation, + &field_volume, + &text_ctcss, + &record_view, + &waterfall}); + + // Set on_change before initialising the field + field_frequency.on_change = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + }; + + // load app settings + auto rc = settings.load("rx_audio", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + receiver_model.set_rf_amp(app_settings.rx_amp); + // field_frequency.set_value(app_settings.rx_frequency); + receiver_model.set_configuration_without_init(static_cast(app_settings.modulation), app_settings.step, app_settings.am_config_index, app_settings.nbfm_config_index, app_settings.wfm_config_index, app_settings.squelch); + } + field_frequency.set_value(receiver_model.tuning_frequency()); + + // Filename Datetime and Frequency + record_view.set_filename_date_frequency(true); + + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.on_show_options = [this]() { + this->on_show_options_frequency(); + }; + + field_lna.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + field_vga.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + const auto modulation = receiver_model.modulation(); + options_modulation.set_by_value(toUType(modulation)); + + options_modulation.on_change = [this](size_t, OptionsField::value_t v) { + this->on_modulation_changed(static_cast(v)); + }; + options_modulation.on_show_options = [this]() { + this->on_show_options_modulation(); + }; + + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + record_view.on_error = [&nav](std::string message) { + nav.display_modal("Error", message); + }; + + waterfall.on_select = [this](int32_t offset) { + field_frequency.set_value(receiver_model.tuning_frequency() + offset); + }; + + audio::output::start(); + + update_modulation(static_cast(modulation)); + on_modulation_changed(static_cast(modulation)); } size_t AnalogAudioView::get_spec_bw_index() { - return spec_bw_index; + return spec_bw_index; } void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) { - spec_bw_index = index; - spec_bw = bw; + spec_bw_index = index; + spec_bw = bw; - baseband::set_spectrum(bw, spec_trigger); - receiver_model.set_sampling_rate(bw); - receiver_model.set_baseband_bandwidth(bw/2); + baseband::set_spectrum(bw, spec_trigger); + receiver_model.set_sampling_rate(bw); + receiver_model.set_baseband_bandwidth(bw / 2); } uint16_t AnalogAudioView::get_spec_trigger() { - return spec_trigger; + return spec_trigger; } void AnalogAudioView::set_spec_trigger(uint16_t trigger) { - spec_trigger = trigger; + spec_trigger = trigger; - baseband::set_spectrum(spec_bw, spec_trigger); + baseband::set_spectrum(spec_bw, spec_trigger); } AnalogAudioView::~AnalogAudioView() { - - // save app settings - app_settings.rx_frequency = field_frequency.value(); - app_settings.lna = receiver_model.lna(); - app_settings.vga = receiver_model.vga(); - app_settings.rx_amp = receiver_model.rf_amp(); - app_settings.step = receiver_model.frequency_step(); - app_settings.modulation = (uint8_t)receiver_model.modulation(); - app_settings.am_config_index = receiver_model.am_configuration(); - app_settings.nbfm_config_index = receiver_model.nbfm_configuration(); - app_settings.wfm_config_index = receiver_model.wfm_configuration(); - app_settings.squelch = receiver_model.squelch_level(); - settings.save("rx_audio", &app_settings); - - // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do - // both? - audio::output::stop(); - - receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps if the last modulation was SPEC - receiver_model.disable(); - - baseband::shutdown(); + // save app settings + app_settings.rx_frequency = field_frequency.value(); + app_settings.lna = receiver_model.lna(); + app_settings.vga = receiver_model.vga(); + app_settings.rx_amp = receiver_model.rf_amp(); + app_settings.step = receiver_model.frequency_step(); + app_settings.modulation = (uint8_t)receiver_model.modulation(); + app_settings.am_config_index = receiver_model.am_configuration(); + app_settings.nbfm_config_index = receiver_model.nbfm_configuration(); + app_settings.wfm_config_index = receiver_model.wfm_configuration(); + app_settings.squelch = receiver_model.squelch_level(); + settings.save("rx_audio", &app_settings); + + // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do + // both? + audio::output::stop(); + + receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps if the last modulation was SPEC + receiver_model.disable(); + + baseband::shutdown(); } void AnalogAudioView::on_hide() { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - waterfall.on_hide(); - View::on_hide(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + waterfall.on_hide(); + View::on_hide(); } void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - waterfall.set_parent_rect(waterfall_rect); + View::set_parent_rect(new_parent_rect); + + const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + waterfall.set_parent_rect(waterfall_rect); } void AnalogAudioView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void AnalogAudioView::on_tuning_frequency_changed(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) { - receiver_model.set_baseband_bandwidth(bandwidth_hz); + receiver_model.set_baseband_bandwidth(bandwidth_hz); } void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - waterfall.on_hide(); - update_modulation(modulation); - on_show_options_modulation(); - waterfall.on_show(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + waterfall.on_hide(); + update_modulation(modulation); + on_show_options_modulation(); + waterfall.on_show(); } void AnalogAudioView::remove_options_widget() { - if( options_widget ) { - remove_child(options_widget.get()); - options_widget.reset(); - } - - field_lna.set_style(nullptr); - options_modulation.set_style(nullptr); - field_frequency.set_style(nullptr); + if (options_widget) { + remove_child(options_widget.get()); + options_widget.reset(); + } + + field_lna.set_style(nullptr); + options_modulation.set_style(nullptr); + field_frequency.set_style(nullptr); } void AnalogAudioView::set_options_widget(std::unique_ptr new_widget) { - remove_options_widget(); - - if( new_widget ) { - options_widget = std::move(new_widget); - } else { - // TODO: Lame hack to hide options view due to my bad paint/damage algorithm. - options_widget = std::make_unique(options_view_rect, style_options_group.background); - } - add_child(options_widget.get()); + remove_options_widget(); + + if (new_widget) { + options_widget = std::move(new_widget); + } else { + // TODO: Lame hack to hide options view due to my bad paint/damage algorithm. + options_widget = std::make_unique(options_view_rect, style_options_group.background); + } + add_child(options_widget.get()); } void AnalogAudioView::on_show_options_frequency() { - auto widget = std::make_unique(options_view_rect, &style_options_group); - - widget->set_step(receiver_model.frequency_step()); - widget->on_change_step = [this](rf::Frequency f) { - this->on_frequency_step_changed(f); - }; - widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000); - widget->on_change_reference_ppm_correction = [this](int32_t v) { - this->on_reference_ppm_correction_changed(v); - }; - - set_options_widget(std::move(widget)); - field_frequency.set_style(&style_options_group); + auto widget = std::make_unique(options_view_rect, &style_options_group); + + widget->set_step(receiver_model.frequency_step()); + widget->on_change_step = [this](rf::Frequency f) { + this->on_frequency_step_changed(f); + }; + widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000); + widget->on_change_reference_ppm_correction = [this](int32_t v) { + this->on_reference_ppm_correction_changed(v); + }; + + set_options_widget(std::move(widget)); + field_frequency.set_style(&style_options_group); } void AnalogAudioView::on_show_options_rf_gain() { - auto widget = std::make_unique(options_view_rect, &style_options_group); + auto widget = std::make_unique(options_view_rect, &style_options_group); - set_options_widget(std::move(widget)); - field_lna.set_style(&style_options_group); + set_options_widget(std::move(widget)); + field_lna.set_style(&style_options_group); } void AnalogAudioView::on_show_options_modulation() { - std::unique_ptr widget; - - const auto modulation = static_cast(receiver_model.modulation()); - switch(modulation) { - case ReceiverModel::Mode::AMAudio: - widget = std::make_unique(options_view_rect, &style_options_group); - waterfall.show_audio_spectrum_view(false); - text_ctcss.hidden(true); - break; - - case ReceiverModel::Mode::NarrowbandFMAudio: - widget = std::make_unique(nbfm_view_rect, &style_options_group); - waterfall.show_audio_spectrum_view(false); - text_ctcss.hidden(false); - break; - - case ReceiverModel::Mode::WidebandFMAudio: - widget = std::make_unique(options_view_rect, &style_options_group); - waterfall.show_audio_spectrum_view(true); - text_ctcss.hidden(true); - break; - - case ReceiverModel::Mode::SpectrumAnalysis: - widget = std::make_unique(this, nbfm_view_rect, &style_options_group); - waterfall.show_audio_spectrum_view(false); - text_ctcss.hidden(true); - break; - - default: - break; - } - - set_options_widget(std::move(widget)); - options_modulation.set_style(&style_options_group); + std::unique_ptr widget; + + const auto modulation = static_cast(receiver_model.modulation()); + switch (modulation) { + case ReceiverModel::Mode::AMAudio: + widget = std::make_unique(options_view_rect, &style_options_group); + waterfall.show_audio_spectrum_view(false); + text_ctcss.hidden(true); + break; + + case ReceiverModel::Mode::NarrowbandFMAudio: + widget = std::make_unique(nbfm_view_rect, &style_options_group); + waterfall.show_audio_spectrum_view(false); + text_ctcss.hidden(false); + break; + + case ReceiverModel::Mode::WidebandFMAudio: + widget = std::make_unique(options_view_rect, &style_options_group); + waterfall.show_audio_spectrum_view(true); + text_ctcss.hidden(true); + break; + + case ReceiverModel::Mode::SpectrumAnalysis: + widget = std::make_unique(this, nbfm_view_rect, &style_options_group); + waterfall.show_audio_spectrum_view(false); + text_ctcss.hidden(true); + break; + + default: + break; + } + + set_options_widget(std::move(widget)); + options_modulation.set_style(&style_options_group); } void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) { - receiver_model.set_frequency_step(f); - field_frequency.set_step(f); + receiver_model.set_frequency_step(f); + field_frequency.set_step(f); } void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) { - persistent_memory::set_correction_ppb(v * 1000); + persistent_memory::set_correction_ppb(v * 1000); } void AnalogAudioView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); } void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) { - audio::output::mute(); - record_view.stop(); - - baseband::shutdown(); - - portapack::spi_flash::image_tag_t image_tag; - switch(modulation) { - case ReceiverModel::Mode::AMAudio: image_tag = portapack::spi_flash::image_tag_am_audio; break; - case ReceiverModel::Mode::NarrowbandFMAudio: image_tag = portapack::spi_flash::image_tag_nfm_audio; break; - case ReceiverModel::Mode::WidebandFMAudio: image_tag = portapack::spi_flash::image_tag_wfm_audio; break; - case ReceiverModel::Mode::SpectrumAnalysis: image_tag = portapack::spi_flash::image_tag_wideband_spectrum; break; - default: - return; - } - - baseband::run_image(image_tag); - - if (modulation == ReceiverModel::Mode::SpectrumAnalysis) { - baseband::set_spectrum(spec_bw, spec_trigger); - } - - const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis); - receiver_model.set_modulation(modulation); - - receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000); - receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw/2 : 1750000); - - receiver_model.enable(); - - // TODO: This doesn't belong here! There's a better way. - size_t sampling_rate = 0; - switch(modulation) { - case ReceiverModel::Mode::AMAudio: sampling_rate = 12000; break; - case ReceiverModel::Mode::NarrowbandFMAudio: sampling_rate = 24000; break; - case ReceiverModel::Mode::WidebandFMAudio: sampling_rate = 48000; break; - default: - break; - } - record_view.set_sampling_rate(sampling_rate); - - if( !is_wideband_spectrum_mode ) { - audio::output::unmute(); - } + audio::output::mute(); + record_view.stop(); + + baseband::shutdown(); + + portapack::spi_flash::image_tag_t image_tag; + switch (modulation) { + case ReceiverModel::Mode::AMAudio: + image_tag = portapack::spi_flash::image_tag_am_audio; + break; + case ReceiverModel::Mode::NarrowbandFMAudio: + image_tag = portapack::spi_flash::image_tag_nfm_audio; + break; + case ReceiverModel::Mode::WidebandFMAudio: + image_tag = portapack::spi_flash::image_tag_wfm_audio; + break; + case ReceiverModel::Mode::SpectrumAnalysis: + image_tag = portapack::spi_flash::image_tag_wideband_spectrum; + break; + default: + return; + } + + baseband::run_image(image_tag); + + if (modulation == ReceiverModel::Mode::SpectrumAnalysis) { + baseband::set_spectrum(spec_bw, spec_trigger); + } + + const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis); + receiver_model.set_modulation(modulation); + + receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000); + receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw / 2 : 1750000); + + receiver_model.enable(); + + // TODO: This doesn't belong here! There's a better way. + size_t sampling_rate = 0; + switch (modulation) { + case ReceiverModel::Mode::AMAudio: + sampling_rate = 12000; + break; + case ReceiverModel::Mode::NarrowbandFMAudio: + sampling_rate = 24000; + break; + case ReceiverModel::Mode::WidebandFMAudio: + sampling_rate = 48000; + break; + default: + break; + } + record_view.set_sampling_rate(sampling_rate); + + if (!is_wideband_spectrum_mode) { + audio::output::unmute(); + } } - void AnalogAudioView::handle_coded_squelch(const uint32_t value) { - float diff, min_diff = value; - size_t min_idx { 0 }; - size_t c; - - // Find nearest match - for (c = 0; c < tone_keys.size(); c++) { - diff = abs(((float)value / 100.0) - tone_keys[c].second); - if (diff < min_diff) { - min_idx = c; - min_diff = diff; - } - } - - // Arbitrary confidence threshold - if (min_diff < 40) - text_ctcss.set("CTCSS " + tone_keys[min_idx].first); - else - text_ctcss.set("???"); + float diff, min_diff = value; + size_t min_idx{0}; + size_t c; + + // Find nearest match + for (c = 0; c < tone_keys.size(); c++) { + diff = abs(((float)value / 100.0) - tone_keys[c].second); + if (diff < min_diff) { + min_idx = c; + min_diff = diff; + } + } + + // Arbitrary confidence threshold + if (min_diff < 40) + text_ctcss.set("CTCSS " + tone_keys[min_idx].first); + else + text_ctcss.set("???"); } } /* namespace ui */ diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index b53b785f5..f71e98283 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -32,250 +32,240 @@ #include "app_settings.hpp" #include "tone_key.hpp" - namespace ui { -constexpr Style style_options_group { - .font = font::fixed_8x16, - .background = Color::blue(), - .foreground = Color::white(), +constexpr Style style_options_group{ + .font = font::fixed_8x16, + .background = Color::blue(), + .foreground = Color::white(), }; class AMOptionsView : public View { -public: - AMOptionsView(const Rect parent_rect, const Style* const style); - -private: - Text label_config { - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - "BW", - }; - - OptionsField options_config { - { 3 * 8, 0 * 16 }, - 5, - { // using common messages from freqman.cpp - } - }; + public: + AMOptionsView(const Rect parent_rect, const Style* const style); + + private: + Text label_config{ + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + "BW", + }; + + OptionsField options_config{ + {3 * 8, 0 * 16}, + 5, + { + // using common messages from freqman.cpp + }}; }; class NBFMOptionsView : public View { -public: - NBFMOptionsView(const Rect parent_rect, const Style* const style); - -private: - Text label_config { - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - "BW", - }; - OptionsField options_config { - { 3 * 8, 0 * 16 }, - 4, - { // using common messages from freqman.cpp - } - }; - - Text text_squelch { - { 9 * 8, 0 * 16, 8 * 8, 1 * 16 }, - "SQ /99" - }; - NumberField field_squelch { - { 12 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; + public: + NBFMOptionsView(const Rect parent_rect, const Style* const style); + + private: + Text label_config{ + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + "BW", + }; + OptionsField options_config{ + {3 * 8, 0 * 16}, + 4, + { + // using common messages from freqman.cpp + }}; + + Text text_squelch{ + {9 * 8, 0 * 16, 8 * 8, 1 * 16}, + "SQ /99"}; + NumberField field_squelch{ + {12 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; }; - class WFMOptionsView : public View { -public: - WFMOptionsView(const Rect parent_rect, const Style* const style); - -private: - Text label_config { - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - "BW", - }; - OptionsField options_config { - { 3 * 8, 0 * 16 }, - 4, - { // using common messages from freqman.cpp - } - }; + public: + WFMOptionsView(const Rect parent_rect, const Style* const style); + + private: + Text label_config{ + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + "BW", + }; + OptionsField options_config{ + {3 * 8, 0 * 16}, + 4, + { + // using common messages from freqman.cpp + }}; }; class AnalogAudioView; class SPECOptionsView : public View { -public: - SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style); - -private: - Text label_config { - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - "BW", - }; - OptionsField options_config { - { 3 * 8, 0 * 16 }, - 4, - { - { "20m ", 20000000 }, - { "10m ", 10000000 }, - { " 5m ", 5000000 }, - { " 2m ", 2000000 }, - { " 1m ", 1000000 }, - { "500k", 500000 }, - { "100k", 100000 }, - } - }; - - Text text_speed { - { 9 * 8, 0 * 16, 8 * 8, 1 * 16 }, - "SP /63" - }; - NumberField field_speed { - { 12 * 8, 0 * 16 }, - 2, - { 0, 63 }, - 1, - ' ', - }; + public: + SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style); + + private: + Text label_config{ + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + "BW", + }; + OptionsField options_config{ + {3 * 8, 0 * 16}, + 4, + { + {"20m ", 20000000}, + {"10m ", 10000000}, + {" 5m ", 5000000}, + {" 2m ", 2000000}, + {" 1m ", 1000000}, + {"500k", 500000}, + {"100k", 100000}, + }}; + + Text text_speed{ + {9 * 8, 0 * 16, 8 * 8, 1 * 16}, + "SP /63"}; + NumberField field_speed{ + {12 * 8, 0 * 16}, + 2, + {0, 63}, + 1, + ' ', + }; }; class AnalogAudioView : public View { -public: - AnalogAudioView(NavigationView& nav); - ~AnalogAudioView(); - - void on_hide() override; - - void set_parent_rect(const Rect new_parent_rect) override; - - void focus() override; - - std::string title() const override { return "Audio RX"; }; - - size_t get_spec_bw_index(); - void set_spec_bw(size_t index, uint32_t bw); - - uint16_t get_spec_trigger(); - void set_spec_trigger(uint16_t trigger); - -private: - static constexpr ui::Dim header_height = 3 * 16; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }; - const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 }; - - size_t spec_bw_index = 0; - uint32_t spec_bw = 20000000; - uint16_t spec_trigger = 63; - - NavigationView& nav_; - //bool exit_on_squelch { false }; - - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - Audio audio { - { 21 * 8, 10, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 5 * 8, 0 * 16 }, - }; - - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - - OptionsField options_modulation { - { 0 * 8, 0 * 16 }, - 4, - { - { " AM ", toUType(ReceiverModel::Mode::AMAudio) }, - { "NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio) }, - { "WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, - { "SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis) }, - } - }; - - NumberField field_volume { - { 28 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - Text text_ctcss { - { 19 * 8, 1 * 16, 11 * 8, 1 * 16 }, - "" - }; - - std::unique_ptr options_widget { }; - - RecordView record_view { - { 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, - u"AUD", u"AUDIO", - RecordView::FileType::WAV, - 4096, - 4 - }; - - spectrum::WaterfallWidget waterfall { true }; - - void on_tuning_frequency_changed(rf::Frequency f); - void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); - void on_modulation_changed(const ReceiverModel::Mode modulation); - void on_show_options_frequency(); - void on_show_options_rf_gain(); - void on_show_options_modulation(); - void on_frequency_step_changed(rf::Frequency f); - void on_reference_ppm_correction_changed(int32_t v); - void on_headphone_volume_changed(int32_t v); - void on_edit_frequency(); - - void remove_options_widget(); - void set_options_widget(std::unique_ptr new_widget); - - void update_modulation(const ReceiverModel::Mode modulation); - - //void squelched(); - void handle_coded_squelch(const uint32_t value); - - /*MessageHandlerRegistration message_handler_squelch_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - (void)p; - this->squelched(); - } - };*/ - - MessageHandlerRegistration message_handler_coded_squelch { - Message::ID::CodedSquelch, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_coded_squelch(message.value); - } - }; + public: + AnalogAudioView(NavigationView& nav); + ~AnalogAudioView(); + + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void focus() override; + + std::string title() const override { return "Audio RX"; }; + + size_t get_spec_bw_index(); + void set_spec_bw(size_t index, uint32_t bw); + + uint16_t get_spec_trigger(); + void set_spec_trigger(uint16_t trigger); + + private: + static constexpr ui::Dim header_height = 3 * 16; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16}; + const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; + + size_t spec_bw_index = 0; + uint32_t spec_bw = 20000000; + uint16_t spec_trigger = 63; + + NavigationView& nav_; + // bool exit_on_squelch { false }; + + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + Audio audio{ + {21 * 8, 10, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {5 * 8, 0 * 16}, + }; + + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + + OptionsField options_modulation{ + {0 * 8, 0 * 16}, + 4, + { + {" AM ", toUType(ReceiverModel::Mode::AMAudio)}, + {"NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio)}, + {"WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, + {"SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis)}, + }}; + + NumberField field_volume{ + {28 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + Text text_ctcss{ + {19 * 8, 1 * 16, 11 * 8, 1 * 16}, + ""}; + + std::unique_ptr options_widget{}; + + RecordView record_view{ + {0 * 8, 2 * 16, 30 * 8, 1 * 16}, + u"AUD", + u"AUDIO", + RecordView::FileType::WAV, + 4096, + 4}; + + spectrum::WaterfallWidget waterfall{true}; + + void on_tuning_frequency_changed(rf::Frequency f); + void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); + void on_modulation_changed(const ReceiverModel::Mode modulation); + void on_show_options_frequency(); + void on_show_options_rf_gain(); + void on_show_options_modulation(); + void on_frequency_step_changed(rf::Frequency f); + void on_reference_ppm_correction_changed(int32_t v); + void on_headphone_volume_changed(int32_t v); + void on_edit_frequency(); + + void remove_options_widget(); + void set_options_widget(std::unique_ptr new_widget); + + void update_modulation(const ReceiverModel::Mode modulation); + + // void squelched(); + void handle_coded_squelch(const uint32_t value); + + /*MessageHandlerRegistration message_handler_squelch_signal { + Message::ID::RequestSignal, + [this](const Message* const p) { + (void)p; + this->squelched(); + } + };*/ + + MessageHandlerRegistration message_handler_coded_squelch{ + Message::ID::CodedSquelch, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_coded_squelch(message.value); + }}; }; } /* namespace ui */ -#endif/*__ANALOG_AUDIO_APP_H__*/ +#endif /*__ANALOG_AUDIO_APP_H__*/ diff --git a/firmware/application/apps/analog_tv_app.cpp b/firmware/application/apps/analog_tv_app.cpp index c51021346..05df59dbf 100644 --- a/firmware/application/apps/analog_tv_app.cpp +++ b/firmware/application/apps/analog_tv_app.cpp @@ -42,213 +42,208 @@ namespace ui { /* AnalogTvView *******************************************************/ AnalogTvView::AnalogTvView( - NavigationView& nav -) : nav_ (nav) -{ - add_children({ - &rssi, - &channel, - &audio, - &field_frequency, - &field_lna, - &field_vga, - &options_modulation, - &field_volume, - &tv - }); - - // Set on_change before initialising the field - field_frequency.on_change = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - }; - - // load app settings - auto rc = settings.load("rx_tv", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - receiver_model.set_rf_amp(app_settings.rx_amp); - field_frequency.set_value(app_settings.rx_frequency); - } - else field_frequency.set_value(receiver_model.tuning_frequency()); - - - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency.on_show_options = [this]() { - this->on_show_options_frequency(); - }; - - field_lna.on_show_options = [this]() { - this->on_show_options_rf_gain(); - }; - - field_vga.on_show_options = [this]() { - this->on_show_options_rf_gain(); - }; - - const auto modulation = receiver_model.modulation(); - options_modulation.set_by_value(toUType(ReceiverModel::Mode::WidebandFMAudio)); - options_modulation.on_change = [this](size_t, OptionsField::value_t v) { - this->on_modulation_changed(static_cast(v)); - }; - options_modulation.on_show_options = [this]() { - this->on_show_options_modulation(); - }; - - field_volume.set_value(0); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - tv.on_select = [this](int32_t offset) { - field_frequency.set_value(receiver_model.tuning_frequency() + offset); - }; - - update_modulation(static_cast(modulation)); - on_modulation_changed(ReceiverModel::Mode::WidebandFMAudio); + NavigationView& nav) + : nav_(nav) { + add_children({&rssi, + &channel, + &audio, + &field_frequency, + &field_lna, + &field_vga, + &options_modulation, + &field_volume, + &tv}); + + // Set on_change before initialising the field + field_frequency.on_change = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + }; + + // load app settings + auto rc = settings.load("rx_tv", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + receiver_model.set_rf_amp(app_settings.rx_amp); + field_frequency.set_value(app_settings.rx_frequency); + } else + field_frequency.set_value(receiver_model.tuning_frequency()); + + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.on_show_options = [this]() { + this->on_show_options_frequency(); + }; + + field_lna.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + field_vga.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + const auto modulation = receiver_model.modulation(); + options_modulation.set_by_value(toUType(ReceiverModel::Mode::WidebandFMAudio)); + options_modulation.on_change = [this](size_t, OptionsField::value_t v) { + this->on_modulation_changed(static_cast(v)); + }; + options_modulation.on_show_options = [this]() { + this->on_show_options_modulation(); + }; + + field_volume.set_value(0); + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + tv.on_select = [this](int32_t offset) { + field_frequency.set_value(receiver_model.tuning_frequency() + offset); + }; + + update_modulation(static_cast(modulation)); + on_modulation_changed(ReceiverModel::Mode::WidebandFMAudio); } AnalogTvView::~AnalogTvView() { + // save app settings + app_settings.rx_frequency = field_frequency.value(); + settings.save("rx_tv", &app_settings); - // save app settings - app_settings.rx_frequency = field_frequency.value(); - settings.save("rx_tv", &app_settings); - - // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do - // both? - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do + // both? + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } void AnalogTvView::on_hide() { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - tv.on_hide(); - View::on_hide(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + tv.on_hide(); + View::on_hide(); } void AnalogTvView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - - const ui::Rect tv_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - tv.set_parent_rect(tv_rect); + View::set_parent_rect(new_parent_rect); + + const ui::Rect tv_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + tv.set_parent_rect(tv_rect); } void AnalogTvView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void AnalogTvView::on_tuning_frequency_changed(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) { - receiver_model.set_baseband_bandwidth(bandwidth_hz); + receiver_model.set_baseband_bandwidth(bandwidth_hz); } void AnalogTvView::on_modulation_changed(const ReceiverModel::Mode modulation) { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - tv.on_hide(); - update_modulation(modulation); - on_show_options_modulation(); - tv.on_show(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + tv.on_hide(); + update_modulation(modulation); + on_show_options_modulation(); + tv.on_show(); } void AnalogTvView::remove_options_widget() { - if( options_widget ) { - remove_child(options_widget.get()); - options_widget.reset(); - } - - field_lna.set_style(nullptr); - options_modulation.set_style(nullptr); - field_frequency.set_style(nullptr); + if (options_widget) { + remove_child(options_widget.get()); + options_widget.reset(); + } + + field_lna.set_style(nullptr); + options_modulation.set_style(nullptr); + field_frequency.set_style(nullptr); } void AnalogTvView::set_options_widget(std::unique_ptr new_widget) { - remove_options_widget(); + remove_options_widget(); - if( new_widget ) { - options_widget = std::move(new_widget); - } else { - // TODO: Lame hack to hide options view due to my bad paint/damage algorithm. - options_widget = std::make_unique(options_view_rect, style_options_group_new.background); - } - add_child(options_widget.get()); + if (new_widget) { + options_widget = std::move(new_widget); + } else { + // TODO: Lame hack to hide options view due to my bad paint/damage algorithm. + options_widget = std::make_unique(options_view_rect, style_options_group_new.background); + } + add_child(options_widget.get()); } void AnalogTvView::on_show_options_frequency() { - auto widget = std::make_unique(options_view_rect, &style_options_group_new); + auto widget = std::make_unique(options_view_rect, &style_options_group_new); - widget->set_step(receiver_model.frequency_step()); - widget->on_change_step = [this](rf::Frequency f) { - this->on_frequency_step_changed(f); - }; - widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000); - widget->on_change_reference_ppm_correction = [this](int32_t v) { - this->on_reference_ppm_correction_changed(v); - }; + widget->set_step(receiver_model.frequency_step()); + widget->on_change_step = [this](rf::Frequency f) { + this->on_frequency_step_changed(f); + }; + widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000); + widget->on_change_reference_ppm_correction = [this](int32_t v) { + this->on_reference_ppm_correction_changed(v); + }; - set_options_widget(std::move(widget)); - field_frequency.set_style(&style_options_group_new); + set_options_widget(std::move(widget)); + field_frequency.set_style(&style_options_group_new); } void AnalogTvView::on_show_options_rf_gain() { - auto widget = std::make_unique(options_view_rect, &style_options_group_new); + auto widget = std::make_unique(options_view_rect, &style_options_group_new); - set_options_widget(std::move(widget)); - field_lna.set_style(&style_options_group_new); + set_options_widget(std::move(widget)); + field_lna.set_style(&style_options_group_new); } void AnalogTvView::on_show_options_modulation() { - std::unique_ptr widget; + std::unique_ptr widget; + + static_cast(receiver_model.modulation()); + tv.show_audio_spectrum_view(true); - static_cast(receiver_model.modulation()); - tv.show_audio_spectrum_view(true); - - set_options_widget(std::move(widget)); - options_modulation.set_style(&style_options_group_new); + set_options_widget(std::move(widget)); + options_modulation.set_style(&style_options_group_new); } void AnalogTvView::on_frequency_step_changed(rf::Frequency f) { - receiver_model.set_frequency_step(f); - field_frequency.set_step(f); + receiver_model.set_frequency_step(f); + field_frequency.set_step(f); } void AnalogTvView::on_reference_ppm_correction_changed(int32_t v) { - persistent_memory::set_correction_ppb(v * 1000); + persistent_memory::set_correction_ppb(v * 1000); } void AnalogTvView::on_headphone_volume_changed(int32_t v) { - (void)v; //avoid warning - //tv::TVView::set_headphone_volume(this,v); + (void)v; // avoid warning + // tv::TVView::set_headphone_volume(this,v); } void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) { - audio::output::mute(); + audio::output::mute(); - baseband::shutdown(); + baseband::shutdown(); - portapack::spi_flash::image_tag_t image_tag; - image_tag = portapack::spi_flash::image_tag_am_tv; + portapack::spi_flash::image_tag_t image_tag; + image_tag = portapack::spi_flash::image_tag_am_tv; - baseband::run_image(image_tag); + baseband::run_image(image_tag); - receiver_model.set_modulation(modulation); - receiver_model.set_sampling_rate(2000000); - receiver_model.set_baseband_bandwidth(2000000); - receiver_model.enable(); + receiver_model.set_modulation(modulation); + receiver_model.set_sampling_rate(2000000); + receiver_model.set_baseband_bandwidth(2000000); + receiver_model.enable(); } } /* namespace ui */ diff --git a/firmware/application/apps/analog_tv_app.hpp b/firmware/application/apps/analog_tv_app.hpp index cea94b2da..acffab88c 100644 --- a/firmware/application/apps/analog_tv_app.hpp +++ b/firmware/application/apps/analog_tv_app.hpp @@ -36,102 +36,97 @@ namespace ui { -constexpr Style style_options_group_new { - .font = font::fixed_8x16, - .background = Color::blue(), - .foreground = Color::white(), +constexpr Style style_options_group_new{ + .font = font::fixed_8x16, + .background = Color::blue(), + .foreground = Color::white(), }; class AnalogTvView : public View { -public: - AnalogTvView(NavigationView& nav); - ~AnalogTvView(); - - void on_hide() override; - - void set_parent_rect(const Rect new_parent_rect) override; - - void focus() override; - - std::string title() const override { return "AnalogTV RX"; }; - -private: - static constexpr ui::Dim header_height = 3 * 16; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }; - const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 }; - - NavigationView& nav_; - - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - Audio audio { - { 21 * 8, 10, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 5 * 8, 0 * 16 }, - }; - - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - - OptionsField options_modulation { - { 0 * 8, 0 * 16 }, - 4, - { - { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, - { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, - { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, - } - }; - - NumberField field_volume { - { 27 * 8, 0 * 16 }, - 3, - { 0, 255 }, - 1, - ' ', - }; - - std::unique_ptr options_widget { }; - - tv::TVWidget tv { }; - - void on_tuning_frequency_changed(rf::Frequency f); - void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); - void on_modulation_changed(const ReceiverModel::Mode modulation); - void on_show_options_frequency(); - void on_show_options_rf_gain(); - void on_show_options_modulation(); - void on_frequency_step_changed(rf::Frequency f); - void on_reference_ppm_correction_changed(int32_t v); - void on_headphone_volume_changed(int32_t v); - void on_edit_frequency(); - - void remove_options_widget(); - void set_options_widget(std::unique_ptr new_widget); - - void update_modulation(const ReceiverModel::Mode modulation); - + public: + AnalogTvView(NavigationView& nav); + ~AnalogTvView(); + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void focus() override; + + std::string title() const override { return "AnalogTV RX"; }; + + private: + static constexpr ui::Dim header_height = 3 * 16; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16}; + const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; + + NavigationView& nav_; + + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + Audio audio{ + {21 * 8, 10, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {5 * 8, 0 * 16}, + }; + + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + + OptionsField options_modulation{ + {0 * 8, 0 * 16}, + 4, + { + {"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, + {"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, + {"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, + }}; + + NumberField field_volume{ + {27 * 8, 0 * 16}, + 3, + {0, 255}, + 1, + ' ', + }; + + std::unique_ptr options_widget{}; + + tv::TVWidget tv{}; + + void on_tuning_frequency_changed(rf::Frequency f); + void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); + void on_modulation_changed(const ReceiverModel::Mode modulation); + void on_show_options_frequency(); + void on_show_options_rf_gain(); + void on_show_options_modulation(); + void on_frequency_step_changed(rf::Frequency f); + void on_reference_ppm_correction_changed(int32_t v); + void on_headphone_volume_changed(int32_t v); + void on_edit_frequency(); + + void remove_options_widget(); + void set_options_widget(std::unique_ptr new_widget); + + void update_modulation(const ReceiverModel::Mode modulation); }; } /* namespace ui */ -#endif/*__ANALOG_TV_APP_H__*/ +#endif /*__ANALOG_TV_APP_H__*/ diff --git a/firmware/application/apps/capture_app.cpp b/firmware/application/apps/capture_app.cpp index 5666055b0..24ac2d54a 100644 --- a/firmware/application/apps/capture_app.cpp +++ b/firmware/application/apps/capture_app.cpp @@ -30,142 +30,140 @@ using namespace portapack; namespace ui { CaptureAppView::CaptureAppView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_capture); - - add_children({ - &labels, - &rssi, - &channel, - &field_frequency, - &field_frequency_step, - &field_rf_amp, - &field_lna, - &field_vga, - &option_bandwidth, - &record_view, - &waterfall, - }); - - // Hack for initialization - // TODO: This should be included in a more global section so apps dont need to do it - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - //------------------- - - field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_tuning_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency_step.set_by_value(receiver_model.frequency_step()); - field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { - receiver_model.set_frequency_step(v); - this->field_frequency.set_step(v); - }; - - option_bandwidth.on_change = [this](size_t, uint32_t base_rate) { - sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side - /* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */ - /* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/ - /* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */ - - waterfall.on_hide(); - record_view.set_sampling_rate(sampling_rate); - receiver_model.set_sampling_rate(sampling_rate); - /* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */ - - switch(sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem. - - case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz - anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options. - break; - - case 4000000 ... 6000000: // BW capture range (500k ... 750kHz max ) fs_max = 8 x 750kHz = 6Mhz - // BW 500k ... 750kHz , ex. 500kHz (fs = 8*BW = 4Mhz) , BW 600kHz (fs = 4,8Mhz) , BW 750 kHz (fs = 6Mhz) - anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar. - break; - - case 8800000: // BW capture 1,1Mhz fs = 8 x 1,1Mhz = 8,8Mhz . (1Mhz showed slightly higher noise background). - anti_alias_baseband_bandwidth_filter = 3500000; - break; - - case 14000000: // BW capture 1,75Mhz , fs = 8 x 1,75Mhz = 14Mhz - // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture - anti_alias_baseband_bandwidth_filter = 5000000; - break; - - case 16000000: // BW capture 2Mhz , fs = 8 x 2Mhz = 16Mhz - // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture - anti_alias_baseband_bandwidth_filter = 6000000; - break; - - case 20000000: // BW capture 2,5Mhz , fs= 8 x 2,5 Mhz = 20Mhz - // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture - anti_alias_baseband_bandwidth_filter = 7000000; - break; - - default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others. - // We tested also 9Mhz FPB stightly too much noise floor , better 8Mhz - anti_alias_baseband_bandwidth_filter = 8000000; - } - receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter); - - - waterfall.on_show(); - }; - - option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz - - receiver_model.set_modulation(ReceiverModel::Mode::Capture); - receiver_model.enable(); - - record_view.on_error = [&nav](std::string message) { - nav.display_modal("Error", message); - }; + baseband::run_image(portapack::spi_flash::image_tag_capture); + + add_children({ + &labels, + &rssi, + &channel, + &field_frequency, + &field_frequency_step, + &field_rf_amp, + &field_lna, + &field_vga, + &option_bandwidth, + &record_view, + &waterfall, + }); + + // Hack for initialization + // TODO: This should be included in a more global section so apps dont need to do it + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + //------------------- + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency_step.set_by_value(receiver_model.frequency_step()); + field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { + receiver_model.set_frequency_step(v); + this->field_frequency.set_step(v); + }; + + option_bandwidth.on_change = [this](size_t, uint32_t base_rate) { + sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side + /* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */ + /* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/ + /* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */ + + waterfall.on_hide(); + record_view.set_sampling_rate(sampling_rate); + receiver_model.set_sampling_rate(sampling_rate); + /* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */ + + switch (sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem. + + case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz + anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options. + break; + + case 4000000 ... 6000000: // BW capture range (500k ... 750kHz max ) fs_max = 8 x 750kHz = 6Mhz + // BW 500k ... 750kHz , ex. 500kHz (fs = 8*BW = 4Mhz) , BW 600kHz (fs = 4,8Mhz) , BW 750 kHz (fs = 6Mhz) + anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar. + break; + + case 8800000: // BW capture 1,1Mhz fs = 8 x 1,1Mhz = 8,8Mhz . (1Mhz showed slightly higher noise background). + anti_alias_baseband_bandwidth_filter = 3500000; + break; + + case 14000000: // BW capture 1,75Mhz , fs = 8 x 1,75Mhz = 14Mhz + // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture + anti_alias_baseband_bandwidth_filter = 5000000; + break; + + case 16000000: // BW capture 2Mhz , fs = 8 x 2Mhz = 16Mhz + // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture + anti_alias_baseband_bandwidth_filter = 6000000; + break; + + case 20000000: // BW capture 2,5Mhz , fs= 8 x 2,5 Mhz = 20Mhz + // good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture + anti_alias_baseband_bandwidth_filter = 7000000; + break; + + default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others. + // We tested also 9Mhz FPB stightly too much noise floor , better 8Mhz + anti_alias_baseband_bandwidth_filter = 8000000; + } + receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter); + + waterfall.on_show(); + }; + + option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz + + receiver_model.set_modulation(ReceiverModel::Mode::Capture); + receiver_model.enable(); + + record_view.on_error = [&nav](std::string message) { + nav.display_modal("Error", message); + }; } CaptureAppView::~CaptureAppView() { - - // Hack for preventing halting other apps - // TODO: This should be also part of something global - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - // ---------------------------- - - receiver_model.disable(); - baseband::shutdown(); + // Hack for preventing halting other apps + // TODO: This should be also part of something global + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + // ---------------------------- + + receiver_model.disable(); + baseband::shutdown(); } void CaptureAppView::on_hide() { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - waterfall.on_hide(); - View::on_hide(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + waterfall.on_hide(); + View::on_hide(); } void CaptureAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - waterfall.set_parent_rect(waterfall_rect); + const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + waterfall.set_parent_rect(waterfall_rect); } void CaptureAppView::focus() { - record_view.focus(); + record_view.focus(); } void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } } /* namespace ui */ diff --git a/firmware/application/apps/capture_app.hpp b/firmware/application/apps/capture_app.hpp index 9acefaafc..93acffe87 100644 --- a/firmware/application/apps/capture_app.hpp +++ b/firmware/application/apps/capture_app.hpp @@ -29,94 +29,91 @@ #include "ui_record_view.hpp" #include "ui_spectrum.hpp" -#define BW_OPTIONS \ - { " 8k5", 8500 }, \ - { " 11k", 11000 }, \ - { " 16k", 16000 }, \ - { " 25k", 25000 }, \ - { " 50k", 50000 }, \ - { " 100k", 100000 }, \ - { " 250k", 250000 }, \ - { " 500k", 500000 }, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \ - { " 600k", 600000 }, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \ - { " 750k", 750000 }, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \ - { "1100k", 1100000 }, \ - { "1750k", 1750000 }, \ - { "2000k", 2000000 }, \ - { "2500k", 2500000 }, \ - { "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC) +#define BW_OPTIONS \ + {" 8k5", 8500}, \ + {" 11k", 11000}, \ + {" 16k", 16000}, \ + {" 25k", 25000}, \ + {" 50k", 50000}, \ + {" 100k", 100000}, \ + {" 250k", 250000}, \ + {" 500k", 500000}, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \ + {" 600k", 600000}, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \ + {" 750k", 750000}, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \ + {"1100k", 1100000}, \ + {"1750k", 1750000}, \ + {"2000k", 2000000}, \ + {"2500k", 2500000}, \ + { "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC) namespace ui { class CaptureAppView : public View { -public: - CaptureAppView(NavigationView& nav); - ~CaptureAppView(); - - void on_hide() override; - - void set_parent_rect(const Rect new_parent_rect) override; - - void focus() override; - - std::string title() const override { return "Capture"; }; - -private: - static constexpr ui::Dim header_height = 3 * 16; - - uint32_t sampling_rate = 0; - uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var. - - void on_tuning_frequency_changed(rf::Frequency f); - - Labels labels { - { { 0 * 8, 1 * 16 }, "Rate:", Color::light_grey() }, - }; - - RSSI rssi { - { 24 * 8, 0, 6 * 8, 4 }, - }; - - Channel channel { - { 24 * 8, 5, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 16 }, - }; - - FrequencyStepView field_frequency_step { - { 10 * 8, 0 * 16 }, - }; - - RFAmpField field_rf_amp { - { 16 * 8, 0 * 16 } - }; - - LNAGainField field_lna { - { 18 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 21 * 8, 0 * 16 } - }; - - OptionsField option_bandwidth { - { 5 * 8, 1 * 16 }, - 5, - { - BW_OPTIONS - } - }; - - RecordView record_view { - { 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, - u"BBD_????.*", u"CAPTURES", RecordView::FileType::RawS16, 16384, 3 - }; - - spectrum::WaterfallWidget waterfall { }; + public: + CaptureAppView(NavigationView& nav); + ~CaptureAppView(); + + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void focus() override; + + std::string title() const override { return "Capture"; }; + + private: + static constexpr ui::Dim header_height = 3 * 16; + + uint32_t sampling_rate = 0; + uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var. + + void on_tuning_frequency_changed(rf::Frequency f); + + Labels labels{ + {{0 * 8, 1 * 16}, "Rate:", Color::light_grey()}, + }; + + RSSI rssi{ + {24 * 8, 0, 6 * 8, 4}, + }; + + Channel channel{ + {24 * 8, 5, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 16}, + }; + + FrequencyStepView field_frequency_step{ + {10 * 8, 0 * 16}, + }; + + RFAmpField field_rf_amp{ + {16 * 8, 0 * 16}}; + + LNAGainField field_lna{ + {18 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {21 * 8, 0 * 16}}; + + OptionsField option_bandwidth{ + {5 * 8, 1 * 16}, + 5, + {BW_OPTIONS}}; + + RecordView record_view{ + {0 * 8, 2 * 16, 30 * 8, 1 * 16}, + u"BBD_????.*", + u"CAPTURES", + RecordView::FileType::RawS16, + 16384, + 3}; + + spectrum::WaterfallWidget waterfall{}; }; } /* namespace ui */ -#endif/*__CAPTURE_APP_HPP__*/ +#endif /*__CAPTURE_APP_HPP__*/ diff --git a/firmware/application/apps/dump1090.hpp b/firmware/application/apps/dump1090.hpp index ef496518f..b3406b738 100644 --- a/firmware/application/apps/dump1090.hpp +++ b/firmware/application/apps/dump1090.hpp @@ -52,54 +52,53 @@ // Default version number, if not overriden by the Makefile #ifndef MODES_DUMP1090_VERSION -# define MODES_DUMP1090_VERSION "unknown" +#define MODES_DUMP1090_VERSION "unknown" #endif #ifndef MODES_DUMP1090_VARIANT -# define MODES_DUMP1090_VARIANT "dump1090-unknown" +#define MODES_DUMP1090_VARIANT "dump1090-unknown" #endif - -#define MODES_DEFAULT_FREQ 1090000000 -#define MODES_DEFAULT_WIDTH 1000 -#define MODES_DEFAULT_HEIGHT 700 -#define MODES_RTL_BUFFERS 15 // Number of RTL buffers -#define MODES_RTL_BUF_SIZE (16*16384) // 256k -#define MODES_MAG_BUF_SAMPLES (MODES_RTL_BUF_SIZE / 2) // Each sample is 2 bytes -#define MODES_MAG_BUFFERS 12 // Number of magnitude buffers (should be smaller than RTL_BUFFERS for flowcontrol to work) -#define MODES_AUTO_GAIN -100 // Use automatic gain -#define MODES_MAX_GAIN 999999 // Use max available gain -#define MODES_MSG_SQUELCH_DB 4.0 // Minimum SNR, in dB -#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors - -#define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit -#define MODEAC_MSG_BYTES 2 -#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit - -#define MODES_PREAMBLE_US 8 // microseconds = bits -#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2) -#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t)) -#define MODES_LONG_MSG_BYTES 14 -#define MODES_SHORT_MSG_BYTES 7 -#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8) -#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8) -#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2) -#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2) -#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t)) -#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t)) - -#define MODES_OS_PREAMBLE_SAMPLES (20) -#define MODES_OS_PREAMBLE_SIZE (MODES_OS_PREAMBLE_SAMPLES * sizeof(uint16_t)) -#define MODES_OS_LONG_MSG_SAMPLES (268) +#define MODES_DEFAULT_FREQ 1090000000 +#define MODES_DEFAULT_WIDTH 1000 +#define MODES_DEFAULT_HEIGHT 700 +#define MODES_RTL_BUFFERS 15 // Number of RTL buffers +#define MODES_RTL_BUF_SIZE (16 * 16384) // 256k +#define MODES_MAG_BUF_SAMPLES (MODES_RTL_BUF_SIZE / 2) // Each sample is 2 bytes +#define MODES_MAG_BUFFERS 12 // Number of magnitude buffers (should be smaller than RTL_BUFFERS for flowcontrol to work) +#define MODES_AUTO_GAIN -100 // Use automatic gain +#define MODES_MAX_GAIN 999999 // Use max available gain +#define MODES_MSG_SQUELCH_DB 4.0 // Minimum SNR, in dB +#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors + +#define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit +#define MODEAC_MSG_BYTES 2 +#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit + +#define MODES_PREAMBLE_US 8 // microseconds = bits +#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2) +#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t)) +#define MODES_LONG_MSG_BYTES 14 +#define MODES_SHORT_MSG_BYTES 7 +#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8) +#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8) +#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2) +#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2) +#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t)) +#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t)) + +#define MODES_OS_PREAMBLE_SAMPLES (20) +#define MODES_OS_PREAMBLE_SIZE (MODES_OS_PREAMBLE_SAMPLES * sizeof(uint16_t)) +#define MODES_OS_LONG_MSG_SAMPLES (268) #define MODES_OS_SHORT_MSG_SAMPLES (135) -#define MODES_OS_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t)) -#define MODES_OS_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t)) +#define MODES_OS_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t)) +#define MODES_OS_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t)) -#define MODES_OUT_BUF_SIZE (1500) -#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256) -#define MODES_OUT_FLUSH_INTERVAL (60000) +#define MODES_OUT_BUF_SIZE (1500) +#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256) +#define MODES_OUT_FLUSH_INTERVAL (60000) -#define MODES_USER_LATLON_VALID (1<<0) +#define MODES_USER_LATLON_VALID (1 << 0) #define INVALID_ALTITUDE (-9999) @@ -119,19 +118,19 @@ typedef enum { * (Earlier values are higher priority) */ typedef enum { - ADDR_ADSB_ICAO, /* Mode S or ADS-B, ICAO address, transponder sourced */ - ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */ - ADDR_ADSR_ICAO, /* ADS-R, ICAO address */ - ADDR_TISB_ICAO, /* TIS-B, ICAO address */ + ADDR_ADSB_ICAO, /* Mode S or ADS-B, ICAO address, transponder sourced */ + ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */ + ADDR_ADSR_ICAO, /* ADS-R, ICAO address */ + ADDR_TISB_ICAO, /* TIS-B, ICAO address */ - ADDR_ADSB_OTHER, /* ADS-B, other address format */ - ADDR_ADSR_OTHER, /* ADS-R, other address format */ - ADDR_TISB_TRACKFILE, /* TIS-B, Mode A code + track file number */ - ADDR_TISB_OTHER, /* TIS-B, other address format */ + ADDR_ADSB_OTHER, /* ADS-B, other address format */ + ADDR_ADSR_OTHER, /* ADS-R, other address format */ + ADDR_TISB_TRACKFILE, /* TIS-B, Mode A code + track file number */ + ADDR_TISB_OTHER, /* TIS-B, other address format */ - ADDR_MODE_A, /* Mode A */ + ADDR_MODE_A, /* Mode A */ - ADDR_UNKNOWN /* unknown address format */ + ADDR_UNKNOWN /* unknown address format */ } addrtype_t; typedef enum { @@ -158,20 +157,25 @@ typedef enum { } airground_t; typedef enum { - SIL_INVALID, SIL_UNKNOWN, SIL_PER_SAMPLE, SIL_PER_HOUR + SIL_INVALID, + SIL_UNKNOWN, + SIL_PER_SAMPLE, + SIL_PER_HOUR } sil_type_t; typedef enum { - CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE + CPR_SURFACE, + CPR_AIRBORNE, + CPR_COARSE } cpr_type_t; typedef enum { - HEADING_INVALID, // Not set - HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north - HEADING_TRUE, // Heading, degrees clockwise from true north - HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north - HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus - HEADING_TRACK_OR_HEADING // GROUND_TRACK / MAGNETIC / TRUE depending on the TAH bit in opstatus + HEADING_INVALID, // Not set + HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north + HEADING_TRUE, // Heading, degrees clockwise from true north + HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north + HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus + HEADING_TRACK_OR_HEADING // GROUND_TRACK / MAGNETIC / TRUE depending on the TAH bit in opstatus } heading_type_t; typedef enum { @@ -209,61 +213,68 @@ typedef enum { } emergency_t; typedef enum { - NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS + NAV_ALT_INVALID, + NAV_ALT_UNKNOWN, + NAV_ALT_AIRCRAFT, + NAV_ALT_MCP, + NAV_ALT_FMS } nav_altitude_source_t; -#define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses +#define MODES_NON_ICAO_ADDRESS (1 << 24) // Set on addresses to indicate they are not ICAO addresses -#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds -#define MODES_INTERACTIVE_DISPLAY_TTL 60000 // Delete from display after 60 seconds +#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds +#define MODES_INTERACTIVE_DISPLAY_TTL 60000 // Delete from display after 60 seconds -#define MODES_NET_HEARTBEAT_INTERVAL 60000 // milliseconds +#define MODES_NET_HEARTBEAT_INTERVAL 60000 // milliseconds -#define MODES_CLIENT_BUF_SIZE 1024 -#define MODES_NET_SNDBUF_SIZE (1024*64) -#define MODES_NET_SNDBUF_MAX (7) +#define MODES_CLIENT_BUF_SIZE 1024 +#define MODES_NET_SNDBUF_SIZE (1024 * 64) +#define MODES_NET_SNDBUF_MAX (7) #define HISTORY_SIZE 120 #define HISTORY_INTERVAL 30000 -#define MODES_NOTUSED(V) ((void) V) +#define MODES_NOTUSED(V) ((void)V) #define MAX_AMPLITUDE 65535.0 #define MAX_POWER (MAX_AMPLITUDE * MAX_AMPLITUDE) -#define FAUP_DEFAULT_RATE_MULTIPLIER 1.0 // FA Upload rate multiplier - +#define FAUP_DEFAULT_RATE_MULTIPLIER 1.0 // FA Upload rate multiplier //======================== structure declarations ========================= typedef enum { - SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR + SDR_NONE, + SDR_IFILE, + SDR_RTLSDR, + SDR_BLADERF, + SDR_HACKRF, + SDR_LIMESDR } sdr_type_t; - // The struct we use to store information about a decoded message. struct modesMessage { // Generic fields - unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message. - unsigned char verbatim[MODES_LONG_MSG_BYTES]; // Binary message, as originally received before correction - int msgbits; // Number of bits in message - int msgtype; // Downlink format # - uint32_t crc; // Message CRC - int correctedbits; // No. of bits corrected - uint32_t addr; // Address Announced - addrtype_t addrtype; // address format / source - uint64_t timestampMsg; // Timestamp of the message (12MHz clock) - uint64_t sysTimestampMsg; // Timestamp of the message (system time) - int remote; // If set this message is from a remote station - double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power - int score; // Scoring from scoreModesMessage, if used - int reliable; // is this a "reliable" message (uncorrected DF11/DF17/DF18)? - - datasource_t source; // Characterizes the overall message source + unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message. + unsigned char verbatim[MODES_LONG_MSG_BYTES]; // Binary message, as originally received before correction + int msgbits; // Number of bits in message + int msgtype; // Downlink format # + uint32_t crc; // Message CRC + int correctedbits; // No. of bits corrected + uint32_t addr; // Address Announced + addrtype_t addrtype; // address format / source + uint64_t timestampMsg; // Timestamp of the message (12MHz clock) + uint64_t sysTimestampMsg; // Timestamp of the message (system time) + int remote; // If set this message is from a remote station + double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power + int score; // Scoring from scoreModesMessage, if used + int reliable; // is this a "reliable" message (uncorrected DF11/DF17/DF18)? + + datasource_t source; // Characterizes the overall message source // Raw data, just extracted directly from the message // The names reflect the field names in Annex 4 - unsigned IID; // extracted from CRC of DF11s + unsigned IID; // extracted from CRC of DF11s unsigned AA; unsigned AC; unsigned CA; @@ -312,25 +323,25 @@ struct modesMessage { unsigned alert : 1; unsigned emergency_valid : 1; - unsigned metype; // DF17/18 ME type - unsigned mesub; // DF17/18 ME subtype + unsigned metype; // DF17/18 ME type + unsigned mesub; // DF17/18 ME subtype - commb_format_t commb_format; // Inferred format of a comm-b message + commb_format_t commb_format; // Inferred format of a comm-b message // valid if altitude_baro_valid: - int altitude_baro; // Altitude in either feet or meters - altitude_unit_t altitude_baro_unit; // the unit used for altitude + int altitude_baro; // Altitude in either feet or meters + altitude_unit_t altitude_baro_unit; // the unit used for altitude // valid if altitude_geom_valid: - int altitude_geom; // Altitude in either feet or meters - altitude_unit_t altitude_geom_unit; // the unit used for altitude + int altitude_geom; // Altitude in either feet or meters + altitude_unit_t altitude_geom_unit; // the unit used for altitude // following fields are valid if the corresponding _valid field is set: - int geom_delta; // Difference between geometric and baro alt - float heading; // ground track or heading, degrees (0-359). Reported directly or computed from from EW and NS velocity - heading_type_t heading_type;// how to interpret 'track_or_heading' - float track_rate; // Rate of change of track, degrees/second - float roll; // Roll, degrees, negative is left roll + int geom_delta; // Difference between geometric and baro alt + float heading; // ground track or heading, degrees (0-359). Reported directly or computed from from EW and NS velocity + heading_type_t heading_type; // how to interpret 'track_or_heading' + float track_rate; // Rate of change of track, degrees/second + float roll; // Roll, degrees, negative is left roll struct { // Groundspeed, kts, reported directly or computed from from EW and NS velocity // For surface movement, this has different interpretations for v0 and v2; both @@ -339,23 +350,23 @@ struct modesMessage { float v2; float selected; } gs; - unsigned ias; // Indicated airspeed, kts - unsigned tas; // True airspeed, kts - double mach; // Mach number - int baro_rate; // Rate of change of barometric altitude, feet/minute - int geom_rate; // Rate of change of geometric (GNSS / INS) altitude, feet/minute - unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits - char callsign[9]; // 8 chars flight number, NUL-terminated - unsigned category; // A0 - D7 encoded as a single hex byte - emergency_t emergency; // emergency/priority status + unsigned ias; // Indicated airspeed, kts + unsigned tas; // True airspeed, kts + double mach; // Mach number + int baro_rate; // Rate of change of barometric altitude, feet/minute + int geom_rate; // Rate of change of geometric (GNSS / INS) altitude, feet/minute + unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits + char callsign[9]; // 8 chars flight number, NUL-terminated + unsigned category; // A0 - D7 encoded as a single hex byte + emergency_t emergency; // emergency/priority status // valid if cpr_valid - cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) - unsigned cpr_lat; // Non decoded latitude. - unsigned cpr_lon; // Non decoded longitude. - unsigned cpr_nucp; // NUCp/NIC value implied by message type + cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B) + unsigned cpr_lat; // Non decoded latitude. + unsigned cpr_lon; // Non decoded longitude. + unsigned cpr_nucp; // NUCp/NIC value implied by message type - airground_t airground; // air/ground state + airground_t airground; // air/ground state // valid if cpr_decoded: double decoded_lat; @@ -374,20 +385,20 @@ struct modesMessage { unsigned gva_valid : 1; unsigned sda_valid : 1; - unsigned nic_a : 1; // if nic_a_valid - unsigned nic_b : 1; // if nic_b_valid - unsigned nic_c : 1; // if nic_c_valid - unsigned nic_baro : 1; // if nic_baro_valid + unsigned nic_a : 1; // if nic_a_valid + unsigned nic_b : 1; // if nic_b_valid + unsigned nic_c : 1; // if nic_c_valid + unsigned nic_baro : 1; // if nic_baro_valid - unsigned nac_p : 4; // if nac_p_valid - unsigned nac_v : 3; // if nac_v_valid + unsigned nac_p : 4; // if nac_p_valid + unsigned nac_v : 3; // if nac_v_valid - unsigned sil : 2; // if sil_type != SIL_INVALID + unsigned sil : 2; // if sil_type != SIL_INVALID sil_type_t sil_type; - unsigned gva : 2; // if gva_valid + unsigned gva : 2; // if gva_valid - unsigned sda : 2; // if sda_valid + unsigned sda : 2; // if sda_valid } accuracy; // Operational Status @@ -428,11 +439,11 @@ struct modesMessage { unsigned qnh_valid : 1; unsigned modes_valid : 1; - float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended) + float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended) heading_type_t heading_type; unsigned fms_altitude; // FMS selected altitude unsigned mcp_altitude; // MCP/FCU selected altitude - float qnh; // altimeter setting (QFE or QNH/QNE), millibars + float qnh; // altimeter setting (QFE or QNH/QNE), millibars nav_altitude_source_t altitude_source; @@ -440,5 +451,4 @@ struct modesMessage { } nav; }; - -#endif // __DUMP1090_H +#endif // __DUMP1090_H diff --git a/firmware/application/apps/ert_app.cpp b/firmware/application/apps/ert_app.cpp index b626ac5d1..b210fd02a 100644 --- a/firmware/application/apps/ert_app.cpp +++ b/firmware/application/apps/ert_app.cpp @@ -36,25 +36,29 @@ namespace ert { namespace format { std::string type(Packet::Type value) { - switch(value) { - default: - case Packet::Type::Unknown: return "???"; - case Packet::Type::IDM: return "IDM"; - case Packet::Type::SCM: return "SCM"; - case Packet::Type::SCMPLUS: return "SCM+"; - } + switch (value) { + default: + case Packet::Type::Unknown: + return "???"; + case Packet::Type::IDM: + return "IDM"; + case Packet::Type::SCM: + return "SCM"; + case Packet::Type::SCMPLUS: + return "SCM+"; + } } std::string id(ID value) { - return to_string_dec_uint(value, 10); + return to_string_dec_uint(value, 10); } std::string consumption(Consumption value) { - return to_string_dec_uint(value, 10); + return to_string_dec_uint(value, 10); } std::string commodity_type(CommodityType value) { - return to_string_dec_uint(value, 2); + return to_string_dec_uint(value, 2); } } /* namespace format */ @@ -62,114 +66,112 @@ std::string commodity_type(CommodityType value) { } /* namespace ert */ void ERTLogger::on_packet(const ert::Packet& packet) { - const auto formatted = packet.symbols_formatted(); - std::string entry = ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors; - log_file.write_entry(packet.received_at(), entry); + const auto formatted = packet.symbols_formatted(); + std::string entry = ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors; + log_file.write_entry(packet.received_at(), entry); } -const ERTRecentEntry::Key ERTRecentEntry::invalid_key { }; +const ERTRecentEntry::Key ERTRecentEntry::invalid_key{}; void ERTRecentEntry::update(const ert::Packet& packet) { - received_count++; + received_count++; - last_consumption = packet.consumption(); + last_consumption = packet.consumption(); } namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption); - - if( entry.received_count > 999 ) { - line += " +++"; - } else { - line += " " + to_string_dec_uint(entry.received_count, 3); - } - - line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.location(), style, line); + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption); + + if (entry.received_count > 999) { + line += " +++"; + } else { + line += " " + to_string_dec_uint(entry.received_count, 3); + } + + line.resize(target_rect.width() / 8, ' '); + painter.draw_string(target_rect.location(), style, line); } ERTAppView::ERTAppView(NavigationView&) { - baseband::run_image(portapack::spi_flash::image_tag_ert); - - add_children({ - &field_rf_amp, - &field_lna, - &field_vga, - &rssi, - &recent_entries_view, - }); - - // load app settings - auto rc = settings.load("rx_ert", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - - receiver_model.set_tuning_frequency(initial_target_frequency); - receiver_model.set_sampling_rate(sampling_rate); - receiver_model.set_baseband_bandwidth(baseband_bandwidth); - receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - -/* radio::enable({ - initial_target_frequency, - sampling_rate, - baseband_bandwidth, - rf::Direction::Receive, - receiver_model.rf_amp(), - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()), - }); */ - - logger = std::make_unique(); - if( logger ) { - logger->append( LOG_ROOT_DIR "/ERT.TXT" ); - } + baseband::run_image(portapack::spi_flash::image_tag_ert); + + add_children({ + &field_rf_amp, + &field_lna, + &field_vga, + &rssi, + &recent_entries_view, + }); + + // load app settings + auto rc = settings.load("rx_ert", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } + + receiver_model.set_tuning_frequency(initial_target_frequency); + receiver_model.set_sampling_rate(sampling_rate); + receiver_model.set_baseband_bandwidth(baseband_bandwidth); + receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. + + /* radio::enable({ + initial_target_frequency, + sampling_rate, + baseband_bandwidth, + rf::Direction::Receive, + receiver_model.rf_amp(), + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga()), + }); */ + + logger = std::make_unique(); + if (logger) { + logger->append(LOG_ROOT_DIR "/ERT.TXT"); + } } ERTAppView::~ERTAppView() { + // save app settings + settings.save("rx_ert", &app_settings); - // save app settings - settings.save("rx_ert", &app_settings); + receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_ - receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_ - - baseband::shutdown(); + baseband::shutdown(); } void ERTAppView::focus() { - field_vga.focus(); + field_vga.focus(); } void ERTAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - recent_entries_view.set_parent_rect({ 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }); + View::set_parent_rect(new_parent_rect); + recent_entries_view.set_parent_rect({0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}); } void ERTAppView::on_packet(const ert::Packet& packet) { - if( logger ) { - logger->on_packet(packet); - } - - if( packet.crc_ok() ) { - auto& entry = ::on_packet(recent, ERTRecentEntry::Key { packet.id(), packet.commodity_type() }); - entry.update(packet); - recent_entries_view.set_dirty(); - } + if (logger) { + logger->on_packet(packet); + } + + if (packet.crc_ok()) { + auto& entry = ::on_packet(recent, ERTRecentEntry::Key{packet.id(), packet.commodity_type()}); + entry.update(packet); + recent_entries_view.set_dirty(); + } } void ERTAppView::on_show_list() { - recent_entries_view.hidden(false); - recent_entries_view.focus(); + recent_entries_view.hidden(false); + recent_entries_view.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ert_app.hpp b/firmware/application/apps/ert_app.hpp index c121e11b3..7b9dae938 100644 --- a/firmware/application/apps/ert_app.hpp +++ b/firmware/application/apps/ert_app.hpp @@ -39,67 +39,65 @@ #include struct ERTKey { - ert::ID id; - ert::CommodityType commodity_type; - - constexpr ERTKey( - ert::ID id = ert::invalid_id, - ert::CommodityType commodity_type = ert::invalid_commodity_type - ) : id { id }, - commodity_type { commodity_type } - { - } - - ERTKey( const ERTKey& other ) = default; - - ERTKey& operator=(const ERTKey& other) { - id = other.id; - commodity_type = other.commodity_type; - return *this; - } - - bool operator==(const ERTKey& other) const { - return (id == other.id) && (commodity_type == other.commodity_type); - } + ert::ID id; + ert::CommodityType commodity_type; + + constexpr ERTKey( + ert::ID id = ert::invalid_id, + ert::CommodityType commodity_type = ert::invalid_commodity_type) + : id{id}, + commodity_type{commodity_type} { + } + + ERTKey(const ERTKey& other) = default; + + ERTKey& operator=(const ERTKey& other) { + id = other.id; + commodity_type = other.commodity_type; + return *this; + } + + bool operator==(const ERTKey& other) const { + return (id == other.id) && (commodity_type == other.commodity_type); + } }; struct ERTRecentEntry { - using Key = ERTKey; + using Key = ERTKey; - // TODO: Is this the right choice of invalid key value? - static const Key invalid_key; + // TODO: Is this the right choice of invalid key value? + static const Key invalid_key; - ert::ID id { ert::invalid_id }; - ert::CommodityType commodity_type { ert::invalid_commodity_type }; + ert::ID id{ert::invalid_id}; + ert::CommodityType commodity_type{ert::invalid_commodity_type}; - size_t received_count { 0 }; + size_t received_count{0}; - ert::Consumption last_consumption { }; + ert::Consumption last_consumption{}; - ERTRecentEntry( - const Key& key - ) : id { key.id }, - commodity_type { key.commodity_type } - { - } + ERTRecentEntry( + const Key& key) + : id{key.id}, + commodity_type{key.commodity_type} { + } - Key key() const { - return { id, commodity_type }; - } + Key key() const { + return {id, commodity_type}; + } - void update(const ert::Packet& packet); + void update(const ert::Packet& packet); }; class ERTLogger { -public: - Optional append(const std::filesystem::path& filename) { - return log_file.append(filename); - } - - void on_packet(const ert::Packet& packet); - -private: - LogFile log_file { }; + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + + void on_packet(const ert::Packet& packet); + + private: + LogFile log_file{}; }; using ERTRecentEntries = RecentEntries; @@ -109,72 +107,67 @@ namespace ui { using ERTRecentEntriesView = RecentEntriesView; class ERTAppView : public View { -public: - static constexpr uint32_t initial_target_frequency = 911600000; - static constexpr uint32_t sampling_rate = 4194304; - static constexpr uint32_t baseband_bandwidth = 2500000; - - ERTAppView(NavigationView& nav); - ~ERTAppView(); + public: + static constexpr uint32_t initial_target_frequency = 911600000; + static constexpr uint32_t sampling_rate = 4194304; + static constexpr uint32_t baseband_bandwidth = 2500000; - void set_parent_rect(const Rect new_parent_rect) override; + ERTAppView(NavigationView& nav); + ~ERTAppView(); - // Prevent painting of region covered entirely by a child. - // TODO: Add flag to View that specifies view does not need to be cleared before painting. - void paint(Painter&) override { }; + void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; + // Prevent painting of region covered entirely by a child. + // TODO: Add flag to View that specifies view does not need to be cleared before painting. + void paint(Painter&) override{}; - std::string title() const override { return "ERT RX"; }; + void focus() override; -private: - ERTRecentEntries recent { }; - std::unique_ptr logger { }; + std::string title() const override { return "ERT RX"; }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; + private: + ERTRecentEntries recent{}; + std::unique_ptr logger{}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; - const RecentEntriesColumns columns { { - { "ID", 10 }, - { "Tp", 2 }, - { "Consumpt", 10 }, - { "Cnt", 3 }, - } }; - ERTRecentEntriesView recent_entries_view { columns, recent }; + const RecentEntriesColumns columns{{ + {"ID", 10}, + {"Tp", 2}, + {"Consumpt", 10}, + {"Cnt", 3}, + }}; + ERTRecentEntriesView recent_entries_view{columns, recent}; - static constexpr auto header_height = 1 * 16; + static constexpr auto header_height = 1 * 16; - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; - MessageHandlerRegistration message_handler_packet { - Message::ID::ERTPacket, - [this](Message* const p) { - const auto message = static_cast(p); - const ert::Packet packet { message->type, message->packet }; - this->on_packet(packet); - } - }; + MessageHandlerRegistration message_handler_packet{ + Message::ID::ERTPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const ert::Packet packet{message->type, message->packet}; + this->on_packet(packet); + }}; - void on_packet(const ert::Packet& packet); - void on_show_list(); + void on_packet(const ert::Packet& packet); + void on_show_list(); }; } /* namespace ui */ -#endif/*__ERT_APP_H__*/ +#endif /*__ERT_APP_H__*/ diff --git a/firmware/application/apps/gps_sim_app.cpp b/firmware/application/apps/gps_sim_app.cpp index 51624684b..36f4040ed 100644 --- a/firmware/application/apps/gps_sim_app.cpp +++ b/firmware/application/apps/gps_sim_app.cpp @@ -37,228 +37,225 @@ using namespace portapack; namespace ui { void GpsSimAppView::set_ready() { - ready_signal = true; + ready_signal = true; } void GpsSimAppView::on_file_changed(std::filesystem::path new_file_path) { - File data_file, info_file; - char file_data[257]; - - // Get file size - auto data_open_error = data_file.open("/" + new_file_path.string()); - if (data_open_error.is_valid()) { - file_error(); - return; - } - - file_path = new_file_path; - - // Get original record frequency if available - std::filesystem::path info_file_path = file_path; - info_file_path.replace_extension(u".TXT"); - - sample_rate = 500000; - - auto info_open_error = info_file.open("/" + info_file_path.string()); - if (!info_open_error.is_valid()) { - memset(file_data, 0, 257); - auto read_size = info_file.read(file_data, 256); - if (!read_size.is_error()) { - auto pos1 = strstr(file_data, "center_frequency="); - if (pos1) { - pos1 += 17; - field_frequency.set_value(strtoll(pos1, nullptr, 10)); - } - - auto pos2 = strstr(file_data, "sample_rate="); - if (pos2) { - pos2 += 12; - sample_rate = strtoll(pos2, nullptr, 10); - } - } - } - - text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz"); - - auto file_size = data_file.size(); - auto duration = (file_size * 1000) / (1 * 2 * sample_rate); - - progressbar.set_max(file_size / 1024); - text_filename.set(file_path.filename().string().substr(0, 12)); - text_duration.set(to_string_time_ms(duration)); - - button_play.focus(); + File data_file, info_file; + char file_data[257]; + + // Get file size + auto data_open_error = data_file.open("/" + new_file_path.string()); + if (data_open_error.is_valid()) { + file_error(); + return; + } + + file_path = new_file_path; + + // Get original record frequency if available + std::filesystem::path info_file_path = file_path; + info_file_path.replace_extension(u".TXT"); + + sample_rate = 500000; + + auto info_open_error = info_file.open("/" + info_file_path.string()); + if (!info_open_error.is_valid()) { + memset(file_data, 0, 257); + auto read_size = info_file.read(file_data, 256); + if (!read_size.is_error()) { + auto pos1 = strstr(file_data, "center_frequency="); + if (pos1) { + pos1 += 17; + field_frequency.set_value(strtoll(pos1, nullptr, 10)); + } + + auto pos2 = strstr(file_data, "sample_rate="); + if (pos2) { + pos2 += 12; + sample_rate = strtoll(pos2, nullptr, 10); + } + } + } + + text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz"); + + auto file_size = data_file.size(); + auto duration = (file_size * 1000) / (1 * 2 * sample_rate); + + progressbar.set_max(file_size / 1024); + text_filename.set(file_path.filename().string().substr(0, 12)); + text_duration.set(to_string_time_ms(duration)); + + button_play.focus(); } void GpsSimAppView::on_tx_progress(const uint32_t progress) { - progressbar.set_value(progress); + progressbar.set_value(progress); } void GpsSimAppView::focus() { - button_open.focus(); + button_open.focus(); } void GpsSimAppView::file_error() { - nav_.display_modal("Error", "File read error."); + nav_.display_modal("Error", "File read error."); } bool GpsSimAppView::is_active() const { - return (bool)replay_thread; + return (bool)replay_thread; } void GpsSimAppView::toggle() { - if( is_active() ) { - stop(false); - } else { - start(); - } + if (is_active()) { + stop(false); + } else { + start(); + } } void GpsSimAppView::start() { - stop(false); - - std::unique_ptr reader; - - auto p = std::make_unique(); - auto open_error = p->open(file_path); - if( open_error.is_valid() ) { - file_error(); - } else { - reader = std::move(p); - } - - if( reader ) { - button_play.set_bitmap(&bitmap_stop); - baseband::set_sample_rate(sample_rate ); - - replay_thread = std::make_unique( - std::move(reader), - read_size, buffer_count, - &ready_signal, - [](uint32_t return_code) { - ReplayThreadDoneMessage message { return_code }; - EventDispatcher::send_message(message); - } - ); - } - - rf_amp =(transmitter_model.rf_amp() ); // recover rf_amp settings applied from ui_transmiter.cpp - - radio::enable({ - receiver_model.tuning_frequency(), - sample_rate , - baseband_bandwidth, - rf::Direction::Transmit, - rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()) - }); + stop(false); + + std::unique_ptr reader; + + auto p = std::make_unique(); + auto open_error = p->open(file_path); + if (open_error.is_valid()) { + file_error(); + } else { + reader = std::move(p); + } + + if (reader) { + button_play.set_bitmap(&bitmap_stop); + baseband::set_sample_rate(sample_rate); + + replay_thread = std::make_unique( + std::move(reader), + read_size, buffer_count, + &ready_signal, + [](uint32_t return_code) { + ReplayThreadDoneMessage message{return_code}; + EventDispatcher::send_message(message); + }); + } + + rf_amp = (transmitter_model.rf_amp()); // recover rf_amp settings applied from ui_transmiter.cpp + + radio::enable({receiver_model.tuning_frequency(), + sample_rate, + baseband_bandwidth, + rf::Direction::Transmit, + rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga())}); } void GpsSimAppView::stop(const bool do_loop) { - if( is_active() ) - replay_thread.reset(); - - if (do_loop && check_loop.value()) { - start(); - } else { - radio::disable(); - button_play.set_bitmap(&bitmap_play); - } - - ready_signal = false; + if (is_active()) + replay_thread.reset(); + + if (do_loop && check_loop.value()) { + start(); + } else { + radio::disable(); + button_play.set_bitmap(&bitmap_play); + } + + ready_signal = false; } void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) { - if (return_code == ReplayThread::END_OF_FILE) { - stop(true); - } else if (return_code == ReplayThread::READ_ERROR) { - stop(false); - file_error(); - } - - progressbar.set_value(0); + if (return_code == ReplayThread::END_OF_FILE) { + stop(true); + } else if (return_code == ReplayThread::READ_ERROR) { + stop(false); + file_error(); + } + + progressbar.set_value(0); } GpsSimAppView::GpsSimAppView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_gps); - - add_children({ - &button_open, - &text_filename, - &text_sample_rate, - &text_duration, - &progressbar, - &field_frequency, - &tx_view, // now it handles previous rfgain , rfamp. - &check_loop, - &button_play, - &waterfall, - }); - - field_frequency.set_value(target_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(this->target_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency.set_step(5000); - - button_play.on_select = [this](ImageButton&) { - this->toggle(); - }; - - button_open.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".C8"); - open_view->on_changed = [this](std::filesystem::path new_file_path) { - on_file_changed(new_file_path); - }; - }; + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_gps); + + add_children({ + &button_open, + &text_filename, + &text_sample_rate, + &text_duration, + &progressbar, + &field_frequency, + &tx_view, // now it handles previous rfgain , rfamp. + &check_loop, + &button_play, + &waterfall, + }); + + field_frequency.set_value(target_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(this->target_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.set_step(5000); + + button_play.on_select = [this](ImageButton&) { + this->toggle(); + }; + + button_open.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".C8"); + open_view->on_changed = [this](std::filesystem::path new_file_path) { + on_file_changed(new_file_path); + }; + }; } GpsSimAppView::~GpsSimAppView() { - radio::disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + radio::disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void GpsSimAppView::on_hide() { - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - if( is_active() ) - stop(false); - waterfall.on_hide(); - View::on_hide(); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + if (is_active()) + stop(false); + waterfall.on_hide(); + View::on_hide(); } void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - waterfall.set_parent_rect(waterfall_rect); + const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + waterfall.set_parent_rect(waterfall_rect); } void GpsSimAppView::on_target_frequency_changed(rf::Frequency f) { - set_target_frequency(f); + set_target_frequency(f); } void GpsSimAppView::set_target_frequency(const rf::Frequency new_value) { - persistent_memory::set_tuned_frequency(new_value);; + persistent_memory::set_tuned_frequency(new_value); + ; } rf::Frequency GpsSimAppView::target_frequency() const { - return persistent_memory::tuned_frequency(); + return persistent_memory::tuned_frequency(); } } /* namespace ui */ diff --git a/firmware/application/apps/gps_sim_app.hpp b/firmware/application/apps/gps_sim_app.hpp index e75d1d73a..3c4af9240 100644 --- a/firmware/application/apps/gps_sim_app.hpp +++ b/firmware/application/apps/gps_sim_app.hpp @@ -24,8 +24,8 @@ #ifndef __GPS_SIM_APP_HPP__ #define __GPS_SIM_APP_HPP__ -#define SHORT_UI true -#define NORMAL_UI false +#define SHORT_UI true +#define NORMAL_UI false #include "ui_widget.hpp" #include "ui_navigation.hpp" @@ -40,120 +40,111 @@ namespace ui { class GpsSimAppView : public View { -public: - GpsSimAppView(NavigationView& nav); - ~GpsSimAppView(); - - void on_hide() override; - void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; - - std::string title() const override { return "GPS Sim TX"; }; - -private: - NavigationView& nav_; - - static constexpr ui::Dim header_height = 3 * 16; - - uint32_t sample_rate = 0; - int32_t tx_gain { 47 }; - bool rf_amp { true }; // aux private var to store temporal, same as Replay App rf_amp user selection. - static constexpr uint32_t baseband_bandwidth = 3000000; //filter bandwidth - const size_t read_size { 16384 }; - const size_t buffer_count { 3 }; - - void on_file_changed(std::filesystem::path new_file_path); - void on_target_frequency_changed(rf::Frequency f); - void on_tx_progress(const uint32_t progress); - - void set_target_frequency(const rf::Frequency new_value); - rf::Frequency target_frequency() const; - - void toggle(); - void start(); - void stop(const bool do_loop); - bool is_active() const; - void set_ready(); - void handle_replay_thread_done(const uint32_t return_code); - void file_error(); - - std::filesystem::path file_path { }; - std::unique_ptr replay_thread { }; - bool ready_signal { false }; - - Button button_open { - { 0 * 8, 0 * 16, 10 * 8, 2 * 16 }, - "Open file" - }; - - Text text_filename { - { 11 * 8, 0 * 16, 12 * 8, 16 }, - "-" - }; - Text text_sample_rate { - { 24 * 8, 0 * 16, 6 * 8, 16 }, - "-" - }; - - Text text_duration { - { 11 * 8, 1 * 16, 6 * 8, 16 }, - "-" - }; - ProgressBar progressbar { - { 18 * 8, 1 * 16, 12 * 8, 16 } - }; - - FrequencyField field_frequency { - { 0 * 8, 2 * 16 }, - }; - - TransmitterView2 tx_view { // new handling of NumberField field_rfgain, NumberField field_rfamp - 74, 1*8, SHORT_UI // x(columns), y (line) position. (Used in Replay / GPS Simul / Playlist App) -// 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) - }; - - Checkbox check_loop { - { 21 * 8, 2 * 16 }, - 4, - "Loop", - true - }; - ImageButton button_play { - { 28 * 8, 2 * 16, 2 * 8, 1 * 16 }, - &bitmap_play, - Color::green(), - Color::black() - }; - - spectrum::WaterfallWidget waterfall { }; - - MessageHandlerRegistration message_handler_replay_thread_error { - Message::ID::ReplayThreadDone, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_replay_thread_done(message.return_code); - } - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->set_ready(); - } - } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress); - } - }; + public: + GpsSimAppView(NavigationView& nav); + ~GpsSimAppView(); + + void on_hide() override; + void set_parent_rect(const Rect new_parent_rect) override; + void focus() override; + + std::string title() const override { return "GPS Sim TX"; }; + + private: + NavigationView& nav_; + + static constexpr ui::Dim header_height = 3 * 16; + + uint32_t sample_rate = 0; + int32_t tx_gain{47}; + bool rf_amp{true}; // aux private var to store temporal, same as Replay App rf_amp user selection. + static constexpr uint32_t baseband_bandwidth = 3000000; // filter bandwidth + const size_t read_size{16384}; + const size_t buffer_count{3}; + + void on_file_changed(std::filesystem::path new_file_path); + void on_target_frequency_changed(rf::Frequency f); + void on_tx_progress(const uint32_t progress); + + void set_target_frequency(const rf::Frequency new_value); + rf::Frequency target_frequency() const; + + void toggle(); + void start(); + void stop(const bool do_loop); + bool is_active() const; + void set_ready(); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); + + std::filesystem::path file_path{}; + std::unique_ptr replay_thread{}; + bool ready_signal{false}; + + Button button_open{ + {0 * 8, 0 * 16, 10 * 8, 2 * 16}, + "Open file"}; + + Text text_filename{ + {11 * 8, 0 * 16, 12 * 8, 16}, + "-"}; + Text text_sample_rate{ + {24 * 8, 0 * 16, 6 * 8, 16}, + "-"}; + + Text text_duration{ + {11 * 8, 1 * 16, 6 * 8, 16}, + "-"}; + ProgressBar progressbar{ + {18 * 8, 1 * 16, 12 * 8, 16}}; + + FrequencyField field_frequency{ + {0 * 8, 2 * 16}, + }; + + TransmitterView2 tx_view{ + // new handling of NumberField field_rfgain, NumberField field_rfamp + 74, 1 * 8, SHORT_UI // x(columns), y (line) position. (Used in Replay / GPS Simul / Playlist App) + // 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) + }; + + Checkbox check_loop{ + {21 * 8, 2 * 16}, + 4, + "Loop", + true}; + ImageButton button_play{ + {28 * 8, 2 * 16, 2 * 8, 1 * 16}, + &bitmap_play, + Color::green(), + Color::black()}; + + spectrum::WaterfallWidget waterfall{}; + + MessageHandlerRegistration message_handler_replay_thread_error{ + Message::ID::ReplayThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_replay_thread_done(message.return_code); + }}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->set_ready(); + } + }}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress); + }}; }; } /* namespace ui */ -#endif/*__GPS_SIM_APP_HPP__*/ +#endif /*__GPS_SIM_APP_HPP__*/ diff --git a/firmware/application/apps/lge_app.cpp b/firmware/application/apps/lge_app.cpp index f5f81e65f..62fe0931f 100644 --- a/firmware/application/apps/lge_app.cpp +++ b/firmware/application/apps/lge_app.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2019 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -40,334 +40,334 @@ using namespace portapack; namespace ui { void LGEView::focus() { - options_frame.focus(); + options_frame.focus(); } LGEView::~LGEView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_lge", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_lge", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown();// better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void LGEView::generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector& data) { - std::array header = { - command, - (uint8_t)(address_a & 255), - (uint8_t)(address_a >> 8), - (uint8_t)(address_b & 255), - (uint8_t)(address_b >> 8), - }; - - data.insert(data.begin(), header.begin(), header.end()); - - frame_size = rfm69.gen_frame(data); - - for (auto b : data) - console.write(to_string_hex(b, 2) + " "); + std::array header = { + command, + (uint8_t)(address_a & 255), + (uint8_t)(address_a >> 8), + (uint8_t)(address_b & 255), + (uint8_t)(address_b >> 8), + }; + + data.insert(data.begin(), header.begin(), header.end()); + + frame_size = rfm69.gen_frame(data); + + for (auto b : data) + console.write(to_string_hex(b, 2) + " "); } void LGEView::generate_frame_touche() { - // 0001.89s - // 0D 96 02 12 0E 00 46 28 01 45 27 01 44 23 66 30 - std::vector data { 0x46, 0x28, 0x01, 0x45, 0x27, 0x01, 0x44, 0x23 }; - - console.write("\n\x1B\x07Touche:\x1B\x10"); - generate_lge_frame(0x96, (field_player.value() << 8) | field_room.value(), 0x0001, data); + // 0001.89s + // 0D 96 02 12 0E 00 46 28 01 45 27 01 44 23 66 30 + std::vector data{0x46, 0x28, 0x01, 0x45, 0x27, 0x01, 0x44, 0x23}; + + console.write("\n\x1B\x07Touche:\x1B\x10"); + generate_lge_frame(0x96, (field_player.value() << 8) | field_room.value(), 0x0001, data); } void LGEView::generate_frame_nickname() { - // 0040.48s: - // 30 02 1A 00 19 00 FF 00 02 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00 - // 04 01 B0 04 7F 1F 11 33 40 1F 22 01 07 00 00 01 07 00 00 63 05 00 00 99 A2 - - std::vector data { }; - std::array data_header = { 0xFF, 0x00, 0x02 }; - std::array data_footer = { - 0x01, 0xB0, 0x04, 0x7F, - 0x1F, 0x11, 0x33, 0x40, - 0x1F, 0x22, 0x01, 0x07, - 0x00, 0x00, 0x01, 0x07, - 0x00, 0x00, 0x63, 0x05, - 0x00, 0x00 - }; - uint32_t c; - - //data_header[2] = field_room.value(); // ? - //data_footer[0] = field_room.value(); // ? - - data.insert(data.begin(), data_header.begin(), data_header.end()); - - data.push_back(field_player.value()); - - c = 0; - for (auto &ch : nickname) { - data.push_back(ch); - c++; - } - // Space at the end, is this required ? - data.push_back(0x20); - // Pad with zeroes - while (++c < 16) - data.push_back(0x00); - - data.push_back(field_team.value()); - - data.insert(data.end(), data_footer.begin(), data_footer.end()); - - console.write("\n\x1B\x0ESet nickname:\x1B\x10"); - - generate_lge_frame(0x02, 0x001A, field_player.value(), data); + // 0040.48s: + // 30 02 1A 00 19 00 FF 00 02 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00 + // 04 01 B0 04 7F 1F 11 33 40 1F 22 01 07 00 00 01 07 00 00 63 05 00 00 99 A2 + + std::vector data{}; + std::array data_header = {0xFF, 0x00, 0x02}; + std::array data_footer = { + 0x01, 0xB0, 0x04, 0x7F, + 0x1F, 0x11, 0x33, 0x40, + 0x1F, 0x22, 0x01, 0x07, + 0x00, 0x00, 0x01, 0x07, + 0x00, 0x00, 0x63, 0x05, + 0x00, 0x00}; + uint32_t c; + + // data_header[2] = field_room.value(); // ? + // data_footer[0] = field_room.value(); // ? + + data.insert(data.begin(), data_header.begin(), data_header.end()); + + data.push_back(field_player.value()); + + c = 0; + for (auto& ch : nickname) { + data.push_back(ch); + c++; + } + // Space at the end, is this required ? + data.push_back(0x20); + // Pad with zeroes + while (++c < 16) + data.push_back(0x00); + + data.push_back(field_team.value()); + + data.insert(data.end(), data_footer.begin(), data_footer.end()); + + console.write("\n\x1B\x0ESet nickname:\x1B\x10"); + + generate_lge_frame(0x02, 0x001A, field_player.value(), data); } void LGEView::generate_frame_team() { - // 0041.83s: - // 3D 03 FF FF FF FF 02 03 01 52 4F 55 47 45 00 00 00 00 00 00 00 00 00 00 00 00 - // 02 56 45 52 54 45 00 00 00 00 00 00 00 00 00 00 00 01 03 42 4C 45 55 45 00 00 - // 00 00 00 00 00 00 00 00 00 02 43 29 - - std::vector data { }; - std::array data_header = { 0x02, 0x01 }; - uint32_t c; - - data.insert(data.begin(), data_header.begin(), data_header.end()); - - data.push_back(field_team.value()); - - c = 0; - for (auto &ch : nickname) { - data.push_back(ch); - c++; - } - // Pad with zeroes - while (c++ < 16) - data.push_back(0x00); - - data.push_back(field_team.value() - 1); // Color ? - - console.write("\n\x1B\x0ASet team:\x1B\x10"); - - generate_lge_frame(0x03, data); + // 0041.83s: + // 3D 03 FF FF FF FF 02 03 01 52 4F 55 47 45 00 00 00 00 00 00 00 00 00 00 00 00 + // 02 56 45 52 54 45 00 00 00 00 00 00 00 00 00 00 00 01 03 42 4C 45 55 45 00 00 + // 00 00 00 00 00 00 00 00 00 02 43 29 + + std::vector data{}; + std::array data_header = {0x02, 0x01}; + uint32_t c; + + data.insert(data.begin(), data_header.begin(), data_header.end()); + + data.push_back(field_team.value()); + + c = 0; + for (auto& ch : nickname) { + data.push_back(ch); + c++; + } + // Pad with zeroes + while (c++ < 16) + data.push_back(0x00); + + data.push_back(field_team.value() - 1); // Color ? + + console.write("\n\x1B\x0ASet team:\x1B\x10"); + + generate_lge_frame(0x03, data); } void LGEView::generate_frame_broadcast_nickname() { - // 0043.86s: - // 3D 04 FF FF FF FF 02 03 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00 04 - // 07 50 4F 4E 45 59 20 00 00 00 00 00 00 00 00 00 00 05 1B 41 42 42 59 20 00 00 - // 00 00 00 00 00 00 00 00 00 04 0A 02 - - std::vector data { }; - std::array data_header = { 0x02, 0x01 }; - uint32_t c; - - data.insert(data.begin(), data_header.begin(), data_header.end()); - - data.push_back(field_player.value()); - - c = 0; - for (auto &ch : nickname) { - data.push_back(ch); - c++; - } - // Space at the end, is this required ? - data.push_back(0x20); - // Pad with zeroes - while (++c < 16) - data.push_back(0x00); - - data.push_back(field_team.value()); - - console.write("\n\x1B\x09" "Broadcast nickname:\x1B\x10"); - - generate_lge_frame(0x04, data); + // 0043.86s: + // 3D 04 FF FF FF FF 02 03 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00 04 + // 07 50 4F 4E 45 59 20 00 00 00 00 00 00 00 00 00 00 05 1B 41 42 42 59 20 00 00 + // 00 00 00 00 00 00 00 00 00 04 0A 02 + + std::vector data{}; + std::array data_header = {0x02, 0x01}; + uint32_t c; + + data.insert(data.begin(), data_header.begin(), data_header.end()); + + data.push_back(field_player.value()); + + c = 0; + for (auto& ch : nickname) { + data.push_back(ch); + c++; + } + // Space at the end, is this required ? + data.push_back(0x20); + // Pad with zeroes + while (++c < 16) + data.push_back(0x00); + + data.push_back(field_team.value()); + + console.write( + "\n\x1B\x09" + "Broadcast nickname:\x1B\x10"); + + generate_lge_frame(0x04, data); } void LGEView::generate_frame_start() { - // 0166.13s: - // 0A 05 FF FF FF FF 02 EC FF FF FF A3 35 - std::vector data { 0x02, 0xEC, 0xFF, 0xFF, 0xFF }; - - //data[0] = field_room.value(); // ? - - console.write("\n\x1B\x0DStart:\x1B\x10"); - generate_lge_frame(0x05, data); + // 0166.13s: + // 0A 05 FF FF FF FF 02 EC FF FF FF A3 35 + std::vector data{0x02, 0xEC, 0xFF, 0xFF, 0xFF}; + + // data[0] = field_room.value(); // ? + + console.write("\n\x1B\x0DStart:\x1B\x10"); + generate_lge_frame(0x05, data); } void LGEView::generate_frame_gameover() { - std::vector data { (uint8_t)field_room.value() }; - - console.write("\n\x1B\x0CGameover:\x1B\x10"); - generate_lge_frame(0x0D, data); + std::vector data{(uint8_t)field_room.value()}; + + console.write("\n\x1B\x0CGameover:\x1B\x10"); + generate_lge_frame(0x0D, data); } void LGEView::generate_frame_collier() { - uint8_t flags = 0; - - // Custom - // 0C 00 13 37 13 37 id flags channel playerid zapduty zaptime checksum CRC CRC - // channel: field_channel - // playerid: field_player - // zapduty: field_power - // zaptime: field_duration - - if (checkbox_heartbeat.value()) - flags |= 1; - if (checkbox_rxtick.value()) - flags |= 2; - - uint8_t checksum = 0; - uint8_t id = (uint8_t)field_id.value(); - - std::vector data { - id, - flags, - (uint8_t)field_room.value(), - (uint8_t)field_player.value(), - (uint8_t)field_power.value(), - (uint8_t)(field_duration.value() * 10) - }; - - for (auto &v : data) - checksum += v; - - data.push_back(checksum - id); - - console.write("\n\x1B\x06" "Config:\x1B\x10"); - generate_lge_frame(0x00, 0x3713, 0x3713, data); + uint8_t flags = 0; + + // Custom + // 0C 00 13 37 13 37 id flags channel playerid zapduty zaptime checksum CRC CRC + // channel: field_channel + // playerid: field_player + // zapduty: field_power + // zaptime: field_duration + + if (checkbox_heartbeat.value()) + flags |= 1; + if (checkbox_rxtick.value()) + flags |= 2; + + uint8_t checksum = 0; + uint8_t id = (uint8_t)field_id.value(); + + std::vector data{ + id, + flags, + (uint8_t)field_room.value(), + (uint8_t)field_player.value(), + (uint8_t)field_power.value(), + (uint8_t)(field_duration.value() * 10)}; + + for (auto& v : data) + checksum += v; + + data.push_back(checksum - id); + + console.write( + "\n\x1B\x06" + "Config:\x1B\x10"); + generate_lge_frame(0x00, 0x3713, 0x3713, data); } void LGEView::start_tx() { - if (tx_mode == ALL) { - transmitter_model.set_tuning_frequency(channels[channel_index]); - tx_view.on_show(); // Refresh tuning frequency display - tx_view.set_dirty(); - } - transmitter_model.set_sampling_rate(2280000); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - chThdSleep(100); - - baseband::set_fsk_data(frame_size * 8, 2280000 / 9600, 4000, 256); + if (tx_mode == ALL) { + transmitter_model.set_tuning_frequency(channels[channel_index]); + tx_view.on_show(); // Refresh tuning frequency display + tx_view.set_dirty(); + } + transmitter_model.set_sampling_rate(2280000); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + chThdSleep(100); + + baseband::set_fsk_data(frame_size * 8, 2280000 / 9600, 4000, 256); } void LGEView::stop_tx() { - tx_mode = IDLE; - transmitter_model.disable(); - tx_view.set_transmitting(false); + tx_mode = IDLE; + transmitter_model.disable(); + tx_view.set_transmitting(false); } void LGEView::on_tx_progress(const uint32_t progress, const bool done) { - (void)progress; - - if (!done) return; - - transmitter_model.disable(); - - /*if (repeats < 2) { - chThdSleep(100); - repeats++; - start_tx(); - } else {*/ - if (tx_mode == ALL) { - if (channel_index < 2) { - channel_index++; - repeats = 0; - start_tx(); - } else { - stop_tx(); - } - } else { - stop_tx(); - } - //} + (void)progress; + + if (!done) return; + + transmitter_model.disable(); + + /*if (repeats < 2) { + chThdSleep(100); + repeats++; + start_tx(); + } else {*/ + if (tx_mode == ALL) { + if (channel_index < 2) { + channel_index++; + repeats = 0; + start_tx(); + } else { + stop_tx(); + } + } else { + stop_tx(); + } + //} } LGEView::LGEView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_fsktx); - - add_children({ - &labels, - &options_frame, - &field_room, - &button_text, - &field_team, - &field_player, - &field_id, - &field_power, - &field_duration, - &checkbox_heartbeat, - &checkbox_rxtick, - &checkbox_channels, - &console, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_lge", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - field_room.set_value(1); - field_team.set_value(1); - field_player.set_value(1); - field_id.set_value(1); - field_power.set_value(1); - field_duration.set_value(2); - - button_text.on_select = [this, &nav](Button&) { - text_prompt( - nav, - nickname, - 15, - [this](std::string& buffer) { - button_text.set_text(buffer); - }); - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (tx_mode == IDLE) { - auto i = options_frame.selected_index_value(); - if (i == 0) - generate_frame_touche(); - else if (i == 1) - generate_frame_nickname(); - else if (i == 2) - generate_frame_team(); - else if (i == 3) - generate_frame_broadcast_nickname(); - else if (i == 4) - generate_frame_start(); - else if (i == 5) - generate_frame_gameover(); - else if (i == 6) - generate_frame_collier(); - - repeats = 0; - channel_index = 0; - tx_mode = checkbox_channels.value() ? ALL : SINGLE; - tx_view.set_transmitting(true); - start_tx(); - } - }; - - tx_view.on_stop = [this]() { - stop_tx(); - }; + baseband::run_image(portapack::spi_flash::image_tag_fsktx); + + add_children({&labels, + &options_frame, + &field_room, + &button_text, + &field_team, + &field_player, + &field_id, + &field_power, + &field_duration, + &checkbox_heartbeat, + &checkbox_rxtick, + &checkbox_channels, + &console, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_lge", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + field_room.set_value(1); + field_team.set_value(1); + field_player.set_value(1); + field_id.set_value(1); + field_power.set_value(1); + field_duration.set_value(2); + + button_text.on_select = [this, &nav](Button&) { + text_prompt( + nav, + nickname, + 15, + [this](std::string& buffer) { + button_text.set_text(buffer); + }); + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (tx_mode == IDLE) { + auto i = options_frame.selected_index_value(); + if (i == 0) + generate_frame_touche(); + else if (i == 1) + generate_frame_nickname(); + else if (i == 2) + generate_frame_team(); + else if (i == 3) + generate_frame_broadcast_nickname(); + else if (i == 4) + generate_frame_start(); + else if (i == 5) + generate_frame_gameover(); + else if (i == 6) + generate_frame_collier(); + + repeats = 0; + channel_index = 0; + tx_mode = checkbox_channels.value() ? ALL : SINGLE; + tx_view.set_transmitting(true); + start_tx(); + } + }; + + tx_view.on_stop = [this]() { + stop_tx(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/lge_app.hpp b/firmware/application/apps/lge_app.hpp index 75377b29e..6e47a2a7a 100644 --- a/firmware/application/apps/lge_app.hpp +++ b/firmware/application/apps/lge_app.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2019 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -35,167 +35,150 @@ namespace ui { class LGEView : public View { -public: - LGEView(NavigationView& nav); - ~LGEView(); - - void focus() override; - - std::string title() const override { return "LGE tool TX"; }; - -private: - enum tx_modes { - IDLE = 0, - SINGLE, - ALL - }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - tx_modes tx_mode = IDLE; - - RFM69 rfm69 { 5, 0x2DD4, true, true }; - - uint32_t frame_size { 0 }; - uint32_t repeats { 0 }; - uint32_t channel_index { 0 }; - std::string nickname { "ABCDEF" }; - - rf::Frequency channels[3] = { 868067000, 868183000, 868295000 }; - - void start_tx(); - void stop_tx(); - - void generate_lge_frame(const uint8_t command, std::vector& data) { - generate_lge_frame(command, 0xFFFF, 0xFFFF, data); - } - void generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector& data); - void generate_frame_touche(); - void generate_frame_nickname(); - void generate_frame_team(); - void generate_frame_broadcast_nickname(); - void generate_frame_start(); - void generate_frame_gameover(); - void generate_frame_collier(); - - void on_tx_progress(const uint32_t progress, const bool done); - - Labels labels { - //{ { 7 * 8, 1 * 8 }, "NO FUN ALLOWED !", Color::red() }, - { { 1 * 8, 1 * 8 }, "Frame:", Color::light_grey() }, - { { 2 * 8, 3 * 8 }, "Room:", Color::light_grey() }, - { { 14 * 8, 3 * 8 }, "Text:", Color::light_grey() }, - { { 2 * 8, 5 * 8 }, "Team:", Color::light_grey() }, - { { 0 * 8, 7 * 8 }, "Player:", Color::light_grey() }, - { { 0 * 8, 10 * 8 }, "Vest:", Color::light_grey() }, - { { 4 * 8, 12 * 8 }, "ID:", Color::light_grey() }, - { { 3 * 8, 14 * 8 }, "Pow: /10", Color::light_grey() }, - { { 2 * 8, 16 * 8 }, "Time: x100ms", Color::light_grey() } - }; - - OptionsField options_frame { - { 7 * 8, 1 * 8 }, - 13, - { - { "Key", 0 }, - { "Set nickname", 1 }, - { "Set team", 2 }, - { "Brdcst nick", 3 }, - { "Start", 4 }, - { "Game over", 5 }, - { "Set vest", 6 } - } - }; - - Checkbox checkbox_channels { - { 20 * 8, 1 * 8 }, - 7, - "All ch.", - true - }; - - NumberField field_room { - { 7 * 8, 3 * 8 }, - 1, - { 1, 2 }, - 1, - '0' - }; - - Button button_text { - { 14 * 8, 5 * 8, 16 * 8, 3 * 8 }, - "ABCDEF" - }; - - NumberField field_team { - { 7 * 8, 5 * 8 }, - 1, - { 1, 6 }, - 1, - '0' - }; - - NumberField field_player { - { 7 * 8, 7 * 8 }, - 2, - { 1, 50 }, - 1, - '0' - }; - - Checkbox checkbox_heartbeat { - { 17 * 8, 12 * 8 }, - 9, - "Heartbeat", - true - }; - Checkbox checkbox_rxtick { - { 17 * 8, 15 * 8 }, - 7, - "RX tick", - true - }; - NumberField field_id { - { 7 * 8, 12 * 8 }, - 1, - { 1, 2 }, - 1, - '0' - }; - NumberField field_power { - { 7 * 8, 14 * 8 }, - 2, - { 1, 10 }, - 1, - '0' - }; - NumberField field_duration { - { 7 * 8, 16 * 8 }, - 2, - { 1, 25 }, - 1, - '0' - }; - - Console console { - { 0, 18 * 8, 30 * 8, 7 * 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + LGEView(NavigationView& nav); + ~LGEView(); + + void focus() override; + + std::string title() const override { return "LGE tool TX"; }; + + private: + enum tx_modes { + IDLE = 0, + SINGLE, + ALL + }; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + tx_modes tx_mode = IDLE; + + RFM69 rfm69{5, 0x2DD4, true, true}; + + uint32_t frame_size{0}; + uint32_t repeats{0}; + uint32_t channel_index{0}; + std::string nickname{"ABCDEF"}; + + rf::Frequency channels[3] = {868067000, 868183000, 868295000}; + + void start_tx(); + void stop_tx(); + + void generate_lge_frame(const uint8_t command, std::vector& data) { + generate_lge_frame(command, 0xFFFF, 0xFFFF, data); + } + void generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector& data); + void generate_frame_touche(); + void generate_frame_nickname(); + void generate_frame_team(); + void generate_frame_broadcast_nickname(); + void generate_frame_start(); + void generate_frame_gameover(); + void generate_frame_collier(); + + void on_tx_progress(const uint32_t progress, const bool done); + + Labels labels{ + //{ { 7 * 8, 1 * 8 }, "NO FUN ALLOWED !", Color::red() }, + {{1 * 8, 1 * 8}, "Frame:", Color::light_grey()}, + {{2 * 8, 3 * 8}, "Room:", Color::light_grey()}, + {{14 * 8, 3 * 8}, "Text:", Color::light_grey()}, + {{2 * 8, 5 * 8}, "Team:", Color::light_grey()}, + {{0 * 8, 7 * 8}, "Player:", Color::light_grey()}, + {{0 * 8, 10 * 8}, "Vest:", Color::light_grey()}, + {{4 * 8, 12 * 8}, "ID:", Color::light_grey()}, + {{3 * 8, 14 * 8}, "Pow: /10", Color::light_grey()}, + {{2 * 8, 16 * 8}, "Time: x100ms", Color::light_grey()}}; + + OptionsField options_frame{ + {7 * 8, 1 * 8}, + 13, + {{"Key", 0}, + {"Set nickname", 1}, + {"Set team", 2}, + {"Brdcst nick", 3}, + {"Start", 4}, + {"Game over", 5}, + {"Set vest", 6}}}; + + Checkbox checkbox_channels{ + {20 * 8, 1 * 8}, + 7, + "All ch.", + true}; + + NumberField field_room{ + {7 * 8, 3 * 8}, + 1, + {1, 2}, + 1, + '0'}; + + Button button_text{ + {14 * 8, 5 * 8, 16 * 8, 3 * 8}, + "ABCDEF"}; + + NumberField field_team{ + {7 * 8, 5 * 8}, + 1, + {1, 6}, + 1, + '0'}; + + NumberField field_player{ + {7 * 8, 7 * 8}, + 2, + {1, 50}, + 1, + '0'}; + + Checkbox checkbox_heartbeat{ + {17 * 8, 12 * 8}, + 9, + "Heartbeat", + true}; + Checkbox checkbox_rxtick{ + {17 * 8, 15 * 8}, + 7, + "RX tick", + true}; + NumberField field_id{ + {7 * 8, 12 * 8}, + 1, + {1, 2}, + 1, + '0'}; + NumberField field_power{ + {7 * 8, 14 * 8}, + 2, + {1, 10}, + 1, + '0'}; + NumberField field_duration{ + {7 * 8, 16 * 8}, + 2, + {1, 25}, + 1, + '0'}; + + Console console{ + {0, 18 * 8, 30 * 8, 7 * 16}}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/pocsag_app.cpp b/firmware/application/apps/pocsag_app.cpp index 15aeca158..67aff790e 100644 --- a/firmware/application/apps/pocsag_app.cpp +++ b/firmware/application/apps/pocsag_app.cpp @@ -33,216 +33,210 @@ using namespace pocsag; #include "audio.hpp" void POCSAGLogger::log_raw_data(const pocsag::POCSAGPacket& packet, const uint32_t frequency) { - std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz " + - to_string_dec_uint(packet.bitrate()) + " Codewords:"; - - // Raw hex dump of all the codewords - for (size_t c = 0; c < 16; c++) - entry += to_string_hex(packet[c], 8) + " "; - - log_file.write_entry(packet.timestamp(), entry); + std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz " + + to_string_dec_uint(packet.bitrate()) + " Codewords:"; + + // Raw hex dump of all the codewords + for (size_t c = 0; c < 16; c++) + entry += to_string_hex(packet[c], 8) + " "; + + log_file.write_entry(packet.timestamp(), entry); } void POCSAGLogger::log_decoded( - const pocsag::POCSAGPacket& packet, - const std::string text) { - - log_file.write_entry(packet.timestamp(), text); + const pocsag::POCSAGPacket& packet, + const std::string text) { + log_file.write_entry(packet.timestamp(), text); } namespace ui { void POCSAGAppView::update_freq(rf::Frequency f) { - set_target_frequency(f); - portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ? + set_target_frequency(f); + portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ? } POCSAGAppView::POCSAGAppView(NavigationView& nav) { - uint32_t ignore_address; - - baseband::run_image(portapack::spi_flash::image_tag_pocsag); - - add_children({ - &rssi, - &channel, - &audio, - &field_rf_amp, - &field_lna, - &field_vga, - &field_frequency, - &check_log, - &field_volume, - &check_ignore, - &sym_ignore, - &console - }); - - // Set on_change before initialising the field - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - - // load app settings - auto rc = settings.load("rx_pocsag", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - field_frequency.set_value(app_settings.rx_frequency); - } - else field_frequency.set_value(receiver_model.tuning_frequency()); - - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.enable(); - - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - check_log.set_value(logging); - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - }; - - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - check_ignore.set_value(ignore); - check_ignore.on_select = [this](Checkbox&, bool v) { - ignore = v; - }; - - ignore_address = persistent_memory::pocsag_ignore_address(); - for (size_t c = 0; c < 7; c++) { - sym_ignore.set_sym(6 - c, ignore_address % 10); - ignore_address /= 10; - } - - logger = std::make_unique(); - if (logger) - logger->append(LOG_ROOT_DIR "/POCSAG.TXT"); - - audio::output::start(); - audio::output::unmute(); - - baseband::set_pocsag(); + uint32_t ignore_address; + + baseband::run_image(portapack::spi_flash::image_tag_pocsag); + + add_children({&rssi, + &channel, + &audio, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &check_log, + &field_volume, + &check_ignore, + &sym_ignore, + &console}); + + // Set on_change before initialising the field + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + + // load app settings + auto rc = settings.load("rx_pocsag", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + field_frequency.set_value(app_settings.rx_frequency); + } else + field_frequency.set_value(receiver_model.tuning_frequency()); + + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.enable(); + + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + check_ignore.set_value(ignore); + check_ignore.on_select = [this](Checkbox&, bool v) { + ignore = v; + }; + + ignore_address = persistent_memory::pocsag_ignore_address(); + for (size_t c = 0; c < 7; c++) { + sym_ignore.set_sym(6 - c, ignore_address % 10); + ignore_address /= 10; + } + + logger = std::make_unique(); + if (logger) + logger->append(LOG_ROOT_DIR "/POCSAG.TXT"); + + audio::output::start(); + audio::output::unmute(); + + baseband::set_pocsag(); } -POCSAGAppView::~POCSAGAppView() { +POCSAGAppView::~POCSAGAppView() { + // save app settings + app_settings.rx_frequency = field_frequency.value(); + settings.save("rx_pocsag", &app_settings); - // save app settings - app_settings.rx_frequency = field_frequency.value(); - settings.save("rx_pocsag", &app_settings); + audio::output::stop(); - audio::output::stop(); + // Save ignored address + persistent_memory::set_pocsag_ignore_address(sym_ignore.value_dec_u32()); - // Save ignored address - persistent_memory::set_pocsag_ignore_address(sym_ignore.value_dec_u32()); - - receiver_model.disable(); - baseband::shutdown(); + receiver_model.disable(); + baseband::shutdown(); } void POCSAGAppView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void POCSAGAppView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); } - // Useless ? void POCSAGAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); } -void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { - std::string alphanum_text = ""; - - if (message->packet.flag() != NORMAL) - console.writeln("\n\x1B\x0CRC ERROR: " + pocsag::flag_str(message->packet.flag())); - else { - pocsag_decode_batch(message->packet, &pocsag_state); - - if ((ignore) && (pocsag_state.address == sym_ignore.value_dec_u32())) { - // Ignore (inform, but no log) - //console.write("\n\x1B\x03" + to_string_time(message->packet.timestamp()) + - // " Ignored address " + to_string_dec_uint(pocsag_state.address)); - return; - } - // Too many errors for reliable decode - if ((ignore) && (pocsag_state.errors >= 3)) { - return; - } - - - std::string console_info; - const uint32_t roundVal = 50; - const uint32_t bitrate = roundVal * ((message->packet.bitrate() + (roundVal/2))/roundVal); - console_info = "\n" + to_string_datetime(message->packet.timestamp(), HM); - console_info += " " + to_string_dec_uint(bitrate); - console_info += " ADDR:" + to_string_dec_uint(pocsag_state.address); - console_info += " F" + to_string_dec_uint(pocsag_state.function); - - // Store last received address for POCSAG TX - persistent_memory::set_pocsag_last_address(pocsag_state.address); - - if (pocsag_state.out_type == ADDRESS) { - // Address only - - console.write(console_info); - - if (logger && logging) { - logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + - " F" + to_string_dec_uint(pocsag_state.function) + - " Address only"); - } - - last_address = pocsag_state.address; - } else if (pocsag_state.out_type == MESSAGE) { - if (pocsag_state.address != last_address) { - // New message - console.writeln(console_info); - console.write(pocsag_state.output); - - last_address = pocsag_state.address; - } else { - // Message continues... - console.write(pocsag_state.output); - } - - if (logger && logging) - logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + - " F" + to_string_dec_uint(pocsag_state.function) + - " Alpha: " + pocsag_state.output); - } - } - - // Log raw data whatever it contains - if (logger && logging) - logger->log_raw_data(message->packet, target_frequency()); +void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) { + std::string alphanum_text = ""; + + if (message->packet.flag() != NORMAL) + console.writeln("\n\x1B\x0CRC ERROR: " + pocsag::flag_str(message->packet.flag())); + else { + pocsag_decode_batch(message->packet, &pocsag_state); + + if ((ignore) && (pocsag_state.address == sym_ignore.value_dec_u32())) { + // Ignore (inform, but no log) + // console.write("\n\x1B\x03" + to_string_time(message->packet.timestamp()) + + // " Ignored address " + to_string_dec_uint(pocsag_state.address)); + return; + } + // Too many errors for reliable decode + if ((ignore) && (pocsag_state.errors >= 3)) { + return; + } + + std::string console_info; + const uint32_t roundVal = 50; + const uint32_t bitrate = roundVal * ((message->packet.bitrate() + (roundVal / 2)) / roundVal); + console_info = "\n" + to_string_datetime(message->packet.timestamp(), HM); + console_info += " " + to_string_dec_uint(bitrate); + console_info += " ADDR:" + to_string_dec_uint(pocsag_state.address); + console_info += " F" + to_string_dec_uint(pocsag_state.function); + + // Store last received address for POCSAG TX + persistent_memory::set_pocsag_last_address(pocsag_state.address); + + if (pocsag_state.out_type == ADDRESS) { + // Address only + + console.write(console_info); + + if (logger && logging) { + logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + + " F" + to_string_dec_uint(pocsag_state.function) + + " Address only"); + } + + last_address = pocsag_state.address; + } else if (pocsag_state.out_type == MESSAGE) { + if (pocsag_state.address != last_address) { + // New message + console.writeln(console_info); + console.write(pocsag_state.output); + + last_address = pocsag_state.address; + } else { + // Message continues... + console.write(pocsag_state.output); + } + + if (logger && logging) + logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + + " F" + to_string_dec_uint(pocsag_state.function) + + " Alpha: " + pocsag_state.output); + } + } + + // Log raw data whatever it contains + if (logger && logging) + logger->log_raw_data(message->packet, target_frequency()); } void POCSAGAppView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - receiver_model.set_tuning_frequency(new_value); + target_frequency_ = new_value; + receiver_model.set_tuning_frequency(new_value); } uint32_t POCSAGAppView::target_frequency() const { - return target_frequency_; + return target_frequency_; } } /* namespace ui */ diff --git a/firmware/application/apps/pocsag_app.hpp b/firmware/application/apps/pocsag_app.hpp index 316a3cb8a..1cde04b9b 100644 --- a/firmware/application/apps/pocsag_app.hpp +++ b/firmware/application/apps/pocsag_app.hpp @@ -33,116 +33,108 @@ #include "pocsag_packet.hpp" class POCSAGLogger { -public: - Optional append(const std::string& filename) { - return log_file.append(filename); - } - - void log_raw_data(const pocsag::POCSAGPacket& packet, const uint32_t frequency); - void log_decoded(const pocsag::POCSAGPacket& packet, const std::string text); - -private: - LogFile log_file { }; + public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const pocsag::POCSAGPacket& packet, const uint32_t frequency); + void log_decoded(const pocsag::POCSAGPacket& packet, const std::string text); + + private: + LogFile log_file{}; }; namespace ui { class POCSAGAppView : public View { -public: - POCSAGAppView(NavigationView& nav); - ~POCSAGAppView(); - - void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; - - std::string title() const override { return "POCSAG RX"; }; - -private: - static constexpr uint32_t initial_target_frequency = 466175000; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - bool logging { false }; - bool ignore { false }; - uint32_t last_address = 0xFFFFFFFF; - pocsag::POCSAGState pocsag_state { }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - Audio audio{ - { 21 * 8, 10, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 8 }, - }; - NumberField field_volume { - { 28 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - Checkbox check_ignore { - { 0 * 8, 21 }, - 8, - "Ign addr", - false - }; - SymField sym_ignore { - { 13 * 8, 25 }, - 7, - SymField::SYMFIELD_DEC - }; - Checkbox check_log { - { 240 - 8 * 8, 21 }, - 3, - "LOG", - false - }; - - Console console { - { 0, 3 * 16, 240, 256 } - }; - - std::unique_ptr logger { }; - - uint32_t target_frequency_ = initial_target_frequency; - - void update_freq(rf::Frequency f); - - void on_packet(const POCSAGPacketMessage * message); - - void on_headphone_volume_changed(int32_t v); - - uint32_t target_frequency() const; - void set_target_frequency(const uint32_t new_value); - - MessageHandlerRegistration message_handler_packet { - Message::ID::POCSAGPacket, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_packet(message); - } - }; + public: + POCSAGAppView(NavigationView& nav); + ~POCSAGAppView(); + + void set_parent_rect(const Rect new_parent_rect) override; + void focus() override; + + std::string title() const override { return "POCSAG RX"; }; + + private: + static constexpr uint32_t initial_target_frequency = 466175000; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + bool logging{false}; + bool ignore{false}; + uint32_t last_address = 0xFFFFFFFF; + pocsag::POCSAGState pocsag_state{}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + Audio audio{ + {21 * 8, 10, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 8}, + }; + NumberField field_volume{ + {28 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + Checkbox check_ignore{ + {0 * 8, 21}, + 8, + "Ign addr", + false}; + SymField sym_ignore{ + {13 * 8, 25}, + 7, + SymField::SYMFIELD_DEC}; + Checkbox check_log{ + {240 - 8 * 8, 21}, + 3, + "LOG", + false}; + + Console console{ + {0, 3 * 16, 240, 256}}; + + std::unique_ptr logger{}; + + uint32_t target_frequency_ = initial_target_frequency; + + void update_freq(rf::Frequency f); + + void on_packet(const POCSAGPacketMessage* message); + + void on_headphone_volume_changed(int32_t v); + + uint32_t target_frequency() const; + void set_target_frequency(const uint32_t new_value); + + MessageHandlerRegistration message_handler_packet{ + Message::ID::POCSAGPacket, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_packet(message); + }}; }; } /* namespace ui */ -#endif/*__POCSAG_APP_H__*/ +#endif /*__POCSAG_APP_H__*/ diff --git a/firmware/application/apps/replay_app.cpp b/firmware/application/apps/replay_app.cpp index 2e0fd358a..5da956cf2 100644 --- a/firmware/application/apps/replay_app.cpp +++ b/firmware/application/apps/replay_app.cpp @@ -36,242 +36,238 @@ using namespace portapack; namespace ui { void ReplayAppView::set_ready() { - ready_signal = true; + ready_signal = true; } void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) { - File data_file, info_file; - char file_data[257]; - - // Get file size - auto data_open_error = data_file.open("/" + new_file_path.string()); - if (data_open_error.is_valid()) { - file_error(); - return; - } - - file_path = new_file_path; - - // Get original record frequency if available - std::filesystem::path info_file_path = file_path; - info_file_path.replace_extension(u".TXT"); - - sample_rate = 500000; - - auto info_open_error = info_file.open("/" + info_file_path.string()); - if (!info_open_error.is_valid()) { - memset(file_data, 0, 257); - auto read_size = info_file.read(file_data, 256); - if (!read_size.is_error()) { - auto pos1 = strstr(file_data, "center_frequency="); - if (pos1) { - pos1 += 17; - field_frequency.set_value(strtoll(pos1, nullptr, 10)); - } - - auto pos2 = strstr(file_data, "sample_rate="); - if (pos2) { - pos2 += 12; - sample_rate = strtoll(pos2, nullptr, 10); - } - } - } - - text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz"); - - auto file_size = data_file.size(); - auto duration = (file_size * 1000) / (2 * 2 * sample_rate); - - progressbar.set_max(file_size); - text_filename.set(file_path.filename().string().substr(0, 12)); - text_duration.set(to_string_time_ms(duration)); - - button_play.focus(); + File data_file, info_file; + char file_data[257]; + + // Get file size + auto data_open_error = data_file.open("/" + new_file_path.string()); + if (data_open_error.is_valid()) { + file_error(); + return; + } + + file_path = new_file_path; + + // Get original record frequency if available + std::filesystem::path info_file_path = file_path; + info_file_path.replace_extension(u".TXT"); + + sample_rate = 500000; + + auto info_open_error = info_file.open("/" + info_file_path.string()); + if (!info_open_error.is_valid()) { + memset(file_data, 0, 257); + auto read_size = info_file.read(file_data, 256); + if (!read_size.is_error()) { + auto pos1 = strstr(file_data, "center_frequency="); + if (pos1) { + pos1 += 17; + field_frequency.set_value(strtoll(pos1, nullptr, 10)); + } + + auto pos2 = strstr(file_data, "sample_rate="); + if (pos2) { + pos2 += 12; + sample_rate = strtoll(pos2, nullptr, 10); + } + } + } + + text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz"); + + auto file_size = data_file.size(); + auto duration = (file_size * 1000) / (2 * 2 * sample_rate); + + progressbar.set_max(file_size); + text_filename.set(file_path.filename().string().substr(0, 12)); + text_duration.set(to_string_time_ms(duration)); + + button_play.focus(); } void ReplayAppView::on_tx_progress(const uint32_t progress) { - progressbar.set_value(progress); + progressbar.set_value(progress); } void ReplayAppView::focus() { - button_open.focus(); + button_open.focus(); } void ReplayAppView::file_error() { - nav_.display_modal("Error", "File read error."); + nav_.display_modal("Error", "File read error."); } bool ReplayAppView::is_active() const { - return (bool)replay_thread; + return (bool)replay_thread; } void ReplayAppView::toggle() { - if( is_active() ) { - stop(false); - } else { - start(); - } + if (is_active()) { + stop(false); + } else { + start(); + } } void ReplayAppView::start() { - stop(false); - - std::unique_ptr reader; - - auto p = std::make_unique(); - auto open_error = p->open(file_path); - if( open_error.is_valid() ) { - file_error(); - return; // Fixes TX bug if there's a file error - } else { - reader = std::move(p); - } - - if( reader ) { - button_play.set_bitmap(&bitmap_stop); - baseband::set_sample_rate(sample_rate * 8); - - replay_thread = std::make_unique( - std::move(reader), - read_size, buffer_count, - &ready_signal, - [](uint32_t return_code) { - ReplayThreadDoneMessage message { return_code }; - EventDispatcher::send_message(message); - } - ); - } - - //Enable Bias Tee if selected - radio::set_antenna_bias(portapack::get_antenna_bias()); - - rf_amp =(transmitter_model.rf_amp() ); // recover rf_amp settings applied from ui_transmiter.cpp - - radio::enable({ - receiver_model.tuning_frequency(), - sample_rate * 8, - baseband_bandwidth, - rf::Direction::Transmit, - rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()) - }); - - if (portapack::persistent_memory::stealth_mode()){ - DisplaySleepMessage message; - EventDispatcher::send_message(message); - } - -} + stop(false); + + std::unique_ptr reader; + + auto p = std::make_unique(); + auto open_error = p->open(file_path); + if (open_error.is_valid()) { + file_error(); + return; // Fixes TX bug if there's a file error + } else { + reader = std::move(p); + } + + if (reader) { + button_play.set_bitmap(&bitmap_stop); + baseband::set_sample_rate(sample_rate * 8); + + replay_thread = std::make_unique( + std::move(reader), + read_size, buffer_count, + &ready_signal, + [](uint32_t return_code) { + ReplayThreadDoneMessage message{return_code}; + EventDispatcher::send_message(message); + }); + } + + // Enable Bias Tee if selected + radio::set_antenna_bias(portapack::get_antenna_bias()); + + rf_amp = (transmitter_model.rf_amp()); // recover rf_amp settings applied from ui_transmiter.cpp + + radio::enable({receiver_model.tuning_frequency(), + sample_rate * 8, + baseband_bandwidth, + rf::Direction::Transmit, + rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga())}); + + if (portapack::persistent_memory::stealth_mode()) { + DisplaySleepMessage message; + EventDispatcher::send_message(message); + } +} void ReplayAppView::stop(const bool do_loop) { - if( is_active() ) - replay_thread.reset(); - - if (do_loop && check_loop.value()) { - start(); - } else { - radio::set_antenna_bias(false); //Turn off Bias Tee - radio::disable(); - button_play.set_bitmap(&bitmap_play); - } - - ready_signal = false; + if (is_active()) + replay_thread.reset(); + + if (do_loop && check_loop.value()) { + start(); + } else { + radio::set_antenna_bias(false); // Turn off Bias Tee + radio::disable(); + button_play.set_bitmap(&bitmap_play); + } + + ready_signal = false; } void ReplayAppView::handle_replay_thread_done(const uint32_t return_code) { - if (return_code == ReplayThread::END_OF_FILE) { - stop(true); - } else if (return_code == ReplayThread::READ_ERROR) { - stop(false); - file_error(); - } - - progressbar.set_value(0); + if (return_code == ReplayThread::END_OF_FILE) { + stop(true); + } else if (return_code == ReplayThread::READ_ERROR) { + stop(false); + file_error(); + } + + progressbar.set_value(0); } ReplayAppView::ReplayAppView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_replay); - - add_children({ - &button_open, - &text_filename, - &text_sample_rate, - &text_duration, - &progressbar, - &field_frequency, - &tx_view, // now it handles previous rfgain , rfamp. - &check_loop, - &button_play, - &waterfall, - }); - - field_frequency.set_value(target_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(this->target_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency.set_step(5000); - - button_play.on_select = [this](ImageButton&) { - this->toggle(); - }; - - button_open.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".C16"); - open_view->on_changed = [this](std::filesystem::path new_file_path) { - on_file_changed(new_file_path); - }; - }; + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_replay); + + add_children({ + &button_open, + &text_filename, + &text_sample_rate, + &text_duration, + &progressbar, + &field_frequency, + &tx_view, // now it handles previous rfgain , rfamp. + &check_loop, + &button_play, + &waterfall, + }); + + field_frequency.set_value(target_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(this->target_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.set_step(5000); + + button_play.on_select = [this](ImageButton&) { + this->toggle(); + }; + + button_open.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".C16"); + open_view->on_changed = [this](std::filesystem::path new_file_path) { + on_file_changed(new_file_path); + }; + }; } ReplayAppView::~ReplayAppView() { - radio::disable(); - - display.fill_rectangle({ 0,0,240, 320 },Color::black()); //Solving sometimes visible bottom waterfall artifacts, clearing all LCD pixels. - chThdSleepMilliseconds(40); // (that happened sometimes if we interrupt the waterfall play at the beggining of the play around 25% and exit ) - hackrf::cpld::load_sram_no_verify(); // to leave all RX reception ok, without "ghost interference signal problem" at the exit . + radio::disable(); + + display.fill_rectangle({0, 0, 240, 320}, Color::black()); // Solving sometimes visible bottom waterfall artifacts, clearing all LCD pixels. + chThdSleepMilliseconds(40); // (that happened sometimes if we interrupt the waterfall play at the beggining of the play around 25% and exit ) + hackrf::cpld::load_sram_no_verify(); // to leave all RX reception ok, without "ghost interference signal problem" at the exit . - baseband::shutdown(); // better this function at the end, after load_sram(). If not , sometimes produced hang up (now not , it is ok). + baseband::shutdown(); // better this function at the end, after load_sram(). If not , sometimes produced hang up (now not , it is ok). } void ReplayAppView::on_hide() { - stop(false); - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - waterfall.on_hide(); - View::on_hide(); + stop(false); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + waterfall.on_hide(); + View::on_hide(); } void ReplayAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - waterfall.set_parent_rect(waterfall_rect); + const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + waterfall.set_parent_rect(waterfall_rect); } void ReplayAppView::on_target_frequency_changed(rf::Frequency f) { - set_target_frequency(f); + set_target_frequency(f); } void ReplayAppView::set_target_frequency(const rf::Frequency new_value) { - persistent_memory::set_tuned_frequency(new_value);; + persistent_memory::set_tuned_frequency(new_value); + ; } rf::Frequency ReplayAppView::target_frequency() const { - return persistent_memory::tuned_frequency(); + return persistent_memory::tuned_frequency(); } } /* namespace ui */ diff --git a/firmware/application/apps/replay_app.hpp b/firmware/application/apps/replay_app.hpp index 45148a369..c9fa098a0 100644 --- a/firmware/application/apps/replay_app.hpp +++ b/firmware/application/apps/replay_app.hpp @@ -23,8 +23,8 @@ #ifndef __REPLAY_APP_HPP__ #define __REPLAY_APP_HPP__ -#define SHORT_UI true -#define NORMAL_UI false +#define SHORT_UI true +#define NORMAL_UI false #include "ui_widget.hpp" #include "ui_navigation.hpp" @@ -39,120 +39,111 @@ namespace ui { class ReplayAppView : public View { -public: - ReplayAppView(NavigationView& nav); - ~ReplayAppView(); - - void on_hide() override; - void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; - - std::string title() const override { return "Replay"; }; - -private: - NavigationView& nav_; - - static constexpr ui::Dim header_height = 3 * 16; - - uint32_t sample_rate = 0; - int32_t tx_gain { 47 }; - bool rf_amp { true }; // aux private var to store temporal, Replay App rf_amp user selection. - static constexpr uint32_t baseband_bandwidth = 2500000; - const size_t read_size { 16384 }; - const size_t buffer_count { 3 }; - - void on_file_changed(std::filesystem::path new_file_path); - void on_target_frequency_changed(rf::Frequency f); - void on_tx_progress(const uint32_t progress); - - void set_target_frequency(const rf::Frequency new_value); - rf::Frequency target_frequency() const; - - void toggle(); - void start(); - void stop(const bool do_loop); - bool is_active() const; - void set_ready(); - void handle_replay_thread_done(const uint32_t return_code); - void file_error(); - - std::filesystem::path file_path { }; - std::unique_ptr replay_thread { }; - bool ready_signal { false }; - - Button button_open { - { 0 * 8, 0 * 16, 10 * 8, 2 * 16 }, - "Open file" - }; - - Text text_filename { - { 11 * 8, 0 * 16, 12 * 8, 16 }, - "-" - }; - Text text_sample_rate { - { 24 * 8, 0 * 16, 6 * 8, 16 }, - "-" - }; - - Text text_duration { - { 11 * 8, 1 * 16, 6 * 8, 16 }, - "-" - }; - ProgressBar progressbar { - { 18 * 8, 1 * 16, 12 * 8, 16 } - }; - - FrequencyField field_frequency { - { 0 * 8, 2 * 16 }, - }; - - TransmitterView2 tx_view { // new handling of NumberField field_rfgain, NumberField field_rfamp - 74, 1*8, SHORT_UI // x(columns), y (line) position. (Uused in Repay / GPS Simul / Playlist App) -// 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) - }; - - Checkbox check_loop { - { 21 * 8, 2 * 16 }, - 4, - "Loop", - true - }; - ImageButton button_play { - { 28 * 8, 2 * 16, 2 * 8, 1 * 16 }, - &bitmap_play, - Color::green(), - Color::black() - }; - - spectrum::WaterfallWidget waterfall { }; - - MessageHandlerRegistration message_handler_replay_thread_error { - Message::ID::ReplayThreadDone, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_replay_thread_done(message.return_code); - } - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->set_ready(); - } - } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress); - } - }; + public: + ReplayAppView(NavigationView& nav); + ~ReplayAppView(); + + void on_hide() override; + void set_parent_rect(const Rect new_parent_rect) override; + void focus() override; + + std::string title() const override { return "Replay"; }; + + private: + NavigationView& nav_; + + static constexpr ui::Dim header_height = 3 * 16; + + uint32_t sample_rate = 0; + int32_t tx_gain{47}; + bool rf_amp{true}; // aux private var to store temporal, Replay App rf_amp user selection. + static constexpr uint32_t baseband_bandwidth = 2500000; + const size_t read_size{16384}; + const size_t buffer_count{3}; + + void on_file_changed(std::filesystem::path new_file_path); + void on_target_frequency_changed(rf::Frequency f); + void on_tx_progress(const uint32_t progress); + + void set_target_frequency(const rf::Frequency new_value); + rf::Frequency target_frequency() const; + + void toggle(); + void start(); + void stop(const bool do_loop); + bool is_active() const; + void set_ready(); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); + + std::filesystem::path file_path{}; + std::unique_ptr replay_thread{}; + bool ready_signal{false}; + + Button button_open{ + {0 * 8, 0 * 16, 10 * 8, 2 * 16}, + "Open file"}; + + Text text_filename{ + {11 * 8, 0 * 16, 12 * 8, 16}, + "-"}; + Text text_sample_rate{ + {24 * 8, 0 * 16, 6 * 8, 16}, + "-"}; + + Text text_duration{ + {11 * 8, 1 * 16, 6 * 8, 16}, + "-"}; + ProgressBar progressbar{ + {18 * 8, 1 * 16, 12 * 8, 16}}; + + FrequencyField field_frequency{ + {0 * 8, 2 * 16}, + }; + + TransmitterView2 tx_view{ + // new handling of NumberField field_rfgain, NumberField field_rfamp + 74, 1 * 8, SHORT_UI // x(columns), y (line) position. (Uused in Repay / GPS Simul / Playlist App) + // 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) + }; + + Checkbox check_loop{ + {21 * 8, 2 * 16}, + 4, + "Loop", + true}; + ImageButton button_play{ + {28 * 8, 2 * 16, 2 * 8, 1 * 16}, + &bitmap_play, + Color::green(), + Color::black()}; + + spectrum::WaterfallWidget waterfall{}; + + MessageHandlerRegistration message_handler_replay_thread_error{ + Message::ID::ReplayThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_replay_thread_done(message.return_code); + }}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->set_ready(); + } + }}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress); + }}; }; } /* namespace ui */ -#endif/*__REPLAY_APP_HPP__*/ +#endif /*__REPLAY_APP_HPP__*/ diff --git a/firmware/application/apps/soundboard_app.cpp b/firmware/application/apps/soundboard_app.cpp index 898d95cc9..6ece0ce9d 100644 --- a/firmware/application/apps/soundboard_app.cpp +++ b/firmware/application/apps/soundboard_app.cpp @@ -33,272 +33,264 @@ using namespace portapack; namespace ui { bool SoundBoardView::is_active() const { - return (bool)replay_thread; + return (bool)replay_thread; } void SoundBoardView::stop() { - if (is_active()) - replay_thread.reset(); - - transmitter_model.disable(); - tx_view.set_transmitting(false); - - //button_play.set_bitmap(&bitmap_play); - ready_signal = false; + if (is_active()) + replay_thread.reset(); + + transmitter_model.disable(); + tx_view.set_transmitting(false); + + // button_play.set_bitmap(&bitmap_play); + ready_signal = false; } void SoundBoardView::handle_replay_thread_done(const uint32_t return_code) { - stop(); - //progressbar.set_value(0); - - if (return_code == ReplayThread::END_OF_FILE) { - if (check_random.value()) { - lfsr_v = lfsr_iterate(lfsr_v); - playing_id = lfsr_v % file_list.size(); - menu_view.set_highlighted(playing_id); - start_tx(playing_id); - } else if (check_loop.value()) { - start_tx(playing_id); - } - } else if (return_code == ReplayThread::READ_ERROR) { - file_error(); - } + stop(); + // progressbar.set_value(0); + + if (return_code == ReplayThread::END_OF_FILE) { + if (check_random.value()) { + lfsr_v = lfsr_iterate(lfsr_v); + playing_id = lfsr_v % file_list.size(); + menu_view.set_highlighted(playing_id); + start_tx(playing_id); + } else if (check_loop.value()) { + start_tx(playing_id); + } + } else if (return_code == ReplayThread::READ_ERROR) { + file_error(); + } } void SoundBoardView::set_ready() { - ready_signal = true; + ready_signal = true; } void SoundBoardView::focus() { - menu_view.focus(); + menu_view.focus(); } void SoundBoardView::file_error() { - nav_.display_modal("Error", "File read error."); + nav_.display_modal("Error", "File read error."); } void SoundBoardView::start_tx(const uint32_t id) { - auto reader = std::make_unique(); - uint32_t tone_key_index = options_tone_key.selected_index(); - uint32_t sample_rate; - - stop(); - - if (!reader->open(u"/WAV/" + file_list[id].native())) { - file_error(); - return; - } - - playing_id = id; - - //progressbar.set_max(reader->sample_count()); - - //button_play.set_bitmap(&bitmap_stop); - - sample_rate = reader->sample_rate(); - - replay_thread = std::make_unique( - std::move(reader), - read_size, buffer_count, - &ready_signal, - [](uint32_t return_code) { - ReplayThreadDoneMessage message { return_code }; - EventDispatcher::send_message(message); - } - ); - - baseband::set_audiotx_config( - 1536000 / 20, // Update vu-meter at 20Hz - transmitter_model.channel_bandwidth(), - 0, // Gain is unused - 8, // shift_bits_s16, default 8 bits, but also unused - TONES_F2D(tone_key_frequency(tone_key_index), 1536000), - 0, //AM - 0, //DSB - 0, //USB - 0 //LSB - ); - baseband::set_sample_rate(sample_rate); - - transmitter_model.set_sampling_rate(1536000); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - tx_view.set_transmitting(true); + auto reader = std::make_unique(); + uint32_t tone_key_index = options_tone_key.selected_index(); + uint32_t sample_rate; + + stop(); + + if (!reader->open(u"/WAV/" + file_list[id].native())) { + file_error(); + return; + } + + playing_id = id; + + // progressbar.set_max(reader->sample_count()); + + // button_play.set_bitmap(&bitmap_stop); + + sample_rate = reader->sample_rate(); + + replay_thread = std::make_unique( + std::move(reader), + read_size, buffer_count, + &ready_signal, + [](uint32_t return_code) { + ReplayThreadDoneMessage message{return_code}; + EventDispatcher::send_message(message); + }); + + baseband::set_audiotx_config( + 1536000 / 20, // Update vu-meter at 20Hz + transmitter_model.channel_bandwidth(), + 0, // Gain is unused + 8, // shift_bits_s16, default 8 bits, but also unused + TONES_F2D(tone_key_frequency(tone_key_index), 1536000), + 0, // AM + 0, // DSB + 0, // USB + 0 // LSB + ); + baseband::set_sample_rate(sample_rate); + + transmitter_model.set_sampling_rate(1536000); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + tx_view.set_transmitting(true); } /*void SoundBoardView::show_infos() { - if (!reader->open(file_list[menu_view.highlighted_index()])) - return; - - text_duration.set(to_string_time_ms(reader->ms_duration())); - text_title.set(reader->title().substr(0, 15)); + if (!reader->open(file_list[menu_view.highlighted_index()])) + return; + + text_duration.set(to_string_time_ms(reader->ms_duration())); + text_title.set(reader->title().substr(0, 15)); }*/ void SoundBoardView::on_tx_progress(const uint32_t progress) { - (void)progress ; // avoid warning - //progressbar.set_value(progress); + (void)progress; // avoid warning + // progressbar.set_value(progress); } void SoundBoardView::on_select_entry() { - tx_view.focus(); + tx_view.focus(); } void SoundBoardView::refresh_list() { - auto reader = std::make_unique(); - - file_list.clear(); - c_page = page; - - // List directories and files, put directories up top - uint32_t count = 0; - for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) { - if (std::filesystem::is_regular_file(entry.status())) { - if (entry.path().string().length()) { - - auto entry_extension = entry.path().extension().string(); - - for (auto &c: entry_extension) - c = toupper(c); - - if (entry_extension == ".WAV") { - - if (reader->open(u"/WAV/" + entry.path().native())) { - if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) { - //sounds[c].ms_duration = reader->ms_duration(); - //sounds[c].path = u"WAV/" + entry.path().native(); - if (count >= (page - 1) * 100 && count < page * 100){ - file_list.push_back(entry.path()); - if (file_list.size() == 100){ - page++; - break; - } - } - count++; - } - } - } - } - } - } - - if (!file_list.size()) { - // Hide widgets, show warning - if (page == 1){ - menu_view.hidden(true); - text_empty.hidden(false); - set_dirty(); - }else{ - page = 1; - refresh_list(); - return; - } - } else { - // Hide warning, show widgets - menu_view.hidden(false); - text_empty.hidden(true); - set_dirty(); - - menu_view.clear(); - - for (size_t n = 0; n < file_list.size(); n++) { - menu_view.add_item({ - file_list[n].string().substr(0, 30), - ui::Color::white(), - nullptr, - [this](KeyEvent){ - on_select_entry(); - } - }); - } - - page_info.set("Page: " + to_string_dec_uint(c_page) + " Sounds: " + to_string_dec_uint(file_list.size())); - menu_view.set_highlighted(0); // Refresh - } - - if (file_list.size() < 100){ - page = 1; - } + auto reader = std::make_unique(); + + file_list.clear(); + c_page = page; + + // List directories and files, put directories up top + uint32_t count = 0; + for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) { + if (std::filesystem::is_regular_file(entry.status())) { + if (entry.path().string().length()) { + auto entry_extension = entry.path().extension().string(); + + for (auto& c : entry_extension) + c = toupper(c); + + if (entry_extension == ".WAV") { + if (reader->open(u"/WAV/" + entry.path().native())) { + if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) { + // sounds[c].ms_duration = reader->ms_duration(); + // sounds[c].path = u"WAV/" + entry.path().native(); + if (count >= (page - 1) * 100 && count < page * 100) { + file_list.push_back(entry.path()); + if (file_list.size() == 100) { + page++; + break; + } + } + count++; + } + } + } + } + } + } + + if (!file_list.size()) { + // Hide widgets, show warning + if (page == 1) { + menu_view.hidden(true); + text_empty.hidden(false); + set_dirty(); + } else { + page = 1; + refresh_list(); + return; + } + } else { + // Hide warning, show widgets + menu_view.hidden(false); + text_empty.hidden(true); + set_dirty(); + + menu_view.clear(); + + for (size_t n = 0; n < file_list.size(); n++) { + menu_view.add_item({file_list[n].string().substr(0, 30), + ui::Color::white(), + nullptr, + [this](KeyEvent) { + on_select_entry(); + }}); + } + + page_info.set("Page: " + to_string_dec_uint(c_page) + " Sounds: " + to_string_dec_uint(file_list.size())); + menu_view.set_highlighted(0); // Refresh + } + + if (file_list.size() < 100) { + page = 1; + } } SoundBoardView::SoundBoardView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_audio_tx); - - add_children({ - &labels, - &menu_view, - &text_empty, - &options_tone_key, - //&text_title, - //&text_duration, - //&progressbar, - &page_info, - &check_loop, - &check_random, - &button_prev_page, - &button_next_page, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_soundboard", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - refresh_list(); - - button_next_page.on_select = [this](Button&) { - this->refresh_list(); - }; - - button_prev_page.on_select = [this](Button&) { - if (c_page == 1) return; - if (c_page == 2) page = 1; - page = c_page - 1; - refresh_list(); - }; - - //text_title.set(to_string_dec_uint(file_list.size())); - - tone_keys_populate(options_tone_key); - options_tone_key.set_selected_index(0); - - check_loop.set_value(false); - check_random.set_value(false); - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(menu_view.highlighted_index()); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - stop(); - }; + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_audio_tx); + + add_children({&labels, + &menu_view, + &text_empty, + &options_tone_key, + //&text_title, + //&text_duration, + //&progressbar, + &page_info, + &check_loop, + &check_random, + &button_prev_page, + &button_next_page, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_soundboard", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + refresh_list(); + + button_next_page.on_select = [this](Button&) { + this->refresh_list(); + }; + + button_prev_page.on_select = [this](Button&) { + if (c_page == 1) return; + if (c_page == 2) page = 1; + page = c_page - 1; + refresh_list(); + }; + + // text_title.set(to_string_dec_uint(file_list.size())); + + tone_keys_populate(options_tone_key); + options_tone_key.set_selected_index(0); + + check_loop.set_value(false); + check_random.set_value(false); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(menu_view.highlighted_index()); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + stop(); + }; } SoundBoardView::~SoundBoardView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_soundboard", &app_settings); - - stop(); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. -} + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_soundboard", &app_settings); + stop(); + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } + +} // namespace ui diff --git a/firmware/application/apps/soundboard_app.hpp b/firmware/application/apps/soundboard_app.hpp index 6d59caa56..b946f80e3 100644 --- a/firmware/application/apps/soundboard_app.hpp +++ b/firmware/application/apps/soundboard_app.hpp @@ -35,148 +35,136 @@ namespace ui { class SoundBoardView : public View { -public: - SoundBoardView(NavigationView& nav); - ~SoundBoardView(); - - SoundBoardView(const SoundBoardView&) = delete; - SoundBoardView(SoundBoardView&&) = delete; - SoundBoardView& operator=(const SoundBoardView&) = delete; - SoundBoardView& operator=(SoundBoardView&&) = delete; - - void focus() override; - - std::string title() const override { return "Soundbrd TX"; }; - -private: - NavigationView& nav_; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - enum tx_modes { - NORMAL = 0, - RANDOM - }; - - tx_modes tx_mode = NORMAL; - - uint32_t playing_id { }; - uint32_t page = 1; - uint32_t c_page = 1; - - std::vector file_list { }; - - const size_t read_size { 2048 }; // Less ? - const size_t buffer_count { 3 }; - std::unique_ptr replay_thread { }; - bool ready_signal { false }; - lfsr_word_t lfsr_v = 1; - - //void show_infos(); - void start_tx(const uint32_t id); - //void on_ctcss_changed(uint32_t v); - void stop(); - bool is_active() const; - void set_ready(); - void handle_replay_thread_done(const uint32_t return_code); - void file_error(); - void on_tx_progress(const uint32_t progress); - void refresh_list(); - void on_select_entry(); - - Labels labels { - //{ { 0, 20 * 8 + 4 }, "Title:", Color::light_grey() }, - { { 0, 180 }, "Key:", Color::light_grey() } - }; - - Button button_next_page { - { 30 * 7, 25 * 8, 10 * 3, 2 * 14 }, - "=>" - }; - - Button button_prev_page { - { 17 * 10, 25 * 8, 10 * 3, 2 * 14 }, - "<=" - }; - - Text page_info { - { 0, 30 * 8 - 4, 30 * 8, 16 } - }; - - MenuView menu_view { - { 0, 0, 240, 175 }, - true - }; - Text text_empty { - { 7 * 8, 12 * 8, 16 * 8, 16 }, - "Empty directory !", - }; - - /*Text text_title { - { 6 * 8, 20 * 8 + 4, 15 * 8, 16 } - };*/ - - /*Text text_duration { - { 22 * 8, 20 * 8 + 4, 6 * 8, 16 } - };*/ - - OptionsField options_tone_key { - { 32 , 180 }, - 18, - { } - }; - - Checkbox check_loop { - { 0, 25 * 8 + 4 }, - 4, - "Loop" - }; - - Checkbox check_random { - { 10 * 7, 25 * 8 + 4 }, - 6, - "Random" - }; - - //ProgressBar progressbar { - // { 0 * 8, 30 * 8 - 4, 30 * 8, 16 } - //}; - - TransmitterView tx_view { - 16 * 16, - 5000, - 12 - }; - - MessageHandlerRegistration message_handler_replay_thread_error { - Message::ID::ReplayThreadDone, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_replay_thread_done(message.return_code); - } - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->set_ready(); - } - } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress); - } - }; + public: + SoundBoardView(NavigationView& nav); + ~SoundBoardView(); + + SoundBoardView(const SoundBoardView&) = delete; + SoundBoardView(SoundBoardView&&) = delete; + SoundBoardView& operator=(const SoundBoardView&) = delete; + SoundBoardView& operator=(SoundBoardView&&) = delete; + + void focus() override; + + std::string title() const override { return "Soundbrd TX"; }; + + private: + NavigationView& nav_; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + enum tx_modes { + NORMAL = 0, + RANDOM + }; + + tx_modes tx_mode = NORMAL; + + uint32_t playing_id{}; + uint32_t page = 1; + uint32_t c_page = 1; + + std::vector file_list{}; + + const size_t read_size{2048}; // Less ? + const size_t buffer_count{3}; + std::unique_ptr replay_thread{}; + bool ready_signal{false}; + lfsr_word_t lfsr_v = 1; + + // void show_infos(); + void start_tx(const uint32_t id); + // void on_ctcss_changed(uint32_t v); + void stop(); + bool is_active() const; + void set_ready(); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); + void on_tx_progress(const uint32_t progress); + void refresh_list(); + void on_select_entry(); + + Labels labels{ + //{ { 0, 20 * 8 + 4 }, "Title:", Color::light_grey() }, + {{0, 180}, "Key:", Color::light_grey()}}; + + Button button_next_page{ + {30 * 7, 25 * 8, 10 * 3, 2 * 14}, + "=>"}; + + Button button_prev_page{ + {17 * 10, 25 * 8, 10 * 3, 2 * 14}, + "<="}; + + Text page_info{ + {0, 30 * 8 - 4, 30 * 8, 16}}; + + MenuView menu_view{ + {0, 0, 240, 175}, + true}; + Text text_empty{ + {7 * 8, 12 * 8, 16 * 8, 16}, + "Empty directory !", + }; + + /*Text text_title { + { 6 * 8, 20 * 8 + 4, 15 * 8, 16 } + };*/ + + /*Text text_duration { + { 22 * 8, 20 * 8 + 4, 6 * 8, 16 } + };*/ + + OptionsField options_tone_key{ + {32, 180}, + 18, + {}}; + + Checkbox check_loop{ + {0, 25 * 8 + 4}, + 4, + "Loop"}; + + Checkbox check_random{ + {10 * 7, 25 * 8 + 4}, + 6, + "Random"}; + + // ProgressBar progressbar { + // { 0 * 8, 30 * 8 - 4, 30 * 8, 16 } + // }; + + TransmitterView tx_view{ + 16 * 16, + 5000, + 12}; + + MessageHandlerRegistration message_handler_replay_thread_error{ + Message::ID::ReplayThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_replay_thread_done(message.return_code); + }}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->set_ready(); + } + }}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress); + }}; }; } /* namespace ui */ -#endif/*__UI_SOUNDBOARD_H__*/ +#endif /*__UI_SOUNDBOARD_H__*/ diff --git a/firmware/application/apps/spectrum_analysis_app.cpp b/firmware/application/apps/spectrum_analysis_app.cpp index de3a14b0c..c419410b1 100644 --- a/firmware/application/apps/spectrum_analysis_app.cpp +++ b/firmware/application/apps/spectrum_analysis_app.cpp @@ -25,10 +25,10 @@ using namespace portapack; SpectrumAnalysisModel::SpectrumAnalysisModel() { - receiver_model.set_baseband_configuration({ - .mode = 4, - .sampling_rate = 20000000, - .decimation_factor = 1, - }); - receiver_model.set_baseband_bandwidth(12000000); + receiver_model.set_baseband_configuration({ + .mode = 4, + .sampling_rate = 20000000, + .decimation_factor = 1, + }); + receiver_model.set_baseband_bandwidth(12000000); } diff --git a/firmware/application/apps/spectrum_analysis_app.hpp b/firmware/application/apps/spectrum_analysis_app.hpp index 9e8196af9..89ba7a917 100644 --- a/firmware/application/apps/spectrum_analysis_app.hpp +++ b/firmware/application/apps/spectrum_analysis_app.hpp @@ -26,19 +26,18 @@ #include "ui_spectrum.hpp" class SpectrumAnalysisModel { -public: - SpectrumAnalysisModel(); + public: + SpectrumAnalysisModel(); }; namespace ui { class SpectrumAnalysisView : public spectrum::WaterfallWidget { -public: - -private: - SpectrumAnalysisModel model; + public: + private: + SpectrumAnalysisModel model; }; } /* namespace ui */ -#endif/*__SPECTRUM_ANALYSIS_APP_H__*/ +#endif /*__SPECTRUM_ANALYSIS_APP_H__*/ diff --git a/firmware/application/apps/tpms_app.cpp b/firmware/application/apps/tpms_app.cpp index 78d929450..558077c24 100644 --- a/firmware/application/apps/tpms_app.cpp +++ b/firmware/application/apps/tpms_app.cpp @@ -38,32 +38,36 @@ static bool use_kpa = true; static bool use_celsius = true; std::string type(Reading::Type type) { - return to_string_dec_uint(toUType(type), 2); + return to_string_dec_uint(toUType(type), 2); } std::string id(TransponderID id) { - return to_string_hex(id.value(), 8); + return to_string_hex(id.value(), 8); } std::string pressure(Pressure pressure) { - return to_string_dec_int(use_kpa? pressure.kilopascal():pressure.psi(), 3); + return to_string_dec_int(use_kpa ? pressure.kilopascal() : pressure.psi(), 3); } std::string temperature(Temperature temperature) { - return to_string_dec_int(use_celsius? temperature.celsius():temperature.fahrenheit(), 3); + return to_string_dec_int(use_celsius ? temperature.celsius() : temperature.fahrenheit(), 3); } std::string flags(Flags flags) { - return to_string_hex(flags, 2); + return to_string_hex(flags, 2); } static std::string signal_type(SignalType signal_type) { - switch(signal_type) { - case SignalType::FSK_19k2_Schrader: return "FSK 38400 19200 Schrader"; - case SignalType::OOK_8k192_Schrader: return "OOK - 8192 Schrader"; - case SignalType::OOK_8k4_Schrader: return "OOK - 8400 Schrader"; - default: return "- - - -"; - } + switch (signal_type) { + case SignalType::FSK_19k2_Schrader: + return "FSK 38400 19200 Schrader"; + case SignalType::OOK_8k192_Schrader: + return "OOK - 8192 Schrader"; + case SignalType::OOK_8k4_Schrader: + return "OOK - 8400 Schrader"; + default: + return "- - - -"; + } } } /* namespace format */ @@ -71,197 +75,198 @@ static std::string signal_type(SignalType signal_type) { } /* namespace tpms */ void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) { - const auto hex_formatted = packet.symbols_formatted(); + const auto hex_formatted = packet.symbols_formatted(); - // TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue! - const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10); + // TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue! + const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10); - std::string entry = tuning_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors; - log_file.write_entry(packet.received_at(), entry); + std::string entry = tuning_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors; + log_file.write_entry(packet.received_at(), entry); } -const TPMSRecentEntry::Key TPMSRecentEntry::invalid_key = { tpms::Reading::Type::None, 0 }; +const TPMSRecentEntry::Key TPMSRecentEntry::invalid_key = {tpms::Reading::Type::None, 0}; void TPMSRecentEntry::update(const tpms::Reading& reading) { - received_count++; - - if( reading.pressure().is_valid() ) { - last_pressure = reading.pressure(); - } - if( reading.temperature().is_valid() ) { - last_temperature = reading.temperature(); - } - if( reading.flags().is_valid() ) { - last_flags = reading.flags(); - } + received_count++; + + if (reading.pressure().is_valid()) { + last_pressure = reading.pressure(); + } + if (reading.temperature().is_valid()) { + last_temperature = reading.temperature(); + } + if (reading.flags().is_valid()) { + last_flags = reading.flags(); + } } namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id); - - if( entry.last_pressure.is_valid() ) { - line += " " + tpms::format::pressure(entry.last_pressure.value()); - } else { - line += " " " "; - } - - if( entry.last_temperature.is_valid() ) { - line += " " + tpms::format::temperature(entry.last_temperature.value()); - } else { - line += " " " "; - } - - if( entry.received_count > 999 ) { - line += " +++"; - } else { - line += " " + to_string_dec_uint(entry.received_count, 3); - } - - if( entry.last_flags.is_valid() ) { - line += " " + tpms::format::flags(entry.last_flags.value()); - } else { - line += " " " "; - } - - line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.location(), style, line); + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id); + + if (entry.last_pressure.is_valid()) { + line += " " + tpms::format::pressure(entry.last_pressure.value()); + } else { + line += + " " + " "; + } + + if (entry.last_temperature.is_valid()) { + line += " " + tpms::format::temperature(entry.last_temperature.value()); + } else { + line += + " " + " "; + } + + if (entry.received_count > 999) { + line += " +++"; + } else { + line += " " + to_string_dec_uint(entry.received_count, 3); + } + + if (entry.last_flags.is_valid()) { + line += " " + tpms::format::flags(entry.last_flags.value()); + } else { + line += + " " + " "; + } + + line.resize(target_rect.width() / 8, ' '); + painter.draw_string(target_rect.location(), style, line); } TPMSAppView::TPMSAppView(NavigationView&) { - baseband::run_image(portapack::spi_flash::image_tag_tpms); - - add_children({ - &rssi, - &channel, - &options_band, - &options_pressure, - &options_temperature, - &field_rf_amp, - &field_lna, - &field_vga, - &recent_entries_view - }); - - // load app settings - auto rc = settings.load("rx_tpms", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - options_band.set_by_value(app_settings.rx_frequency); - } - else options_band.set_by_value(receiver_model.tuning_frequency()); + baseband::run_image(portapack::spi_flash::image_tag_tpms); + + add_children({&rssi, + &channel, + &options_band, + &options_pressure, + &options_temperature, + &field_rf_amp, + &field_lna, + &field_vga, + &recent_entries_view}); + + // load app settings + auto rc = settings.load("rx_tpms", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + options_band.set_by_value(app_settings.rx_frequency); + } else + options_band.set_by_value(receiver_model.tuning_frequency()); receiver_model.set_tuning_frequency(tuning_frequency()); receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_baseband_bandwidth(baseband_bandwidth); receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - -/* radio::enable({ - tuning_frequency(), - sampling_rate, - baseband_bandwidth, - rf::Direction::Receive, - receiver_model.rf_amp(), - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()), - }); */ - - options_band.on_change = [this](size_t, OptionsField::value_t v) { - this->on_band_changed(v); - }; - options_band.set_by_value(target_frequency()); - - options_pressure.on_change = [this](size_t, int32_t i) { - tpms::format::use_kpa = !i; - update_view(); - }; - - options_pressure.set_selected_index(0, true); - - options_temperature.on_change = [this](size_t, int32_t i) { - tpms::format::use_celsius = !i; - update_view(); - }; - - options_temperature.set_selected_index(0, true); - - logger = std::make_unique(); - if( logger ) { - logger->append( LOG_ROOT_DIR "/TPMS.TXT" ); - } + + /* radio::enable({ + tuning_frequency(), + sampling_rate, + baseband_bandwidth, + rf::Direction::Receive, + receiver_model.rf_amp(), + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga()), + }); */ + + options_band.on_change = [this](size_t, OptionsField::value_t v) { + this->on_band_changed(v); + }; + options_band.set_by_value(target_frequency()); + + options_pressure.on_change = [this](size_t, int32_t i) { + tpms::format::use_kpa = !i; + update_view(); + }; + + options_pressure.set_selected_index(0, true); + + options_temperature.on_change = [this](size_t, int32_t i) { + tpms::format::use_celsius = !i; + update_view(); + }; + + options_temperature.set_selected_index(0, true); + + logger = std::make_unique(); + if (logger) { + logger->append(LOG_ROOT_DIR "/TPMS.TXT"); + } } TPMSAppView::~TPMSAppView() { + // save app settings + app_settings.rx_frequency = target_frequency_; + settings.save("rx_tpms", &app_settings); + receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_ - // save app settings - app_settings.rx_frequency = target_frequency_; - settings.save("rx_tpms", &app_settings); - - receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_ - - baseband::shutdown(); + baseband::shutdown(); } void TPMSAppView::focus() { - options_band.focus(); + options_band.focus(); } void TPMSAppView::update_view() { - recent_entries_view.set_parent_rect(view_normal_rect); + recent_entries_view.set_parent_rect(view_normal_rect); } void TPMSAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - view_normal_rect = { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; + view_normal_rect = {0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; - update_view(); + update_view(); } void TPMSAppView::on_packet(const tpms::Packet& packet) { - if( logger ) { - logger->on_packet(packet, target_frequency()); - } - - const auto reading_opt = packet.reading(); - if( reading_opt.is_valid() ) { - const auto reading = reading_opt.value(); - auto& entry = ::on_packet(recent, TPMSRecentEntry::Key { reading.type(), reading.id() }); - entry.update(reading); - recent_entries_view.set_dirty(); - } + if (logger) { + logger->on_packet(packet, target_frequency()); + } + + const auto reading_opt = packet.reading(); + if (reading_opt.is_valid()) { + const auto reading = reading_opt.value(); + auto& entry = ::on_packet(recent, TPMSRecentEntry::Key{reading.type(), reading.id()}); + entry.update(reading); + recent_entries_view.set_dirty(); + } } void TPMSAppView::on_show_list() { - recent_entries_view.hidden(false); - recent_entries_view.focus(); + recent_entries_view.hidden(false); + recent_entries_view.focus(); } void TPMSAppView::on_band_changed(const uint32_t new_band_frequency) { - set_target_frequency(new_band_frequency); + set_target_frequency(new_band_frequency); } void TPMSAppView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - radio::set_tuning_frequency(tuning_frequency()); + target_frequency_ = new_value; + radio::set_tuning_frequency(tuning_frequency()); } uint32_t TPMSAppView::target_frequency() const { - return target_frequency_; + return target_frequency_; } uint32_t TPMSAppView::tuning_frequency() const { - return target_frequency() - (sampling_rate / 4); + return target_frequency() - (sampling_rate / 4); } } /* namespace ui */ diff --git a/firmware/application/apps/tpms_app.hpp b/firmware/application/apps/tpms_app.hpp index 12a7315b9..a30f6b9e8 100644 --- a/firmware/application/apps/tpms_app.hpp +++ b/firmware/application/apps/tpms_app.hpp @@ -39,51 +39,50 @@ namespace std { constexpr bool operator==(const tpms::TransponderID& lhs, const tpms::TransponderID& rhs) { - return (lhs.value() == rhs.value()); + return (lhs.value() == rhs.value()); } } /* namespace std */ struct TPMSRecentEntry { - using Key = std::pair; + using Key = std::pair; - static const Key invalid_key; + static const Key invalid_key; - tpms::Reading::Type type { invalid_key.first }; - tpms::TransponderID id { invalid_key.second }; + tpms::Reading::Type type{invalid_key.first}; + tpms::TransponderID id{invalid_key.second}; - size_t received_count { 0 }; + size_t received_count{0}; - Optional last_pressure { }; - Optional last_temperature { }; - Optional last_flags { }; + Optional last_pressure{}; + Optional last_temperature{}; + Optional last_flags{}; - TPMSRecentEntry( - const Key& key - ) : type { key.first }, - id { key.second } - { - } + TPMSRecentEntry( + const Key& key) + : type{key.first}, + id{key.second} { + } - Key key() const { - return { type, id }; - } + Key key() const { + return {type, id}; + } - void update(const tpms::Reading& reading); + void update(const tpms::Reading& reading); }; using TPMSRecentEntries = RecentEntries; class TPMSLogger { -public: - Optional append(const std::filesystem::path& filename) { - return log_file.append(filename); - } - - void on_packet(const tpms::Packet& packet, const uint32_t target_frequency); - -private: - LogFile log_file { }; + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + + void on_packet(const tpms::Packet& packet, const uint32_t target_frequency); + + private: + LogFile log_file{}; }; namespace ui { @@ -91,116 +90,105 @@ namespace ui { using TPMSRecentEntriesView = RecentEntriesView; class TPMSAppView : public View { -public: - TPMSAppView(NavigationView& nav); - ~TPMSAppView(); - - void set_parent_rect(const Rect new_parent_rect) override; - - // Prevent painting of region covered entirely by a child. - // TODO: Add flag to View that specifies view does not need to be cleared before painting. - void paint(Painter&) override { }; - - void focus() override; - - std::string title() const override { return "TPMS RX"; }; - -private: - static constexpr uint32_t initial_target_frequency = 315000000; - static constexpr uint32_t sampling_rate = 2457600; - static constexpr uint32_t baseband_bandwidth = 1750000; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - MessageHandlerRegistration message_handler_packet { - Message::ID::TPMSPacket, - [this](Message* const p) { - const auto message = static_cast(p); - const tpms::Packet packet { message->packet, message->signal_type }; - this->on_packet(packet); - } - }; - - static constexpr ui::Dim header_height = 1 * 16; - - ui::Rect view_normal_rect { }; - - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - OptionsField options_band { - { 0 * 8, 0 * 16 }, - 3, - { - { "315", 315000000 }, - { "433", 433920000 }, - } - }; - - OptionsField options_pressure { - { 5 * 8, 0 * 16 }, - 3, - { - { "kPa", 0 }, - { "PSI", 1 } - } - }; - - OptionsField options_temperature { - { 9 * 8, 0 * 16 }, - 1, - { - { "C", 0 }, - { "F", 1 } - } - }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - - TPMSRecentEntries recent { }; - std::unique_ptr logger { }; - - const RecentEntriesColumns columns { { - { "Tp", 2 }, - { "ID", 8 }, - { "Pres", 4 }, - { "Temp", 4 }, - { "Cnt", 3 }, - { "Fl", 2 }, - } }; - TPMSRecentEntriesView recent_entries_view { columns, recent }; - - uint32_t target_frequency_ = initial_target_frequency; - - void on_packet(const tpms::Packet& packet); - void on_show_list(); - void update_view(); - - void on_band_changed(const uint32_t new_band_frequency); - - uint32_t target_frequency() const; - void set_target_frequency(const uint32_t new_value); - - uint32_t tuning_frequency() const; + public: + TPMSAppView(NavigationView& nav); + ~TPMSAppView(); + + void set_parent_rect(const Rect new_parent_rect) override; + + // Prevent painting of region covered entirely by a child. + // TODO: Add flag to View that specifies view does not need to be cleared before painting. + void paint(Painter&) override{}; + + void focus() override; + + std::string title() const override { return "TPMS RX"; }; + + private: + static constexpr uint32_t initial_target_frequency = 315000000; + static constexpr uint32_t sampling_rate = 2457600; + static constexpr uint32_t baseband_bandwidth = 1750000; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::TPMSPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const tpms::Packet packet{message->packet, message->signal_type}; + this->on_packet(packet); + }}; + + static constexpr ui::Dim header_height = 1 * 16; + + ui::Rect view_normal_rect{}; + + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + OptionsField options_band{ + {0 * 8, 0 * 16}, + 3, + { + {"315", 315000000}, + {"433", 433920000}, + }}; + + OptionsField options_pressure{ + {5 * 8, 0 * 16}, + 3, + {{"kPa", 0}, + {"PSI", 1}}}; + + OptionsField options_temperature{ + {9 * 8, 0 * 16}, + 1, + {{"C", 0}, + {"F", 1}}}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + + TPMSRecentEntries recent{}; + std::unique_ptr logger{}; + + const RecentEntriesColumns columns{{ + {"Tp", 2}, + {"ID", 8}, + {"Pres", 4}, + {"Temp", 4}, + {"Cnt", 3}, + {"Fl", 2}, + }}; + TPMSRecentEntriesView recent_entries_view{columns, recent}; + + uint32_t target_frequency_ = initial_target_frequency; + + void on_packet(const tpms::Packet& packet); + void on_show_list(); + void update_view(); + + void on_band_changed(const uint32_t new_band_frequency); + + uint32_t target_frequency() const; + void set_target_frequency(const uint32_t new_value); + + uint32_t tuning_frequency() const; }; } /* namespace ui */ -#endif/*__TPMS_APP_H__*/ +#endif /*__TPMS_APP_H__*/ diff --git a/firmware/application/apps/ui_about.cpp b/firmware/application/apps/ui_about.cpp index ce09961bd..8ae7125d6 100644 --- a/firmware/application/apps/ui_about.cpp +++ b/firmware/application/apps/ui_about.cpp @@ -40,106 +40,99 @@ namespace ui { // This is pretty much WaterfallView but in the opposite direction CreditsWidget::CreditsWidget( - Rect parent_rect -) : Widget { parent_rect } -{ + Rect parent_rect) + : Widget{parent_rect} { } void CreditsWidget::paint(Painter&) { } void CreditsWidget::on_show() { - clear(); + clear(); - const auto screen_r = screen_rect(); - display.scroll_set_area(screen_r.top(), screen_r.bottom()); + const auto screen_r = screen_rect(); + display.scroll_set_area(screen_r.top(), screen_r.bottom()); } void CreditsWidget::on_hide() { - display.scroll_disable(); + display.scroll_disable(); } void CreditsWidget::new_row( - const std::array& pixel_row -) { - // Glitch be here (see comment in main.cpp) - const auto draw_y = display.scroll(-1); - - display.draw_pixels( - { { 0, draw_y - 1 }, { 240, 1 } }, - pixel_row - ); + const std::array& pixel_row) { + // Glitch be here (see comment in main.cpp) + const auto draw_y = display.scroll(-1); + + display.draw_pixels( + {{0, draw_y - 1}, {240, 1}}, + pixel_row); } void CreditsWidget::clear() { - display.fill_rectangle( - screen_rect(), - Color::black() - ); + display.fill_rectangle( + screen_rect(), + Color::black()); } void AboutView::update() { - size_t i = 0; - std::array pixel_row; - - slow_down++; - if (slow_down % 3 < 2) return; - - if (!timer) { - if (loop) { - credits_index = 0; - loop = false; - } - - text = credits[credits_index].text; - timer = credits[credits_index].delay; - start_pos = credits[credits_index].start_pos; - - if (timer < 0) { - timer = 240; - loop = true; - } else - timer += 16; - - render_line = 0; - credits_index++; - } else - timer--; - - if (render_line < 16) { - for (const auto c : text) { - const auto glyph = style().font.glyph(c); - - const size_t start = (glyph.size().width() / 8) * render_line; - for (Dim c = 0; c < glyph.size().width(); c++) { - const auto pixel = glyph.pixels()[start + (c >> 3)] & (1U << (c & 0x7)); - pixel_row[start_pos + i + c] = pixel ? Color::white() : Color::black(); - } - - const auto advance = glyph.advance(); - i += advance.x(); - } - render_line++; - } - - credits_display.new_row(pixel_row); + size_t i = 0; + std::array pixel_row; + + slow_down++; + if (slow_down % 3 < 2) return; + + if (!timer) { + if (loop) { + credits_index = 0; + loop = false; + } + + text = credits[credits_index].text; + timer = credits[credits_index].delay; + start_pos = credits[credits_index].start_pos; + + if (timer < 0) { + timer = 240; + loop = true; + } else + timer += 16; + + render_line = 0; + credits_index++; + } else + timer--; + + if (render_line < 16) { + for (const auto c : text) { + const auto glyph = style().font.glyph(c); + + const size_t start = (glyph.size().width() / 8) * render_line; + for (Dim c = 0; c < glyph.size().width(); c++) { + const auto pixel = glyph.pixels()[start + (c >> 3)] & (1U << (c & 0x7)); + pixel_row[start_pos + i + c] = pixel ? Color::white() : Color::black(); + } + + const auto advance = glyph.advance(); + i += advance.x(); + } + render_line++; + } + + credits_display.new_row(pixel_row); } AboutView::AboutView( - NavigationView& nav -) { - add_children({ - &credits_display, - &button_ok - }); - - button_ok.on_select = [&nav](Button&){ - nav.pop(); - }; + NavigationView& nav) { + add_children({&credits_display, + &button_ok}); + + button_ok.on_select = [&nav](Button&) { + nav.pop(); + }; } void AboutView::focus() { - button_ok.focus(); + button_ok.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_about.hpp b/firmware/application/apps/ui_about.hpp index 82c1dfbba..874f16774 100644 --- a/firmware/application/apps/ui_about.hpp +++ b/firmware/application/apps/ui_about.hpp @@ -32,94 +32,89 @@ namespace ui { class CreditsWidget : public Widget { -public: - CreditsWidget(Rect parent_rect); - - void on_show() override; - void on_hide() override; - - void paint(Painter&) override; - - void new_row(const std::array& pixel_row); - -private: - void clear(); + public: + CreditsWidget(Rect parent_rect); + + void on_show() override; + void on_hide() override; + + void paint(Painter&) override; + + void new_row(const std::array& pixel_row); + + private: + void clear(); }; class AboutView : public View { -public: - AboutView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "About"; }; - -private: - void update(); - - uint8_t credits_index { 0 }; - uint8_t render_line { 0 }; - Coord start_pos { 0 }; - uint8_t slow_down { 0 }; - int32_t timer { 0 }; - bool loop { false }; - - std::string text { }; - - typedef struct credits_t { - size_t start_pos; - std::string text; - int32_t delay; - } credits_t; - - // TODO: Make this dinamically centered and parse \n as the delay value so it is easy to maintain - const credits_t credits[26] = { - // 012345678901234567890123456789 - { 60, "PortaPack Mayhem", 0 }, - { 60, "PortaPack|HAVOC", 0 }, - { 11 * 8, "Gurus J. Boone", 0 }, - { 18 * 8, "M. Ossmann", 16 }, - { 11 * 8, "HAVOC Furrtek", 16 }, - { 7 * 8, "POCSAG rx T. Sailer", 0 }, - { 18 * 8, "E. Oenal", 16 }, - { 0 * 8, "Radiosonde infos F4GMU", 0 }, - { 18 * 8, "RS1729", 16 }, - { 4 * 8, "RDS waveform C. Jacquet", 16 }, - { 7 * 8, "Xy. infos cLx", 16 }, - { 2 * 8, "OOK scan trick Samy Kamkar", 16 }, - { 7 * 8, "World map NASA", 16 }, - { 0 * 8, "TouchTunes infos Notpike", 16 }, - { 4 * 8, "Subaru infos Tom", 0 }, - { 18 * 8, "Wimmenhove", 16 }, - { 1 * 8, "GPS,TV,BTLE,NRF Shao", 24 }, - { 6 * 8, "Thanks & donators", 16 }, - { 1 * 8, "Rainer Matla Keld Norman", 0 }, - { 1 * 8, " Giorgio C. DC1RDB", 0 }, - { 1 * 8, " Sigmounte Waax", 0 }, - { 1 * 8, " Windyoona Channels", 0 }, - { 1 * 8, " F4GEV Pyr3x", 0 }, - { 1 * 8, " HB3YOE", 24 }, - { 11 * 8, "MMXVIII", -1 } - }; - - CreditsWidget credits_display { - { 0, 16, 240, 240 } - }; - - Button button_ok { - { 72, 272, 96, 24 }, - "OK" - }; - - MessageHandlerRegistration message_handler_update { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->update(); - } - }; - + public: + AboutView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "About"; }; + + private: + void update(); + + uint8_t credits_index{0}; + uint8_t render_line{0}; + Coord start_pos{0}; + uint8_t slow_down{0}; + int32_t timer{0}; + bool loop{false}; + + std::string text{}; + + typedef struct credits_t { + size_t start_pos; + std::string text; + int32_t delay; + } credits_t; + + // TODO: Make this dinamically centered and parse \n as the delay value so it is easy to maintain + const credits_t credits[26] = { + // 012345678901234567890123456789 + {60, "PortaPack Mayhem", 0}, + {60, "PortaPack|HAVOC", 0}, + {11 * 8, "Gurus J. Boone", 0}, + {18 * 8, "M. Ossmann", 16}, + {11 * 8, "HAVOC Furrtek", 16}, + {7 * 8, "POCSAG rx T. Sailer", 0}, + {18 * 8, "E. Oenal", 16}, + {0 * 8, "Radiosonde infos F4GMU", 0}, + {18 * 8, "RS1729", 16}, + {4 * 8, "RDS waveform C. Jacquet", 16}, + {7 * 8, "Xy. infos cLx", 16}, + {2 * 8, "OOK scan trick Samy Kamkar", 16}, + {7 * 8, "World map NASA", 16}, + {0 * 8, "TouchTunes infos Notpike", 16}, + {4 * 8, "Subaru infos Tom", 0}, + {18 * 8, "Wimmenhove", 16}, + {1 * 8, "GPS,TV,BTLE,NRF Shao", 24}, + {6 * 8, "Thanks & donators", 16}, + {1 * 8, "Rainer Matla Keld Norman", 0}, + {1 * 8, " Giorgio C. DC1RDB", 0}, + {1 * 8, " Sigmounte Waax", 0}, + {1 * 8, " Windyoona Channels", 0}, + {1 * 8, " F4GEV Pyr3x", 0}, + {1 * 8, " HB3YOE", 24}, + {11 * 8, "MMXVIII", -1}}; + + CreditsWidget credits_display{ + {0, 16, 240, 240}}; + + Button button_ok{ + {72, 272, 96, 24}, + "OK"}; + + MessageHandlerRegistration message_handler_update{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->update(); + }}; }; } /* namespace ui */ -#endif/*__UI_ABOUT_H__*/ +#endif /*__UI_ABOUT_H__*/ diff --git a/firmware/application/apps/ui_about_demo.cpp b/firmware/application/apps/ui_about_demo.cpp index d66929bd2..9fb9326d2 100644 --- a/firmware/application/apps/ui_about_demo.cpp +++ b/firmware/application/apps/ui_about_demo.cpp @@ -45,374 +45,370 @@ using namespace lpc43xx; using namespace portapack; namespace ui { - + void AboutView::on_show() { - transmitter_model.set_tuning_frequency(1337000000); // TODO: Change - transmitter_model.set_baseband_configuration({ - .mode = 0, - .sampling_rate = 1536000, - .decimation_factor = 1, - }); - transmitter_model.set_rf_amp(true); - transmitter_model.set_lna(40); - transmitter_model.set_vga(40); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_audiotx_data(32, 50, false, 0); - - //audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max); + transmitter_model.set_tuning_frequency(1337000000); // TODO: Change + transmitter_model.set_baseband_configuration({ + .mode = 0, + .sampling_rate = 1536000, + .decimation_factor = 1, + }); + transmitter_model.set_rf_amp(true); + transmitter_model.set_lna(40); + transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_audiotx_data(32, 50, false, 0); + + // audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max); } void AboutView::render_video() { - uint8_t p, r, luma, chroma, cy; - ui::Color cc; - char ch; - float s; - - // Send framebuffer to LCD. Gotta go fast ! - display.render_box({30, 112}, {180, 72}, framebuffer); - - // Clear framebuffer to black - memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color)); - - // Drum hit palette animation - if (drum > 1) drum--; - - // Render copper bars from Y buffer - for (p = 0; p < 72; p++) { - luma = copperbuffer[p] & 0x0F; // 0 is transparent - if (luma) { - chroma = copperbuffer[p]>>4; - cc = ui::Color(std::min((coppercolor[chroma][0]/luma)*drum,255), std::min((coppercolor[chroma][1]/luma)*drum,255), std::min((coppercolor[chroma][2]/luma)*drum,255)); - for (r = 0; r < 180; r++) - framebuffer[(p*180)+r] = cc; - } - } - - // Scroll in/out state machine - if (anim_state == 0) { - // Scroll in - if (ofy < 8) { - ofy++; - anim_state = 0; - } else { - anim_state = 1; - } - if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) { - ofx += 8; - anim_state = 0; - } - } else if (anim_state == 1) { - // Just wait - if (credits_timer == (30*3)) { - credits_timer = 0; - anim_state = 2; - } else { - credits_timer++; - } - } else { - // Scroll out - if (credits[credits_index].change == true) { - if (ofy > -24) { - ofy--; - anim_state = 2; - } else { - anim_state = 0; - } - } else { - anim_state = 0; - } - if (ofx < 180) { - ofx += 8; - anim_state = 2; - } - - // Switch to next text - if (anim_state == 0) { - if (credits_index == 9) - credits_index = 0; - else - credits_index++; - ofx = -(strlen(credits[credits_index].name) * 16) - 16; - } - } - - // Sine text ("role") - p = 0; - while ((ch = credits[credits_index].role[p])) { - draw_demoglyph({(ui::Coord)(8+(p*16)), (ui::Coord)(ofy+(sine_table_f32[((p*16)+(phase>>5))&0xFF] * 8))}, ch, paletteA); - p++; - } - - // Scroll text (name) - p = 0; - while ((ch = credits[credits_index].name[p])) { - draw_demoglyph({(ui::Coord)(ofx+(p*16)), 56}, ch, paletteB); - p++; - } - - // Clear bars Y buffer - memset(copperbuffer, 0, 72); - - // Render bars to Y buffer - for (p = 0; p < 5; p++) { - cy = copperbars[p]; - for (r = 0; r < 16; r++) - copperbuffer[cy+r] = copperluma[r] + (p<<4); - } - - // Animate bars positions - for (p = 0; p < 5; p++) { - s = sine_table_f32[((p*32)+(phase/24))&0xFF]; - s += sine_table_f32[((p*16)+(phase/35))&0xFF]; - copperbars[p] = 28+(uint8_t)(s * 14); - } - - phase += 128; + uint8_t p, r, luma, chroma, cy; + ui::Color cc; + char ch; + float s; + + // Send framebuffer to LCD. Gotta go fast ! + display.render_box({30, 112}, {180, 72}, framebuffer); + + // Clear framebuffer to black + memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color)); + + // Drum hit palette animation + if (drum > 1) drum--; + + // Render copper bars from Y buffer + for (p = 0; p < 72; p++) { + luma = copperbuffer[p] & 0x0F; // 0 is transparent + if (luma) { + chroma = copperbuffer[p] >> 4; + cc = ui::Color(std::min((coppercolor[chroma][0] / luma) * drum, 255), std::min((coppercolor[chroma][1] / luma) * drum, 255), std::min((coppercolor[chroma][2] / luma) * drum, 255)); + for (r = 0; r < 180; r++) + framebuffer[(p * 180) + r] = cc; + } + } + + // Scroll in/out state machine + if (anim_state == 0) { + // Scroll in + if (ofy < 8) { + ofy++; + anim_state = 0; + } else { + anim_state = 1; + } + if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) { + ofx += 8; + anim_state = 0; + } + } else if (anim_state == 1) { + // Just wait + if (credits_timer == (30 * 3)) { + credits_timer = 0; + anim_state = 2; + } else { + credits_timer++; + } + } else { + // Scroll out + if (credits[credits_index].change == true) { + if (ofy > -24) { + ofy--; + anim_state = 2; + } else { + anim_state = 0; + } + } else { + anim_state = 0; + } + if (ofx < 180) { + ofx += 8; + anim_state = 2; + } + + // Switch to next text + if (anim_state == 0) { + if (credits_index == 9) + credits_index = 0; + else + credits_index++; + ofx = -(strlen(credits[credits_index].name) * 16) - 16; + } + } + + // Sine text ("role") + p = 0; + while ((ch = credits[credits_index].role[p])) { + draw_demoglyph({(ui::Coord)(8 + (p * 16)), (ui::Coord)(ofy + (sine_table_f32[((p * 16) + (phase >> 5)) & 0xFF] * 8))}, ch, paletteA); + p++; + } + + // Scroll text (name) + p = 0; + while ((ch = credits[credits_index].name[p])) { + draw_demoglyph({(ui::Coord)(ofx + (p * 16)), 56}, ch, paletteB); + p++; + } + + // Clear bars Y buffer + memset(copperbuffer, 0, 72); + + // Render bars to Y buffer + for (p = 0; p < 5; p++) { + cy = copperbars[p]; + for (r = 0; r < 16; r++) + copperbuffer[cy + r] = copperluma[r] + (p << 4); + } + + // Animate bars positions + for (p = 0; p < 5; p++) { + s = sine_table_f32[((p * 32) + (phase / 24)) & 0xFF]; + s += sine_table_f32[((p * 16) + (phase / 35)) & 0xFF]; + copperbars[p] = 28 + (uint8_t)(s * 14); + } + + phase += 128; } -void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color * pal) { - uint8_t x, y, c, cl, cr; - uint16_t che; - int16_t lbx, il; - - // Map ASCII to font bitmap - if ((ch >= 32) || (ch < 96)) - che = char_map[ch-32]; - else - che = 0xFF; - - if (che < 0xFF) { - che = (che * 128) + 48; // Start in bitmap - - il = (180 * p.y) + p.x; // Start il framebuffer - - for (y = 0; y < 16; y++) { - if (p.y + y >= 72) break; // Over bottom of framebuffer, abort - if (p.y + y >= 0) { - for (x = 0; x < 8; x++) { - c = demofont_bin[x+(y*8)+che]; // Split byte in 2 4BPP pixels - cl = c >> 4; - cr = c & 0x0F; - lbx = p.x + (x*2); - if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl]; - lbx++; - il++; - if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr]; - il++; - } - il += 180-16; - } else { - il += 180; - } - } - } +void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color* pal) { + uint8_t x, y, c, cl, cr; + uint16_t che; + int16_t lbx, il; + + // Map ASCII to font bitmap + if ((ch >= 32) || (ch < 96)) + che = char_map[ch - 32]; + else + che = 0xFF; + + if (che < 0xFF) { + che = (che * 128) + 48; // Start in bitmap + + il = (180 * p.y) + p.x; // Start il framebuffer + + for (y = 0; y < 16; y++) { + if (p.y + y >= 72) break; // Over bottom of framebuffer, abort + if (p.y + y >= 0) { + for (x = 0; x < 8; x++) { + c = demofont_bin[x + (y * 8) + che]; // Split byte in 2 4BPP pixels + cl = c >> 4; + cr = c & 0x0F; + lbx = p.x + (x * 2); + if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl]; + lbx++; + il++; + if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr]; + il++; + } + il += 180 - 16; + } else { + il += 180; + } + } + } } void AboutView::render_audio() { - uint8_t i, ymdata; - uint16_t ym_render_cnt; - - // This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet - - // Render 1024 music samples - for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) { - - // Update registers at 48000/960 = 50Hz - if (ym_sample_cnt == 0) { - // "Decompress" on the fly and update YM registers - for (i = 0; i < 14; i++) { - if (!ym_regs[i].cnt) { - // New run - ymdata = ymdata_bin[ym_regs[i].ptr++]; - ym_regs[i].cnt = ymdata & 0x7F; - if (ymdata & 0x80) { - ym_regs[i].same = true; - ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++]; - } else { - ym_regs[i].same = false; - } - // Detect drum on channel B - if (i == 3) - if (ym_regs[3].value > 2) drum = 4; - } - if (ym_regs[i].same == false) { - ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++]; - if (i == 13) { - // Update envelope attributes - ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00; - if (!(ym_regs[13].value & 8)) { - ym_env_hold = 1; - ym_env_alt = ym_env_att; - } else { - ym_env_hold = ym_regs[13].value & 1; - ym_env_alt = ym_regs[13].value & 2; - } - // Reset envelope counter - ym_env_step = 0x1F; - ym_env_holding = 0; - ym_env_vol = (ym_env_step ^ ym_env_att); - } - } - ym_regs[i].cnt--; - } - ym_frame++; - } - - // Square wave oscillators - // 2457600/16/48000 = 3.2, but 4 sounds better than 3... - for (i = 0; i < 3; i++) { - ym_osc_cnt[i] += 4; - if (ym_osc_cnt[i] >= (ym_regs[i*2].value | ((ym_regs[(i*2)+1].value & 0x0f) << 8))) { - ym_osc_cnt[i] = 0; - ym_osc_out[i] ^= 1; - } - } - - // Noise generator - ym_noise_cnt += 4; - if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) { - ym_noise_cnt = 0; - ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17); - ym_rng >>= 1; - } - - // Mix tones and noise - for (i = 0; i < 3; i++) - ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1)); - - // Envelope generator - if (!ym_env_holding) { - ym_env_cnt += 8; - if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value<<8))) { - ym_env_cnt = 0; - ym_env_step--; - if (ym_env_step < 0) { - if (ym_env_hold) { - if (ym_env_alt) - ym_env_att ^= 0x1F; - ym_env_holding = 1; - ym_env_step = 0; - } else { - if (ym_env_alt && (ym_env_step & 0x20)) - ym_env_att ^= 0x1F; - ym_env_step &= 0x1F; - } - } - } - } - ym_env_vol = (ym_env_step ^ ym_env_att); - - ym_out = 0; - for (i = 0; i < 3; i++) { - if (ym_regs[i + 8].value & 0x10) { - // Envelope mode - ym_out += (ym_ch[i] ? ym_env_vol : 0); - } else { - // Fixed mode - ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0); - } - } - - ym_buffer[ym_render_cnt] = (ym_out * 2) - 45; - - if (ym_sample_cnt < 960) { - ym_sample_cnt++; - } else { - ym_sample_cnt = 0; - } - - // Loop - if (ym_frame == ym_frames) ym_init(); - } + uint8_t i, ymdata; + uint16_t ym_render_cnt; + + // This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet + + // Render 1024 music samples + for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) { + // Update registers at 48000/960 = 50Hz + if (ym_sample_cnt == 0) { + // "Decompress" on the fly and update YM registers + for (i = 0; i < 14; i++) { + if (!ym_regs[i].cnt) { + // New run + ymdata = ymdata_bin[ym_regs[i].ptr++]; + ym_regs[i].cnt = ymdata & 0x7F; + if (ymdata & 0x80) { + ym_regs[i].same = true; + ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++]; + } else { + ym_regs[i].same = false; + } + // Detect drum on channel B + if (i == 3) + if (ym_regs[3].value > 2) drum = 4; + } + if (ym_regs[i].same == false) { + ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++]; + if (i == 13) { + // Update envelope attributes + ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00; + if (!(ym_regs[13].value & 8)) { + ym_env_hold = 1; + ym_env_alt = ym_env_att; + } else { + ym_env_hold = ym_regs[13].value & 1; + ym_env_alt = ym_regs[13].value & 2; + } + // Reset envelope counter + ym_env_step = 0x1F; + ym_env_holding = 0; + ym_env_vol = (ym_env_step ^ ym_env_att); + } + } + ym_regs[i].cnt--; + } + ym_frame++; + } + + // Square wave oscillators + // 2457600/16/48000 = 3.2, but 4 sounds better than 3... + for (i = 0; i < 3; i++) { + ym_osc_cnt[i] += 4; + if (ym_osc_cnt[i] >= (ym_regs[i * 2].value | ((ym_regs[(i * 2) + 1].value & 0x0f) << 8))) { + ym_osc_cnt[i] = 0; + ym_osc_out[i] ^= 1; + } + } + + // Noise generator + ym_noise_cnt += 4; + if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) { + ym_noise_cnt = 0; + ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17); + ym_rng >>= 1; + } + + // Mix tones and noise + for (i = 0; i < 3; i++) + ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1)); + + // Envelope generator + if (!ym_env_holding) { + ym_env_cnt += 8; + if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value << 8))) { + ym_env_cnt = 0; + ym_env_step--; + if (ym_env_step < 0) { + if (ym_env_hold) { + if (ym_env_alt) + ym_env_att ^= 0x1F; + ym_env_holding = 1; + ym_env_step = 0; + } else { + if (ym_env_alt && (ym_env_step & 0x20)) + ym_env_att ^= 0x1F; + ym_env_step &= 0x1F; + } + } + } + } + ym_env_vol = (ym_env_step ^ ym_env_att); + + ym_out = 0; + for (i = 0; i < 3; i++) { + if (ym_regs[i + 8].value & 0x10) { + // Envelope mode + ym_out += (ym_ch[i] ? ym_env_vol : 0); + } else { + // Fixed mode + ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0); + } + } + + ym_buffer[ym_render_cnt] = (ym_out * 2) - 45; + + if (ym_sample_cnt < 960) { + ym_sample_cnt++; + } else { + ym_sample_cnt = 0; + } + + // Loop + if (ym_frame == ym_frames) ym_init(); + } } void AboutView::update() { - if (framebuffer) { - // Update 1 out of 2 frames, 60Hz is very laggy - if (refresh_cnt & 1) render_video(); - refresh_cnt++; - } - - // Slowly increase volume to avoid jumpscare - if (headphone_vol < (70 << 2)) { - audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max); - headphone_vol++; - } + if (framebuffer) { + // Update 1 out of 2 frames, 60Hz is very laggy + if (refresh_cnt & 1) render_video(); + refresh_cnt++; + } + + // Slowly increase volume to avoid jumpscare + if (headphone_vol < (70 << 2)) { + audio::headphone::set_volume(volume_t::decibel((headphone_vol / 4) - 99) + audio::headphone::volume_range().max); + headphone_vol++; + } } void AboutView::ym_init() { - uint8_t reg; - - for (reg = 0; reg < 14; reg++) { - ym_regs[reg].cnt = 0; - // Pick up start pointers for each YM registers RLE blocks - ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg*2)+3])<<8) + ymdata_bin[(reg*2)+2]; - ym_regs[reg].same = false; // Useless ? - ym_regs[reg].value = 0; // Useless ? - } - - ym_frame = 0; + uint8_t reg; + + for (reg = 0; reg < 14; reg++) { + ym_regs[reg].cnt = 0; + // Pick up start pointers for each YM registers RLE blocks + ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg * 2) + 3]) << 8) + ymdata_bin[(reg * 2) + 2]; + ym_regs[reg].same = false; // Useless ? + ym_regs[reg].value = 0; // Useless ? + } + + ym_frame = 0; } AboutView::AboutView( - NavigationView& nav -) -{ - uint8_t p, c; - - baseband::run_image(portapack::spi_flash::image_tag_audio_tx); - - add_children({ { - &text_title, - &text_firmware, - &text_cpld_hackrf, - &text_cpld_hackrf_status, - &button_ok, - } }); - - if( cpld_hackrf_verify_eeprom() ) { - text_cpld_hackrf_status.set(" OK"); - } else { - text_cpld_hackrf_status.set("BAD"); - } - - // Politely ask for about 26kB - framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color)); - - if (framebuffer) { - memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color)); - - // Copy original font palette - c = 0; - for (p = 0; p < 48; p+=3) - paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p+1], demofont_bin[p+2]); - - // Increase red in another one - c = 0; - for (p = 0; p < 48; p+=3) - paletteB[c++] = ui::Color(std::min(demofont_bin[p]+64, 255), demofont_bin[p+1], demofont_bin[p+2]); - } - - // Init YM synth - ym_frames = ((uint16_t)(ymdata_bin[1])<<8) + ymdata_bin[0]; - ym_init(); - - - button_ok.on_select = [this,&nav](Button&){ - if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this - nav.pop(); - }; + NavigationView& nav) { + uint8_t p, c; + + baseband::run_image(portapack::spi_flash::image_tag_audio_tx); + + add_children({{ + &text_title, + &text_firmware, + &text_cpld_hackrf, + &text_cpld_hackrf_status, + &button_ok, + }}); + + if (cpld_hackrf_verify_eeprom()) { + text_cpld_hackrf_status.set(" OK"); + } else { + text_cpld_hackrf_status.set("BAD"); + } + + // Politely ask for about 26kB + framebuffer = (ui::Color*)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color)); + + if (framebuffer) { + memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color)); + + // Copy original font palette + c = 0; + for (p = 0; p < 48; p += 3) + paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p + 1], demofont_bin[p + 2]); + + // Increase red in another one + c = 0; + for (p = 0; p < 48; p += 3) + paletteB[c++] = ui::Color(std::min(demofont_bin[p] + 64, 255), demofont_bin[p + 1], demofont_bin[p + 2]); + } + + // Init YM synth + ym_frames = ((uint16_t)(ymdata_bin[1]) << 8) + ymdata_bin[0]; + ym_init(); + + button_ok.on_select = [this, &nav](Button&) { + if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this + nav.pop(); + }; } AboutView::~AboutView() { - transmitter_model.disable(); - baseband::shutdown(); + transmitter_model.disable(); + baseband::shutdown(); } void AboutView::focus() { - button_ok.focus(); + button_ok.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_about_demo.hpp b/firmware/application/apps/ui_about_demo.hpp index 1292b789a..37342c53d 100644 --- a/firmware/application/apps/ui_about_demo.hpp +++ b/firmware/application/apps/ui_about_demo.hpp @@ -34,141 +34,135 @@ namespace ui { class AboutView : public View { -public: - AboutView(NavigationView& nav); - ~AboutView(); - - void on_show() override; - void focus() override; - -private: - void ym_init(); - void update(); - void render_video(); - void render_audio(); - void draw_demoglyph(ui::Point p, char ch, ui::Color * pal); - uint16_t debug_cnt = 0; - - typedef struct ymreg_t { - uint8_t value; - uint8_t cnt; - uint16_t ptr; - bool same; - } ymreg_t; - - uint16_t headphone_vol = 5 << 2; - - ymreg_t ym_regs[14]; - uint16_t ym_frames; - uint16_t ym_frame; - uint8_t drum = 0; - uint16_t ym_osc_cnt[3]; - uint32_t ym_rng = 1; - uint16_t ym_noise_cnt; - uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol; - int8_t ym_env_step; - uint16_t ym_env_cnt; - uint8_t ym_osc_out[3]; - uint8_t ym_ch[3]; - uint8_t ym_out; - uint16_t ym_sample_cnt = 0; - - int8_t ym_buffer[1024]; - - uint8_t refresh_cnt; - ui::Color paletteA[16]; - ui::Color paletteB[16]; - ui::Color * framebuffer; - uint32_t phase = 0; - uint8_t copperbars[5] = { 0 }; - uint8_t copperbuffer[72] = { 0 }; - - uint8_t anim_state = 0; - uint8_t credits_index = 0; - uint16_t credits_timer = 0; - - int16_t ofx = -180, ofy = -24; - - const uint8_t char_map[64] = { 0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45, - 58, 59, 0xFF, 43, 40, 57, 26, 42, - 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44, - 0xFF, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 }; - const uint8_t coppercolor[5][3] = { { 255,0,0 }, - { 0,255,0 }, - { 0,0,255 }, - { 255,0,255 }, - { 255,255,0 } }; - - typedef struct credits_t { - char role[12]; - char name[12]; - bool change; - } credits_t; - - // 0123456789A 0123456789A - const credits_t credits[10] = { {"GURUS", "J. BOONE", false}, - {"GURUS", "M. OSSMANN", true}, - {"BUGS", "FURRTEK", true}, - {"RDS WAVE", "C. JACQUET", true}, - {"POCSAG RX", "T. SAILER", false}, - {"POCSAG RX", "E. OENAL", true}, - {"XYLOS DATA", "CLX", true}, - {"GREETS TO", "SIGMOUNTE", false}, - {"GREETS TO", "WINDYOONA", true}, - {"THIS MUSIC", "BIG ALEC", true} - }; - - Text text_title { - { 100, 32, 40, 16 }, - "About", - }; - - Text text_firmware { - { 0, 236, 240, 16 }, - "Version " VERSION_STRING, - }; - - Text text_cpld_hackrf { - { 0, 252, 11*8, 16 }, - "HackRF CPLD", - }; - - Text text_cpld_hackrf_status { - { 240 - 3*8, 252, 3*8, 16 }, - "???" - }; - - Button button_ok { - { 72, 272, 96, 24 }, - "OK" - }; - - MessageHandlerRegistration message_handler_update { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->update(); - } - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::FIFOSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signaltype == 1) { - this->render_audio(); - baseband::set_fifo_data(ym_buffer); - } - } - }; - + public: + AboutView(NavigationView& nav); + ~AboutView(); + + void on_show() override; + void focus() override; + + private: + void ym_init(); + void update(); + void render_video(); + void render_audio(); + void draw_demoglyph(ui::Point p, char ch, ui::Color* pal); + uint16_t debug_cnt = 0; + + typedef struct ymreg_t { + uint8_t value; + uint8_t cnt; + uint16_t ptr; + bool same; + } ymreg_t; + + uint16_t headphone_vol = 5 << 2; + + ymreg_t ym_regs[14]; + uint16_t ym_frames; + uint16_t ym_frame; + uint8_t drum = 0; + uint16_t ym_osc_cnt[3]; + uint32_t ym_rng = 1; + uint16_t ym_noise_cnt; + uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol; + int8_t ym_env_step; + uint16_t ym_env_cnt; + uint8_t ym_osc_out[3]; + uint8_t ym_ch[3]; + uint8_t ym_out; + uint16_t ym_sample_cnt = 0; + + int8_t ym_buffer[1024]; + + uint8_t refresh_cnt; + ui::Color paletteA[16]; + ui::Color paletteB[16]; + ui::Color* framebuffer; + uint32_t phase = 0; + uint8_t copperbars[5] = {0}; + uint8_t copperbuffer[72] = {0}; + + uint8_t anim_state = 0; + uint8_t credits_index = 0; + uint16_t credits_timer = 0; + + int16_t ofx = -180, ofy = -24; + + const uint8_t char_map[64] = {0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45, + 58, 59, 0xFF, 43, 40, 57, 26, 42, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44, + 0xFF, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + const uint8_t copperluma[16] = {8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8}; + const uint8_t coppercolor[5][3] = {{255, 0, 0}, + {0, 255, 0}, + {0, 0, 255}, + {255, 0, 255}, + {255, 255, 0}}; + + typedef struct credits_t { + char role[12]; + char name[12]; + bool change; + } credits_t; + + // 0123456789A 0123456789A + const credits_t credits[10] = {{"GURUS", "J. BOONE", false}, + {"GURUS", "M. OSSMANN", true}, + {"BUGS", "FURRTEK", true}, + {"RDS WAVE", "C. JACQUET", true}, + {"POCSAG RX", "T. SAILER", false}, + {"POCSAG RX", "E. OENAL", true}, + {"XYLOS DATA", "CLX", true}, + {"GREETS TO", "SIGMOUNTE", false}, + {"GREETS TO", "WINDYOONA", true}, + {"THIS MUSIC", "BIG ALEC", true}}; + + Text text_title{ + {100, 32, 40, 16}, + "About", + }; + + Text text_firmware{ + {0, 236, 240, 16}, + "Version " VERSION_STRING, + }; + + Text text_cpld_hackrf{ + {0, 252, 11 * 8, 16}, + "HackRF CPLD", + }; + + Text text_cpld_hackrf_status{ + {240 - 3 * 8, 252, 3 * 8, 16}, + "???"}; + + Button button_ok{ + {72, 272, 96, 24}, + "OK"}; + + MessageHandlerRegistration message_handler_update{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->update(); + }}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::FIFOSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signaltype == 1) { + this->render_audio(); + baseband::set_fifo_data(ym_buffer); + } + }}; }; } /* namespace ui */ -#endif/*__UI_ABOUT_H__*/ +#endif /*__UI_ABOUT_H__*/ diff --git a/firmware/application/apps/ui_about_simple.cpp b/firmware/application/apps/ui_about_simple.cpp index 58b81a9b3..84a972c0f 100644 --- a/firmware/application/apps/ui_about_simple.cpp +++ b/firmware/application/apps/ui_about_simple.cpp @@ -1,28 +1,22 @@ #include "ui_about_simple.hpp" -namespace ui -{ - AboutView::AboutView(NavigationView &nav) - { - add_children({&console, &button_ok}); +namespace ui { +AboutView::AboutView(NavigationView& nav) { + add_children({&console, &button_ok}); - button_ok.on_select = [&nav](Button &) - { - nav.pop(); - }; + button_ok.on_select = [&nav](Button&) { + nav.pop(); + }; - console.writeln("\x1B\x07List of contributors:\x1B\x10"); - console.writeln(""); - } + console.writeln("\x1B\x07List of contributors:\x1B\x10"); + console.writeln(""); +} - void AboutView::update() - { - if (++timer > 200) - { - timer = 0; +void AboutView::update() { + if (++timer > 200) { + timer = 0; - switch (++frame) - { + switch (++frame) { case 1: // TODO: Generate this automatically from github // https://github.com/eried/portapack-mayhem/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c @@ -71,15 +65,14 @@ namespace ui console.writeln("yhetti,ckuethe,smunaut"); console.writeln("wishi,mrbubble62,scateu..."); console.writeln(""); - frame = 0; // Loop + frame = 0; // Loop break; - } } } +} - void AboutView::focus() - { - button_ok.focus(); - } +void AboutView::focus() { + button_ok.focus(); +} } /* namespace ui */ diff --git a/firmware/application/apps/ui_about_simple.hpp b/firmware/application/apps/ui_about_simple.hpp index a05901190..b10308d97 100644 --- a/firmware/application/apps/ui_about_simple.hpp +++ b/firmware/application/apps/ui_about_simple.hpp @@ -7,34 +7,32 @@ #include -namespace ui -{ - class AboutView : public View - { - public: - AboutView(NavigationView &nav); - void focus() override; - std::string title() const override { return "About"; }; - int32_t timer{180}; - short frame{0}; - - private: - void update(); - - Console console{ - {0, 10, 240, 240}}; - - Button button_ok{ - {240/3, 270, 240/3, 24}, - "OK", - }; - - MessageHandlerRegistration message_handler_update{ - Message::ID::DisplayFrameSync, - [this](const Message *const) { - this->update(); - }}; +namespace ui { +class AboutView : public View { + public: + AboutView(NavigationView& nav); + void focus() override; + std::string title() const override { return "About"; }; + int32_t timer{180}; + short frame{0}; + + private: + void update(); + + Console console{ + {0, 10, 240, 240}}; + + Button button_ok{ + {240 / 3, 270, 240 / 3, 24}, + "OK", }; -} // namespace ui + + MessageHandlerRegistration message_handler_update{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->update(); + }}; +}; +} // namespace ui #endif /*__UI_ABOUT_SIMPLE_H__*/ diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 7b1c6bbfa..516ec637d 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -34,530 +34,511 @@ using namespace portapack; namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - char aged_color; - Color target_color; - auto entry_age = entry.age; - - // Color decay for flights not being updated anymore - if (entry_age < ADSB_CURRENT) { - aged_color = 0x10; - target_color = Color::green(); - } else if (entry_age < ADSB_RECENT) { - aged_color = 0x07; - target_color = Color::light_grey(); - } else { - aged_color = 0x08; - target_color = Color::dark_grey(); - } - - std::string entry_string = "\x1B"; - entry_string += aged_color; - entry_string += - (entry.callsign[0]!=' ' ? entry.callsign + " " : entry.icaoStr + " ") + - to_string_dec_uint((unsigned int)(entry.pos.altitude/100),4) + - to_string_dec_uint((unsigned int)entry.velo.speed,4) + - to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " + - (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") + - to_string_dec_uint(entry.age, 4); - - painter.draw_string( - target_rect.location(), - style, - entry_string - ); - - if (entry.pos.valid) - painter.draw_bitmap(target_rect.location() + Point(8 * 8, 0), bitmap_target, target_color, style.background); + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + char aged_color; + Color target_color; + auto entry_age = entry.age; + + // Color decay for flights not being updated anymore + if (entry_age < ADSB_CURRENT) { + aged_color = 0x10; + target_color = Color::green(); + } else if (entry_age < ADSB_RECENT) { + aged_color = 0x07; + target_color = Color::light_grey(); + } else { + aged_color = 0x08; + target_color = Color::dark_grey(); + } + + std::string entry_string = "\x1B"; + entry_string += aged_color; + entry_string += + (entry.callsign[0] != ' ' ? entry.callsign + " " : entry.icaoStr + " ") + + to_string_dec_uint((unsigned int)(entry.pos.altitude / 100), 4) + + to_string_dec_uint((unsigned int)entry.velo.speed, 4) + + to_string_dec_uint((unsigned int)(entry.amp >> 9), 4) + " " + + (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") + + to_string_dec_uint(entry.age, 4); + + painter.draw_string( + target_rect.location(), + style, + entry_string); + + if (entry.pos.valid) + painter.draw_bitmap(target_rect.location() + Point(8 * 8, 0), bitmap_target, target_color, style.background); } void ADSBLogger::log_str(std::string& logline) { - rtc::RTC datetime; - rtcGetTime(&RTCD1, &datetime); - log_file.write_entry(datetime,logline); + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + log_file.write_entry(datetime, logline); } - // Aircraft Details void ADSBRxAircraftDetailsView::focus() { - button_close.focus(); + button_close.focus(); } ADSBRxAircraftDetailsView::~ADSBRxAircraftDetailsView() { - on_close_(); + on_close_(); } ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView( - NavigationView& nav, - const AircraftRecentEntry& entry, - const std::function on_close -) : entry_copy(entry), - on_close_(on_close) -{ - - add_children({ - &labels, - &text_icao_address, - &text_registration, - &text_manufacturer, - &text_model, - &text_type, - &text_number_of_engines, - &text_engine_type, - &text_owner, - &text_operator, - &button_close - }); - - std::unique_ptr logger { }; - - icao_code = to_string_hex(entry_copy.ICAO_address, 6); - text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); - - // Try getting the aircraft information from icao24.db - return_code = db.retrieve_aircraft_record(&aircraft_record, icao_code); - switch(return_code) { - case DATABASE_RECORD_FOUND: - text_registration.set(aircraft_record.aircraft_registration); - text_manufacturer.set(aircraft_record.aircraft_manufacturer); - text_model.set(aircraft_record.aircraft_model); - text_owner.set(aircraft_record.aircraft_owner); - text_operator.set(aircraft_record.aircraft_operator); - // Check for ICAO type, e.g. L2J - if(strlen(aircraft_record.icao_type) == 3) { - switch(aircraft_record.icao_type[0]) { - case 'L': - text_type.set("Landplane"); - break; - case 'S': - text_type.set("Seaplane"); - break; - case 'A': - text_type.set("Amphibian"); - break; - case 'H': - text_type.set("Helicopter"); - break; - case 'G': - text_type.set("Gyrocopter"); - break; - case 'T': - text_type.set("Tilt-wing aircraft"); - break; - } - text_number_of_engines.set(std::string(1, aircraft_record.icao_type[1])); - switch(aircraft_record.icao_type[2]) { - case 'P': - text_engine_type.set("Piston engine"); - break; - case 'T': - text_engine_type.set("Turboprop/Turboshaft engine"); - break; - case 'J': - text_engine_type.set("Jet engine"); - break; - case 'E': - text_engine_type.set("Electric engine"); - break; - } - - } - // Check for ICAO type designator - else if(strlen(aircraft_record.icao_type) == 4) { - if(strcmp(aircraft_record.icao_type,"SHIP") == 0) text_type.set("Airship"); - else if(strcmp(aircraft_record.icao_type,"BALL") == 0) text_type.set("Balloon"); - else if(strcmp(aircraft_record.icao_type,"GLID") == 0) text_type.set("Glider / sailplane"); - else if(strcmp(aircraft_record.icao_type,"ULAC") == 0) text_type.set("Micro/ultralight aircraft"); - else if(strcmp(aircraft_record.icao_type,"GYRO") == 0) text_type.set("Micro/ultralight autogyro"); - else if(strcmp(aircraft_record.icao_type,"UHEL") == 0) text_type.set("Micro/ultralight helicopter"); - else if(strcmp(aircraft_record.icao_type,"SHIP") == 0) text_type.set("Airship"); - else if(strcmp(aircraft_record.icao_type,"PARA") == 0) text_type.set("Powered parachute/paraplane"); - } - break; - case DATABASE_NOT_FOUND: - text_manufacturer.set("No icao24.db file"); - break; - } - button_close.on_select = [&nav](Button&){ - nav.pop(); - }; - + NavigationView& nav, + const AircraftRecentEntry& entry, + const std::function on_close) + : entry_copy(entry), + on_close_(on_close) { + add_children({&labels, + &text_icao_address, + &text_registration, + &text_manufacturer, + &text_model, + &text_type, + &text_number_of_engines, + &text_engine_type, + &text_owner, + &text_operator, + &button_close}); + + std::unique_ptr logger{}; + + icao_code = to_string_hex(entry_copy.ICAO_address, 6); + text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); + + // Try getting the aircraft information from icao24.db + return_code = db.retrieve_aircraft_record(&aircraft_record, icao_code); + switch (return_code) { + case DATABASE_RECORD_FOUND: + text_registration.set(aircraft_record.aircraft_registration); + text_manufacturer.set(aircraft_record.aircraft_manufacturer); + text_model.set(aircraft_record.aircraft_model); + text_owner.set(aircraft_record.aircraft_owner); + text_operator.set(aircraft_record.aircraft_operator); + // Check for ICAO type, e.g. L2J + if (strlen(aircraft_record.icao_type) == 3) { + switch (aircraft_record.icao_type[0]) { + case 'L': + text_type.set("Landplane"); + break; + case 'S': + text_type.set("Seaplane"); + break; + case 'A': + text_type.set("Amphibian"); + break; + case 'H': + text_type.set("Helicopter"); + break; + case 'G': + text_type.set("Gyrocopter"); + break; + case 'T': + text_type.set("Tilt-wing aircraft"); + break; + } + text_number_of_engines.set(std::string(1, aircraft_record.icao_type[1])); + switch (aircraft_record.icao_type[2]) { + case 'P': + text_engine_type.set("Piston engine"); + break; + case 'T': + text_engine_type.set("Turboprop/Turboshaft engine"); + break; + case 'J': + text_engine_type.set("Jet engine"); + break; + case 'E': + text_engine_type.set("Electric engine"); + break; + } + + } + // Check for ICAO type designator + else if (strlen(aircraft_record.icao_type) == 4) { + if (strcmp(aircraft_record.icao_type, "SHIP") == 0) + text_type.set("Airship"); + else if (strcmp(aircraft_record.icao_type, "BALL") == 0) + text_type.set("Balloon"); + else if (strcmp(aircraft_record.icao_type, "GLID") == 0) + text_type.set("Glider / sailplane"); + else if (strcmp(aircraft_record.icao_type, "ULAC") == 0) + text_type.set("Micro/ultralight aircraft"); + else if (strcmp(aircraft_record.icao_type, "GYRO") == 0) + text_type.set("Micro/ultralight autogyro"); + else if (strcmp(aircraft_record.icao_type, "UHEL") == 0) + text_type.set("Micro/ultralight helicopter"); + else if (strcmp(aircraft_record.icao_type, "SHIP") == 0) + text_type.set("Airship"); + else if (strcmp(aircraft_record.icao_type, "PARA") == 0) + text_type.set("Powered parachute/paraplane"); + } + break; + case DATABASE_NOT_FOUND: + text_manufacturer.set("No icao24.db file"); + break; + } + button_close.on_select = [&nav](Button&) { + nav.pop(); + }; }; // End of Aicraft details void ADSBRxDetailsView::focus() { - button_see_map.focus(); + button_see_map.focus(); } void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) { - entry_copy = entry; - uint32_t age = entry_copy.age; - - if (age < 60) - text_last_seen.set(to_string_dec_uint(age) + " seconds ago"); - else - text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago"); - - text_infos.set(entry_copy.info_string); - if(entry_copy.velo.heading < 360 && entry_copy.velo.speed >=0){ //I don't like this but... - text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading) + " Spd:" + to_string_dec_int(entry_copy.velo.speed)); - }else{ - text_info2.set(""); - } - text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14)); - text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); - - if (send_updates) - { - geomap_view->update_tag(trimr(entry.callsign[0]!=' ' ? entry.callsign : to_string_hex(entry.ICAO_address, 6))); - geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude); - } + entry_copy = entry; + uint32_t age = entry_copy.age; + + if (age < 60) + text_last_seen.set(to_string_dec_uint(age) + " seconds ago"); + else + text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago"); + + text_infos.set(entry_copy.info_string); + if (entry_copy.velo.heading < 360 && entry_copy.velo.speed >= 0) { // I don't like this but... + text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading) + " Spd:" + to_string_dec_int(entry_copy.velo.speed)); + } else { + text_info2.set(""); + } + text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14)); + text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); + + if (send_updates) { + geomap_view->update_tag(trimr(entry.callsign[0] != ' ' ? entry.callsign : to_string_hex(entry.ICAO_address, 6))); + geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude); + } } ADSBRxDetailsView::~ADSBRxDetailsView() { - on_close_(); + on_close_(); } ADSBRxDetailsView::ADSBRxDetailsView( - NavigationView& nav, - const AircraftRecentEntry& entry, - const std::function on_close -) : entry_copy(entry), - on_close_(on_close) -{ - - add_children({ - &labels, - &text_icao_address, - &text_callsign, - &text_last_seen, - &text_airline, - &text_country, - &text_infos, - &text_info2, - &text_frame_pos_even, - &text_frame_pos_odd, - &button_aircraft_details, - &button_see_map - }); - - std::unique_ptr logger { }; - update(entry_copy); - - // The following won't (shouldn't !) change for a given airborne aircraft - // Try getting the airline's name from airlines.db - airline_code = entry_copy.callsign.substr(0, 3); - return_code = db.retrieve_airline_record(&airline_record, airline_code); - switch(return_code) { - case DATABASE_RECORD_FOUND: - text_airline.set(airline_record.airline); - text_country.set(airline_record.country); - break; - case DATABASE_NOT_FOUND: - text_airline.set("No airlines.db file"); - break; - } - - - text_callsign.set(entry_copy.callsign); - text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); - - button_aircraft_details.on_select = [this, &nav](Button&) { - aircraft_details_view = nav.push(entry_copy, [this]() { send_updates = false;}); - send_updates = false; - }; - - button_see_map.on_select = [this, &nav](Button&) { - if (!send_updates) { // Prevent recursively launching the map - geomap_view = nav.push( - trimr(entry_copy.callsign[0]!=' ' ? entry_copy.callsign : entry_copy.icaoStr), - entry_copy.pos.altitude, - GeoPos::alt_unit::FEET, - entry_copy.pos.latitude, - entry_copy.pos.longitude, - entry_copy.velo.heading, - [this]() { - send_updates = false; - }); - send_updates = true; - } - }; + NavigationView& nav, + const AircraftRecentEntry& entry, + const std::function on_close) + : entry_copy(entry), + on_close_(on_close) { + add_children({&labels, + &text_icao_address, + &text_callsign, + &text_last_seen, + &text_airline, + &text_country, + &text_infos, + &text_info2, + &text_frame_pos_even, + &text_frame_pos_odd, + &button_aircraft_details, + &button_see_map}); + + std::unique_ptr logger{}; + update(entry_copy); + + // The following won't (shouldn't !) change for a given airborne aircraft + // Try getting the airline's name from airlines.db + airline_code = entry_copy.callsign.substr(0, 3); + return_code = db.retrieve_airline_record(&airline_record, airline_code); + switch (return_code) { + case DATABASE_RECORD_FOUND: + text_airline.set(airline_record.airline); + text_country.set(airline_record.country); + break; + case DATABASE_NOT_FOUND: + text_airline.set("No airlines.db file"); + break; + } + + text_callsign.set(entry_copy.callsign); + text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); + + button_aircraft_details.on_select = [this, &nav](Button&) { + aircraft_details_view = nav.push(entry_copy, [this]() { send_updates = false; }); + send_updates = false; + }; + + button_see_map.on_select = [this, &nav](Button&) { + if (!send_updates) { // Prevent recursively launching the map + geomap_view = nav.push( + trimr(entry_copy.callsign[0] != ' ' ? entry_copy.callsign : entry_copy.icaoStr), + entry_copy.pos.altitude, + GeoPos::alt_unit::FEET, + entry_copy.pos.latitude, + entry_copy.pos.longitude, + entry_copy.velo.heading, + [this]() { + send_updates = false; + }); + send_updates = true; + } + }; }; - void ADSBRxView::focus() { - field_vga.focus(); + field_vga.focus(); } ADSBRxView::~ADSBRxView() { - receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit + receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit - // save app settings - settings.save("rx_adsb", &app_settings); + // save app settings + settings.save("rx_adsb", &app_settings); - //TODO: once all apps keep there own settin previous frequency logic can be removed - receiver_model.set_tuning_frequency(prevFreq); - rtc_time::signal_tick_second -= signal_token_tick_second; - receiver_model.disable(); - baseband::shutdown(); + // TODO: once all apps keep there own settin previous frequency logic can be removed + receiver_model.set_tuning_frequency(prevFreq); + rtc_time::signal_tick_second -= signal_token_tick_second; + receiver_model.disable(); + baseband::shutdown(); } AircraftRecentEntry ADSBRxView::find_or_create_entry(uint32_t ICAO_address) { - auto it = find(recent, ICAO_address); - - // If not found - if (it == std::end(recent)){ - recent.emplace_front(ICAO_address); // Add it - it = find(recent, ICAO_address); // Find it again - } - return *it; + auto it = find(recent, ICAO_address); + + // If not found + if (it == std::end(recent)) { + recent.emplace_front(ICAO_address); // Add it + it = find(recent, ICAO_address); // Find it again + } + return *it; } -void ADSBRxView::replace_entry(AircraftRecentEntry & entry) -{ - uint32_t ICAO_address = entry.ICAO_address; +void ADSBRxView::replace_entry(AircraftRecentEntry& entry) { + uint32_t ICAO_address = entry.ICAO_address; - std::replace_if( recent.begin(), recent.end(), - [ICAO_address](const AircraftRecentEntry & compEntry) {return ICAO_address == compEntry.ICAO_address;}, - entry); + std::replace_if( + recent.begin(), recent.end(), + [ICAO_address](const AircraftRecentEntry& compEntry) { return ICAO_address == compEntry.ICAO_address; }, + entry); } -void ADSBRxView::remove_old_entries() -{ - auto it = recent.rbegin(); - auto end = recent.rend(); - while (it != end) - { - if (it->age_state>=4) { - std::advance(it, 1); - recent.erase( it.base() ); - } else { - break; // stop looking because the list is sorted - } - } +void ADSBRxView::remove_old_entries() { + auto it = recent.rbegin(); + auto end = recent.rend(); + while (it != end) { + if (it->age_state >= 4) { + std::advance(it, 1); + recent.erase(it.base()); + } else { + break; // stop looking because the list is sorted + } + } } -void ADSBRxView::sort_entries_by_state() -{ - // Sorting List pn age_state using lambda function as comparator - recent.sort([](const AircraftRecentEntry & left, const AircraftRecentEntry & right){return (left.age_state < right.age_state); }); +void ADSBRxView::sort_entries_by_state() { + // Sorting List pn age_state using lambda function as comparator + recent.sort([](const AircraftRecentEntry& left, const AircraftRecentEntry& right) { return (left.age_state < right.age_state); }); } -void ADSBRxView::on_frame(const ADSBFrameMessage * message) { - logger = std::make_unique(); - rtc::RTC datetime; - std::string callsign; - std::string str_info; - std::string logentry; - - auto frame = message->frame; - uint32_t ICAO_address = frame.get_ICAO_address(); - - if (frame.check_CRC() && ICAO_address) { - rtcGetTime(&RTCD1, &datetime); - auto entry = find_or_create_entry(ICAO_address); - frame.set_rx_timestamp(datetime.minute() * 60 + datetime.second()); - entry.reset_age(); - if (entry.hits==0) - { - entry.amp = message->amp; // Store amplitude on first hit - } else { - entry.amp = ((entry.amp*15)+message->amp)>>4; // Update smoothed amplitude on updates - } - - entry.inc_hit(); - if (logger) { - logentry += to_string_hex_array(frame.get_raw_data(), 14) + " "; - logentry += "ICAO:" + entry.icaoStr + " "; - } - - if (frame.get_DF() == DF_ADSB) { - uint8_t msg_type = frame.get_msg_type(); - uint8_t msg_sub = frame.get_msg_sub(); - uint8_t * raw_data = frame.get_raw_data(); - - // 4: // surveillance, altitude reply - if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) { - callsign = decode_frame_id(frame); - entry.set_callsign(callsign); - if (logger) { - logentry+=callsign+" "; - } - } - // 9: - // 18: { // Extended squitter/non-transponder - // 21: // Comm-B, identity reply - // 20: // Comm-B, altitude reply - else if (((msg_type >= AIRBORNE_POS_BARO_L) && (msg_type <= AIRBORNE_POS_BARO_H)) || - ((msg_type >= AIRBORNE_POS_GPS_L) && (msg_type <= AIRBORNE_POS_GPS_H))) { - entry.set_frame_pos(frame, raw_data[6] & 4); - - if (entry.pos.valid) { - str_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + - " Lat:" + to_string_decimal(entry.pos.latitude, 2) + - " Lon:" + to_string_decimal(entry.pos.longitude, 2); - - - entry.set_info_string(str_info); - - if (logger) { - // printing the coordinates in the log file with more - // resolution, as we are not constrained by screen - // real estate there: - - std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + - " Lat:" + to_string_decimal(entry.pos.latitude, 7) + - " Lon:" + to_string_decimal(entry.pos.longitude, 7); - logentry+=log_info + " "; - } - - } - } else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){ - entry.set_frame_velo(frame); - if (logger) { - logger->append( LOG_ROOT_DIR "/ADSB.TXT" ); - logentry += "Type:" + to_string_dec_uint(msg_sub) + - " Hdg:" + to_string_dec_uint(entry.velo.heading) + - " Spd: "+ to_string_dec_int(entry.velo.speed); - } - - } - replace_entry(entry); - } // frame.get_DF() == DF_ADSB +void ADSBRxView::on_frame(const ADSBFrameMessage* message) { + logger = std::make_unique(); + rtc::RTC datetime; + std::string callsign; + std::string str_info; + std::string logentry; + + auto frame = message->frame; + uint32_t ICAO_address = frame.get_ICAO_address(); + + if (frame.check_CRC() && ICAO_address) { + rtcGetTime(&RTCD1, &datetime); + auto entry = find_or_create_entry(ICAO_address); + frame.set_rx_timestamp(datetime.minute() * 60 + datetime.second()); + entry.reset_age(); + if (entry.hits == 0) { + entry.amp = message->amp; // Store amplitude on first hit + } else { + entry.amp = ((entry.amp * 15) + message->amp) >> 4; // Update smoothed amplitude on updates + } + entry.inc_hit(); if (logger) { - logger->append( LOG_ROOT_DIR "/ADSB.TXT" ); - // will log each frame in format: - // 20171103100227 8DADBEEFDEADBEEFDEADBEEFDEADBEEF ICAO:nnnnnn callsign Alt:nnnnnn Latnnn.nn Lonnnn.nn - logger->log_str(logentry); + logentry += to_string_hex_array(frame.get_raw_data(), 14) + " "; + logentry += "ICAO:" + entry.icaoStr + " "; } - } + + if (frame.get_DF() == DF_ADSB) { + uint8_t msg_type = frame.get_msg_type(); + uint8_t msg_sub = frame.get_msg_sub(); + uint8_t* raw_data = frame.get_raw_data(); + + // 4: // surveillance, altitude reply + if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) { + callsign = decode_frame_id(frame); + entry.set_callsign(callsign); + if (logger) { + logentry += callsign + " "; + } + } + // 9: + // 18: { // Extended squitter/non-transponder + // 21: // Comm-B, identity reply + // 20: // Comm-B, altitude reply + else if (((msg_type >= AIRBORNE_POS_BARO_L) && (msg_type <= AIRBORNE_POS_BARO_H)) || + ((msg_type >= AIRBORNE_POS_GPS_L) && (msg_type <= AIRBORNE_POS_GPS_H))) { + entry.set_frame_pos(frame, raw_data[6] & 4); + + if (entry.pos.valid) { + str_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + + " Lat:" + to_string_decimal(entry.pos.latitude, 2) + + " Lon:" + to_string_decimal(entry.pos.longitude, 2); + + entry.set_info_string(str_info); + + if (logger) { + // printing the coordinates in the log file with more + // resolution, as we are not constrained by screen + // real estate there: + + std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + + " Lat:" + to_string_decimal(entry.pos.latitude, 7) + + " Lon:" + to_string_decimal(entry.pos.longitude, 7); + logentry += log_info + " "; + } + } + } else if (msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC) { + entry.set_frame_velo(frame); + if (logger) { + logger->append(LOG_ROOT_DIR "/ADSB.TXT"); + logentry += "Type:" + to_string_dec_uint(msg_sub) + + " Hdg:" + to_string_dec_uint(entry.velo.heading) + + " Spd: " + to_string_dec_int(entry.velo.speed); + } + } + replace_entry(entry); + } // frame.get_DF() == DF_ADSB + + if (logger) { + logger->append(LOG_ROOT_DIR "/ADSB.TXT"); + // will log each frame in format: + // 20171103100227 8DADBEEFDEADBEEFDEADBEEFDEADBEEF ICAO:nnnnnn callsign Alt:nnnnnn Latnnn.nn Lonnnn.nn + logger->log_str(logentry); + } + } } -void ADSBRxView::on_tick_second() { - if (recent.size() <= 16){ // Not many entries update everything (16 is one screen full) - updateDetailsAndMap(1); - updateRecentEntries(); - } else if (updateState==0) { // Even second - updateState = 1; - updateDetailsAndMap(2); - } else { // Odd second only performed when there are many entries - updateState = 0; - updateRecentEntries(); - } +void ADSBRxView::on_tick_second() { + if (recent.size() <= 16) { // Not many entries update everything (16 is one screen full) + updateDetailsAndMap(1); + updateRecentEntries(); + } else if (updateState == 0) { // Even second + updateState = 1; + updateDetailsAndMap(2); + } else { // Odd second only performed when there are many entries + updateState = 0; + updateRecentEntries(); + } } void ADSBRxView::updateDetailsAndMap(int ageStep) { - ui::GeoMarker marker; - bool storeNewMarkers = false; - - // Sort and truncate the entries, grouped, newest group first - sort_entries_by_state(); - truncate_entries(recent); - remove_old_entries(); - - // Calculate if it is time to update markers - if (send_updates && details_view && details_view->geomap_view) { - ticksSinceMarkerRefresh += ageStep; - if (ticksSinceMarkerRefresh >= MARKER_UPDATE_SECONDS) { // Update other aircraft every few seconds - storeNewMarkers = true; - ticksSinceMarkerRefresh=0; - } - } else { - ticksSinceMarkerRefresh = MARKER_UPDATE_SECONDS; // Send the markers as soon as the geoview exists - } - - // Increment age, and pass updates to the details and map - const bool otherMarkersCanBeSent = send_updates && storeNewMarkers && details_view && details_view->geomap_view; // Save retesting all of this - MapMarkerStored markerStored = MARKER_NOT_STORED; - if (otherMarkersCanBeSent) {details_view->geomap_view->clear_markers();} - // Loop through all entries - for (auto& entry : recent) { - entry.inc_age(ageStep); - - // Only if there is a details view - if (send_updates && details_view) { - if (entry.key() == detailed_entry_key) // Check if the ICAO address match - { - details_view->update(entry); - } - // Store if the view is present and the list isn't full - else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state<=2)) - { - marker.lon = entry.pos.longitude; - marker.lat = entry.pos.latitude; - marker.angle = entry.velo.heading; - marker.tag = trimr(entry.callsign[0]!=' ' ? entry.callsign : entry.icaoStr); - markerStored = details_view->geomap_view->store_marker(marker); - } - } - } // Loop through all entries, if only to update the age + ui::GeoMarker marker; + bool storeNewMarkers = false; + + // Sort and truncate the entries, grouped, newest group first + sort_entries_by_state(); + truncate_entries(recent); + remove_old_entries(); + + // Calculate if it is time to update markers + if (send_updates && details_view && details_view->geomap_view) { + ticksSinceMarkerRefresh += ageStep; + if (ticksSinceMarkerRefresh >= MARKER_UPDATE_SECONDS) { // Update other aircraft every few seconds + storeNewMarkers = true; + ticksSinceMarkerRefresh = 0; + } + } else { + ticksSinceMarkerRefresh = MARKER_UPDATE_SECONDS; // Send the markers as soon as the geoview exists + } + + // Increment age, and pass updates to the details and map + const bool otherMarkersCanBeSent = send_updates && storeNewMarkers && details_view && details_view->geomap_view; // Save retesting all of this + MapMarkerStored markerStored = MARKER_NOT_STORED; + if (otherMarkersCanBeSent) { + details_view->geomap_view->clear_markers(); + } + // Loop through all entries + for (auto& entry : recent) { + entry.inc_age(ageStep); + + // Only if there is a details view + if (send_updates && details_view) { + if (entry.key() == detailed_entry_key) // Check if the ICAO address match + { + details_view->update(entry); + } + // Store if the view is present and the list isn't full + else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state <= 2)) { + marker.lon = entry.pos.longitude; + marker.lat = entry.pos.latitude; + marker.angle = entry.velo.heading; + marker.tag = trimr(entry.callsign[0] != ' ' ? entry.callsign : entry.icaoStr); + markerStored = details_view->geomap_view->store_marker(marker); + } + } + } // Loop through all entries, if only to update the age } void ADSBRxView::updateRecentEntries() { - // Redraw the list of aircraft - if (!send_updates){ - recent_entries_view.set_dirty(); - } + // Redraw the list of aircraft + if (!send_updates) { + recent_entries_view.set_dirty(); + } } ADSBRxView::ADSBRxView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_adsb_rx); - add_children({ - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &rssi, - &recent_entries_view - }); - - - // load app settings - auto rc = settings.load("rx_adsb", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - else - { - field_lna.set_value(40); - field_vga.set_value(40); - } - - - recent_entries_view.set_parent_rect({ 0, 16, 240, 272 }); - recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) { - detailed_entry_key = entry.key(); - details_view = nav.push( - entry, - [this]() { - send_updates = false; - }); - send_updates = true; - }; - - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - on_tick_second(); - }; - - prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation - - baseband::set_adsb(); - - receiver_model.set_tuning_frequency(1090000000); - receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); - receiver_model.set_sampling_rate(2000000); - receiver_model.set_baseband_bandwidth(2500000); - receiver_model.enable(); + baseband::run_image(portapack::spi_flash::image_tag_adsb_rx); + add_children({&labels, + &field_lna, + &field_vga, + &field_rf_amp, + &rssi, + &recent_entries_view}); + + // load app settings + auto rc = settings.load("rx_adsb", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } else { + field_lna.set_value(40); + field_vga.set_value(40); + } + + recent_entries_view.set_parent_rect({0, 16, 240, 272}); + recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) { + detailed_entry_key = entry.key(); + details_view = nav.push( + entry, + [this]() { + send_updates = false; + }); + send_updates = true; + }; + + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + on_tick_second(); + }; + + prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation + + baseband::set_adsb(); + + receiver_model.set_tuning_frequency(1090000000); + receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); + receiver_model.set_sampling_rate(2000000); + receiver_model.set_baseband_bandwidth(2500000); + receiver_model.enable(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index b22d39309..dff97ab7a 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -40,391 +40,357 @@ using namespace adsb; namespace ui { -#define ADSB_CURRENT 10 // Seconds -#define ADSB_RECENT 30 // Seconds -#define ADSB_REMOVE 300 // Used for removing old entries +#define ADSB_CURRENT 10 // Seconds +#define ADSB_RECENT 30 // Seconds +#define ADSB_REMOVE 300 // Used for removing old entries -#define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id) -#define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id) +#define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id) +#define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id) -#define SURFACE_POS_L 5 // surface position (lowest type id) -#define SURFACE_POS_H 8 // surface position (highest type id) +#define SURFACE_POS_L 5 // surface position (lowest type id) +#define SURFACE_POS_H 8 // surface position (highest type id) -#define AIRBORNE_POS_BARO_L 9 // airborne position (lowest type id) -#define AIRBORNE_POS_BARO_H 18 // airborne position (highest type id) +#define AIRBORNE_POS_BARO_L 9 // airborne position (lowest type id) +#define AIRBORNE_POS_BARO_H 18 // airborne position (highest type id) -#define AIRBORNE_VEL 19 // airborne velocities +#define AIRBORNE_VEL 19 // airborne velocities -#define AIRBORNE_POS_GPS_L 20 // airborne position (lowest type id) -#define AIRBORNE_POS_GPS_H 22 // airborne position (highest type id) +#define AIRBORNE_POS_GPS_L 20 // airborne position (lowest type id) +#define AIRBORNE_POS_GPS_H 22 // airborne position (highest type id) -#define RESERVED_L 23 // reserved for other uses -#define RESERVED_H 31 // reserved for other uses +#define RESERVED_L 23 // reserved for other uses +#define RESERVED_H 31 // reserved for other uses -#define VEL_GND_SUBSONIC 1 -#define VEL_GND_SUPERSONIC 2 -#define VEL_AIR_SUBSONIC 3 -#define VEL_AIR_SUPERSONIC 4 +#define VEL_GND_SUBSONIC 1 +#define VEL_GND_SUPERSONIC 2 +#define VEL_AIR_SUBSONIC 3 +#define VEL_AIR_SUPERSONIC 4 -#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames +#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames struct AircraftRecentEntry { - using Key = uint32_t; - - static constexpr Key invalid_key = 0xffffffff; - - uint32_t ICAO_address { }; - uint16_t hits { 0 }; - - uint16_t age_state { 1 }; - uint32_t age { 0 }; - uint32_t amp { 0 }; - adsb_pos pos { false, 0, 0, 0 }; - adsb_vel velo { false, 0, 999, 0 }; - ADSBFrame frame_pos_even { }; - ADSBFrame frame_pos_odd { }; - - std::string icaoStr {" "}; - std::string callsign { " " }; - std::string info_string { "" }; - - AircraftRecentEntry( - const uint32_t ICAO_address - ) : ICAO_address { ICAO_address } - { - this->icaoStr = to_string_hex(ICAO_address, 6); - } - - Key key() const { - return ICAO_address; - } - - void set_callsign(std::string& new_callsign) { - callsign = new_callsign; - } - - void inc_hit() { - hits++; - } - - void set_frame_pos(ADSBFrame& frame, uint32_t parity) { - if (!parity) - frame_pos_even = frame; - else - frame_pos_odd = frame; - - if (!frame_pos_even.empty() && !frame_pos_odd.empty()) { - if (abs(frame_pos_even.get_rx_timestamp() - frame_pos_odd.get_rx_timestamp()) < O_E_FRAME_TIMEOUT) - pos = decode_frame_pos(frame_pos_even, frame_pos_odd); - } - } - - void set_frame_velo(ADSBFrame& frame){ - velo = decode_frame_velo(frame); - } - - void set_info_string(std::string& new_info_string) { - info_string = new_info_string; - } - - void reset_age() { - age = 0; - } - - void inc_age(int delta) { - age+=delta; - if (age < ADSB_CURRENT){ - age_state = pos.valid ? 0 : 1; - } else if(age < ADSB_RECENT){ - age_state = 2; - } else if(age < ADSB_REMOVE){ - age_state = 3; - } else{ - age_state = 4; - } - } + using Key = uint32_t; + + static constexpr Key invalid_key = 0xffffffff; + + uint32_t ICAO_address{}; + uint16_t hits{0}; + + uint16_t age_state{1}; + uint32_t age{0}; + uint32_t amp{0}; + adsb_pos pos{false, 0, 0, 0}; + adsb_vel velo{false, 0, 999, 0}; + ADSBFrame frame_pos_even{}; + ADSBFrame frame_pos_odd{}; + + std::string icaoStr{" "}; + std::string callsign{" "}; + std::string info_string{""}; + + AircraftRecentEntry( + const uint32_t ICAO_address) + : ICAO_address{ICAO_address} { + this->icaoStr = to_string_hex(ICAO_address, 6); + } + + Key key() const { + return ICAO_address; + } + + void set_callsign(std::string& new_callsign) { + callsign = new_callsign; + } + + void inc_hit() { + hits++; + } + + void set_frame_pos(ADSBFrame& frame, uint32_t parity) { + if (!parity) + frame_pos_even = frame; + else + frame_pos_odd = frame; + + if (!frame_pos_even.empty() && !frame_pos_odd.empty()) { + if (abs(frame_pos_even.get_rx_timestamp() - frame_pos_odd.get_rx_timestamp()) < O_E_FRAME_TIMEOUT) + pos = decode_frame_pos(frame_pos_even, frame_pos_odd); + } + } + + void set_frame_velo(ADSBFrame& frame) { + velo = decode_frame_velo(frame); + } + + void set_info_string(std::string& new_info_string) { + info_string = new_info_string; + } + + void reset_age() { + age = 0; + } + + void inc_age(int delta) { + age += delta; + if (age < ADSB_CURRENT) { + age_state = pos.valid ? 0 : 1; + } else if (age < ADSB_RECENT) { + age_state = 2; + } else if (age < ADSB_REMOVE) { + age_state = 3; + } else { + age_state = 4; + } + } }; using AircraftRecentEntries = RecentEntries; class ADSBLogger { -public: - Optional append(const std::filesystem::path& filename) { - return log_file.append(filename); - } - void log_str(std::string& logline); - -private: - LogFile log_file { }; + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + void log_str(std::string& logline); + + private: + LogFile log_file{}; }; - class ADSBRxAircraftDetailsView : public View { -public: - ADSBRxAircraftDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function on_close); - ~ADSBRxAircraftDetailsView(); - - ADSBRxAircraftDetailsView(const ADSBRxAircraftDetailsView&) = delete; - ADSBRxAircraftDetailsView(ADSBRxAircraftDetailsView&&) = delete; - ADSBRxAircraftDetailsView& operator=(const ADSBRxAircraftDetailsView&) = delete; - ADSBRxAircraftDetailsView& operator=(ADSBRxAircraftDetailsView&&) = delete; - - void focus() override; - - void update(const AircraftRecentEntry& entry); - - std::string title() const override { return "AC Details"; }; - - AircraftRecentEntry get_current_entry() { return entry_copy; } - - std::database::AircraftDBRecord aircraft_record = {}; - -private: - AircraftRecentEntry entry_copy { 0 }; - std::function on_close_ { }; - bool send_updates { false }; - std::database db = { }; - std::string icao_code = ""; - int return_code = 0; - - Labels labels { - { { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, "Registration:", Color::light_grey() }, - { { 0 * 8, 3 * 16 }, "Manufacturer:", Color::light_grey() }, - { { 0 * 8, 5 * 16 }, "Model:", Color::light_grey() }, - { { 0 * 8, 7 * 16 }, "Type:", Color::light_grey() }, - { { 0 * 8, 8 * 16 }, "Number of engines:", Color::light_grey() }, - { { 0 * 8, 9 * 16 }, "Engine type:", Color::light_grey() }, - { { 0 * 8, 11 * 16 }, "Owner:", Color::light_grey() }, - { { 0 * 8, 13 * 16 }, "Operator:", Color::light_grey() } - }; - - Text text_icao_address { - { 5 * 8, 1 * 16, 6 * 8, 16}, - "-" - }; - - Text text_registration { - { 13 * 8, 2 * 16, 8 * 8, 16 }, - "-" - }; - - Text text_manufacturer { - { 0 * 8, 4 * 16, 19 * 8, 16 }, - "-" - }; - - Text text_model { - { 0 * 8, 6 * 16, 30 * 8, 16 }, - "-" - }; - - Text text_type { - { 5 * 8, 7 * 16, 22 * 8, 16 }, - "-" - }; - - Text text_number_of_engines { - { 18 * 8, 8 * 16, 30 * 8, 16 }, - "-" - }; - - Text text_engine_type { - { 0 * 8, 10 * 16, 30 * 8, 16}, - "-" - }; - - Text text_owner { - { 0 * 8, 12 * 16, 30 * 8, 16 }, - "-" - }; - - Text text_operator { - { 0 * 8, 14 * 16, 30 * 8, 16 }, - "-" - }; - - Button button_close { - { 9 * 8, 16 * 16, 12 * 8, 3 * 16 }, - "Back" - }; + public: + ADSBRxAircraftDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function on_close); + ~ADSBRxAircraftDetailsView(); + + ADSBRxAircraftDetailsView(const ADSBRxAircraftDetailsView&) = delete; + ADSBRxAircraftDetailsView(ADSBRxAircraftDetailsView&&) = delete; + ADSBRxAircraftDetailsView& operator=(const ADSBRxAircraftDetailsView&) = delete; + ADSBRxAircraftDetailsView& operator=(ADSBRxAircraftDetailsView&&) = delete; + + void focus() override; + + void update(const AircraftRecentEntry& entry); + + std::string title() const override { return "AC Details"; }; + + AircraftRecentEntry get_current_entry() { return entry_copy; } + + std::database::AircraftDBRecord aircraft_record = {}; + + private: + AircraftRecentEntry entry_copy{0}; + std::function on_close_{}; + bool send_updates{false}; + std::database db = {}; + std::string icao_code = ""; + int return_code = 0; + + Labels labels{ + {{0 * 8, 1 * 16}, "ICAO:", Color::light_grey()}, + {{0 * 8, 2 * 16}, "Registration:", Color::light_grey()}, + {{0 * 8, 3 * 16}, "Manufacturer:", Color::light_grey()}, + {{0 * 8, 5 * 16}, "Model:", Color::light_grey()}, + {{0 * 8, 7 * 16}, "Type:", Color::light_grey()}, + {{0 * 8, 8 * 16}, "Number of engines:", Color::light_grey()}, + {{0 * 8, 9 * 16}, "Engine type:", Color::light_grey()}, + {{0 * 8, 11 * 16}, "Owner:", Color::light_grey()}, + {{0 * 8, 13 * 16}, "Operator:", Color::light_grey()}}; + + Text text_icao_address{ + {5 * 8, 1 * 16, 6 * 8, 16}, + "-"}; + + Text text_registration{ + {13 * 8, 2 * 16, 8 * 8, 16}, + "-"}; + + Text text_manufacturer{ + {0 * 8, 4 * 16, 19 * 8, 16}, + "-"}; + + Text text_model{ + {0 * 8, 6 * 16, 30 * 8, 16}, + "-"}; + + Text text_type{ + {5 * 8, 7 * 16, 22 * 8, 16}, + "-"}; + + Text text_number_of_engines{ + {18 * 8, 8 * 16, 30 * 8, 16}, + "-"}; + + Text text_engine_type{ + {0 * 8, 10 * 16, 30 * 8, 16}, + "-"}; + + Text text_owner{ + {0 * 8, 12 * 16, 30 * 8, 16}, + "-"}; + + Text text_operator{ + {0 * 8, 14 * 16, 30 * 8, 16}, + "-"}; + + Button button_close{ + {9 * 8, 16 * 16, 12 * 8, 3 * 16}, + "Back"}; }; - class ADSBRxDetailsView : public View { -public: - ADSBRxDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function on_close); - ~ADSBRxDetailsView(); - - ADSBRxDetailsView(const ADSBRxDetailsView&) = delete; - ADSBRxDetailsView(ADSBRxDetailsView&&) = delete; - ADSBRxDetailsView& operator=(const ADSBRxDetailsView&) = delete; - ADSBRxDetailsView& operator=(ADSBRxDetailsView&&) = delete; - - void focus() override; - - void update(const AircraftRecentEntry& entry); - - std::string title() const override { return "Details"; }; - - AircraftRecentEntry get_current_entry() { return entry_copy; } - - - std::database::AirlinesDBRecord airline_record = {}; - - GeoMapView* geomap_view { nullptr }; -private: - AircraftRecentEntry entry_copy { 0 }; - std::function on_close_ { }; - ADSBRxAircraftDetailsView* aircraft_details_view { nullptr }; - bool send_updates { false }; - std::database db = { }; - std::string airline_code = ""; - int return_code = 0; - - Labels labels { - { { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() }, - { { 13 * 8, 1 * 16 }, "Callsign:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() }, - { { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() }, - { { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() }, - { { 0 * 8, 13 * 16 }, "Even position frame:", Color::light_grey() }, - { { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() } - }; - - Text text_icao_address { - { 5 * 8, 1 * 16, 6 * 8, 16}, - "-" - }; - - Text text_callsign { - { 22 * 8, 1 * 16, 8 * 8, 16 }, - "-" - }; - - Text text_last_seen { - { 11 * 8, 2 * 16, 19 * 8, 16 }, - "-" - }; - - Text text_airline { - { 0 * 8, 4 * 16, 30 * 8, 16 }, - "-" - }; - - Text text_country { - { 8 * 8, 5 * 16, 22 * 8, 16 }, - "-" - }; - - Text text_infos { - { 0 * 8, 6 * 16, 30 * 8, 16 }, - "-" - }; - - Text text_info2 { - {0*8, 7*16, 30*8, 16}, - "-" - }; - - Text text_frame_pos_even { - { 0 * 8, 14 * 16, 30 * 8, 16 }, - "-" - }; - Text text_frame_pos_odd { - { 0 * 8, 16 * 16, 30 * 8, 16 }, - "-" - }; - - Button button_aircraft_details { - { 2 * 8, 9 * 16, 12 * 8, 3 * 16 }, - "A/C details" - }; - - Button button_see_map { - { 16 * 8, 9 * 16, 12 * 8, 3 * 16 }, - "See on map" - }; + public: + ADSBRxDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function on_close); + ~ADSBRxDetailsView(); + + ADSBRxDetailsView(const ADSBRxDetailsView&) = delete; + ADSBRxDetailsView(ADSBRxDetailsView&&) = delete; + ADSBRxDetailsView& operator=(const ADSBRxDetailsView&) = delete; + ADSBRxDetailsView& operator=(ADSBRxDetailsView&&) = delete; + + void focus() override; + + void update(const AircraftRecentEntry& entry); + + std::string title() const override { return "Details"; }; + + AircraftRecentEntry get_current_entry() { return entry_copy; } + + std::database::AirlinesDBRecord airline_record = {}; + + GeoMapView* geomap_view{nullptr}; + + private: + AircraftRecentEntry entry_copy{0}; + std::function on_close_{}; + ADSBRxAircraftDetailsView* aircraft_details_view{nullptr}; + bool send_updates{false}; + std::database db = {}; + std::string airline_code = ""; + int return_code = 0; + + Labels labels{ + {{0 * 8, 1 * 16}, "ICAO:", Color::light_grey()}, + {{13 * 8, 1 * 16}, "Callsign:", Color::light_grey()}, + {{0 * 8, 2 * 16}, "Last seen:", Color::light_grey()}, + {{0 * 8, 3 * 16}, "Airline:", Color::light_grey()}, + {{0 * 8, 5 * 16}, "Country:", Color::light_grey()}, + {{0 * 8, 13 * 16}, "Even position frame:", Color::light_grey()}, + {{0 * 8, 15 * 16}, "Odd position frame:", Color::light_grey()}}; + + Text text_icao_address{ + {5 * 8, 1 * 16, 6 * 8, 16}, + "-"}; + + Text text_callsign{ + {22 * 8, 1 * 16, 8 * 8, 16}, + "-"}; + + Text text_last_seen{ + {11 * 8, 2 * 16, 19 * 8, 16}, + "-"}; + + Text text_airline{ + {0 * 8, 4 * 16, 30 * 8, 16}, + "-"}; + + Text text_country{ + {8 * 8, 5 * 16, 22 * 8, 16}, + "-"}; + + Text text_infos{ + {0 * 8, 6 * 16, 30 * 8, 16}, + "-"}; + + Text text_info2{ + {0 * 8, 7 * 16, 30 * 8, 16}, + "-"}; + + Text text_frame_pos_even{ + {0 * 8, 14 * 16, 30 * 8, 16}, + "-"}; + Text text_frame_pos_odd{ + {0 * 8, 16 * 16, 30 * 8, 16}, + "-"}; + + Button button_aircraft_details{ + {2 * 8, 9 * 16, 12 * 8, 3 * 16}, + "A/C details"}; + + Button button_see_map{ + {16 * 8, 9 * 16, 12 * 8, 3 * 16}, + "See on map"}; }; - class ADSBRxView : public View { -public: - ADSBRxView(NavigationView& nav); - ~ADSBRxView(); - - ADSBRxView(const ADSBRxView&) = delete; - ADSBRxView(ADSBRxView&&) = delete; - ADSBRxView& operator=(const ADSBRxView&) = delete; - ADSBRxView& operator=(ADSBRxView&&) = delete; - - void focus() override; - - std::string title() const override { return "ADS-B RX"; }; - - void replace_entry(AircraftRecentEntry & entry); - void remove_old_entries(); - AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address); - void sort_entries_by_state(); - -private: - rf::Frequency prevFreq = { 0 }; - std::unique_ptr logger { }; - void on_frame(const ADSBFrameMessage * message); - void on_tick_second(); - int updateState = { 0 }; - void updateRecentEntries(); - void updateDetailsAndMap(int ageStep); - - #define MARKER_UPDATE_SECONDS (5) - int ticksSinceMarkerRefresh { MARKER_UPDATE_SECONDS-1 }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - const RecentEntriesColumns columns { { - { "ICAO/Call", 9 }, - { "Lvl", 3 }, - { "Spd", 3 }, - { "Amp", 3 }, - { "Hit", 3 }, - { "Age", 4 } - } }; - AircraftRecentEntries recent { }; - RecentEntriesView> recent_entries_view { columns, recent }; - - SignalToken signal_token_tick_second { }; - ADSBRxDetailsView* details_view { nullptr }; - uint32_t detailed_entry_key { 0 }; - bool send_updates { false }; - - Labels labels { - { { 0 * 8, 0 * 8 }, "LNA: VGA: AMP:", Color::light_grey() } - }; - - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; - - RSSI rssi { - { 20 * 8, 4, 10 * 8, 8 }, - }; - - MessageHandlerRegistration message_handler_frame { - Message::ID::ADSBFrame, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_frame(message); - } - }; + public: + ADSBRxView(NavigationView& nav); + ~ADSBRxView(); + + ADSBRxView(const ADSBRxView&) = delete; + ADSBRxView(ADSBRxView&&) = delete; + ADSBRxView& operator=(const ADSBRxView&) = delete; + ADSBRxView& operator=(ADSBRxView&&) = delete; + + void focus() override; + + std::string title() const override { return "ADS-B RX"; }; + + void replace_entry(AircraftRecentEntry& entry); + void remove_old_entries(); + AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address); + void sort_entries_by_state(); + + private: + rf::Frequency prevFreq = {0}; + std::unique_ptr logger{}; + void on_frame(const ADSBFrameMessage* message); + void on_tick_second(); + int updateState = {0}; + void updateRecentEntries(); + void updateDetailsAndMap(int ageStep); + +#define MARKER_UPDATE_SECONDS (5) + int ticksSinceMarkerRefresh{MARKER_UPDATE_SECONDS - 1}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + const RecentEntriesColumns columns{{{"ICAO/Call", 9}, + {"Lvl", 3}, + {"Spd", 3}, + {"Amp", 3}, + {"Hit", 3}, + {"Age", 4}}}; + AircraftRecentEntries recent{}; + RecentEntriesView> recent_entries_view{columns, recent}; + + SignalToken signal_token_tick_second{}; + ADSBRxDetailsView* details_view{nullptr}; + uint32_t detailed_entry_key{0}; + bool send_updates{false}; + + Labels labels{ + {{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Color::light_grey()}}; + + LNAGainField field_lna{ + {4 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {11 * 8, 0 * 16}}; + + RFAmpField field_rf_amp{ + {18 * 8, 0 * 16}}; + + RSSI rssi{ + {20 * 8, 4, 10 * 8, 8}, + }; + + MessageHandlerRegistration message_handler_frame{ + Message::ID::ADSBFrame, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_frame(message); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_adsb_tx.cpp b/firmware/application/apps/ui_adsb_tx.cpp index 237879c46..646c836ad 100644 --- a/firmware/application/apps/ui_adsb_tx.cpp +++ b/firmware/application/apps/ui_adsb_tx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -38,338 +38,319 @@ using namespace portapack; namespace ui { Compass::Compass( - const Point parent_pos -) : Widget { { parent_pos, { 64, 64 } } } -{ + const Point parent_pos) + : Widget{{parent_pos, {64, 64}}} { } void Compass::set_value(uint32_t new_value) { - Point center = screen_pos() + Point(32, 32); - - new_value = range.clip(new_value); - - display.draw_line( - center, - center + polar_to_point(value_, 28), - Color::dark_grey() - ); - - display.draw_line( - center, - center + polar_to_point(new_value, 28), - Color::green() - ); - - value_ = new_value; + Point center = screen_pos() + Point(32, 32); + + new_value = range.clip(new_value); + + display.draw_line( + center, + center + polar_to_point(value_, 28), + Color::dark_grey()); + + display.draw_line( + center, + center + polar_to_point(new_value, 28), + Color::green()); + + value_ = new_value; } void Compass::paint(Painter&) { - display.fill_circle(screen_pos() + Point(32, 32), 32, Color::dark_grey(), Color::black()); - - display.fill_rectangle({ screen_pos() + Point(32 - 2, 0), { 4, 4 } }, Color::black()); // N - display.fill_rectangle({ screen_pos() + Point(32 - 2, 64 - 4), { 4, 4 } }, Color::black()); // S - display.fill_rectangle({ screen_pos() + Point(0, 32 - 2), { 4, 4 } }, Color::black()); // W - display.fill_rectangle({ screen_pos() + Point(64 - 4, 32 - 2), { 4, 4 } }, Color::black()); // E - - set_value(value_); + display.fill_circle(screen_pos() + Point(32, 32), 32, Color::dark_grey(), Color::black()); + + display.fill_rectangle({screen_pos() + Point(32 - 2, 0), {4, 4}}, Color::black()); // N + display.fill_rectangle({screen_pos() + Point(32 - 2, 64 - 4), {4, 4}}, Color::black()); // S + display.fill_rectangle({screen_pos() + Point(0, 32 - 2), {4, 4}}, Color::black()); // W + display.fill_rectangle({screen_pos() + Point(64 - 4, 32 - 2), {4, 4}}, Color::black()); // E + + set_value(value_); } ADSBPositionView::ADSBPositionView( - NavigationView& nav, Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("position"); - - add_children({ - &geopos, - &button_set_map - }); - - geopos.set_altitude(36000); - - button_set_map.on_select = [this, &nav](Button&) { - nav.push( - geopos.altitude(), - GeoPos::alt_unit::FEET, - geopos.lat(), - geopos.lon(), - [this](int32_t altitude, float lat, float lon) { - geopos.set_altitude(altitude); - geopos.set_lat(lat); - geopos.set_lon(lon); - }); - }; + NavigationView& nav, + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("position"); + + add_children({&geopos, + &button_set_map}); + + geopos.set_altitude(36000); + + button_set_map.on_select = [this, &nav](Button&) { + nav.push( + geopos.altitude(), + GeoPos::alt_unit::FEET, + geopos.lat(), + geopos.lon(), + [this](int32_t altitude, float lat, float lon) { + geopos.set_altitude(altitude); + geopos.set_lat(lat); + geopos.set_lon(lon); + }); + }; } void ADSBPositionView::collect_frames(const uint32_t ICAO_address, std::vector& frame_list) { - if (!enabled) return; - - ADSBFrame temp_frame; - - encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), - geopos.lat(), geopos.lon(), 0); - - frame_list.emplace_back(temp_frame); - - encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), - geopos.lat(), geopos.lon(), 1); - - frame_list.emplace_back(temp_frame); + if (!enabled) return; + + ADSBFrame temp_frame; + + encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), + geopos.lat(), geopos.lon(), 0); + + frame_list.emplace_back(temp_frame); + + encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), + geopos.lat(), geopos.lon(), 1); + + frame_list.emplace_back(temp_frame); } ADSBCallsignView::ADSBCallsignView( - NavigationView& nav, Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("callsign"); - - add_children({ - &labels_callsign, - &button_callsign - }); - - set_enabled(true); - - button_callsign.set_text(callsign); - - button_callsign.on_select = [this, &nav](Button&) { - text_prompt( - nav, - callsign, - 8, - [this](std::string& s) { - button_callsign.set_text(s); - } - ); - }; + NavigationView& nav, + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("callsign"); + + add_children({&labels_callsign, + &button_callsign}); + + set_enabled(true); + + button_callsign.set_text(callsign); + + button_callsign.on_select = [this, &nav](Button&) { + text_prompt( + nav, + callsign, + 8, + [this](std::string& s) { + button_callsign.set_text(s); + }); + }; } void ADSBCallsignView::collect_frames(const uint32_t ICAO_address, std::vector& frame_list) { - if (!enabled) return; - - ADSBFrame temp_frame; - - encode_frame_id(temp_frame, ICAO_address, callsign); - - frame_list.emplace_back(temp_frame); + if (!enabled) return; + + ADSBFrame temp_frame; + + encode_frame_id(temp_frame, ICAO_address, callsign); + + frame_list.emplace_back(temp_frame); } ADSBSpeedView::ADSBSpeedView( - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("speed"); - - add_children({ - &labels_speed, - &labels_vert_rate, - &compass, - &field_angle, - &field_speed, - &field_vert_rate - }); - - field_angle.set_value(0); - field_speed.set_value(400); - field_vert_rate.set_value(0); - - field_angle.on_change = [this](int32_t v) { - compass.set_value(v); - }; + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("speed"); + + add_children({&labels_speed, + &labels_vert_rate, + &compass, + &field_angle, + &field_speed, + &field_vert_rate}); + + field_angle.set_value(0); + field_speed.set_value(400); + field_vert_rate.set_value(0); + + field_angle.on_change = [this](int32_t v) { + compass.set_value(v); + }; } void ADSBSpeedView::collect_frames(const uint32_t ICAO_address, std::vector& frame_list) { - if (!enabled) return; - - ADSBFrame temp_frame; - - encode_frame_velo(temp_frame, ICAO_address, field_speed.value(), - field_angle.value(), field_vert_rate.value()); // Added v_rate , ft/min , (+) climb , (-) descend . - - frame_list.emplace_back(temp_frame); + if (!enabled) return; + + ADSBFrame temp_frame; + + encode_frame_velo(temp_frame, ICAO_address, field_speed.value(), + field_angle.value(), field_vert_rate.value()); // Added v_rate , ft/min , (+) climb , (-) descend . + + frame_list.emplace_back(temp_frame); } ADSBSquawkView::ADSBSquawkView( - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("squawk"); - - add_children({ - &labels_squawk, - &field_squawk - }); + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("squawk"); + + add_children({&labels_squawk, + &field_squawk}); } void ADSBSquawkView::collect_frames(const uint32_t ICAO_address, std::vector& frame_list) { - if (!enabled) return; - - ADSBFrame temp_frame; - (void)ICAO_address; - - encode_frame_squawk(temp_frame, field_squawk.concatenate_4_octal_u16()); - - frame_list.emplace_back(temp_frame); + if (!enabled) return; + + ADSBFrame temp_frame; + (void)ICAO_address; + + encode_frame_squawk(temp_frame, field_squawk.concatenate_4_octal_u16()); + + frame_list.emplace_back(temp_frame); } ADSBTXThread::ADSBTXThread( - std::vector frames -) : frames_ { std::move(frames) } -{ - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ADSBTXThread::static_fn, this); + std::vector frames) + : frames_{std::move(frames)} { + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ADSBTXThread::static_fn, this); } ADSBTXThread::~ADSBTXThread() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } + if (thread) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } } msg_t ADSBTXThread::static_fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); - return 0; + auto obj = static_cast(arg); + obj->run(); + return 0; } void ADSBTXThread::run() { - uint8_t * bin_ptr = shared_memory.bb_data.data; - uint8_t * raw_ptr; - uint32_t frame_index = 0; //, plane_index = 0; - //uint32_t regen = 0; - //float offs = 0; - - while( !chThdShouldTerminate() ) { - /*if (!regen) { - regen = 10; - - encode_frame_id(frames[0], plane_index, "DEMO" + to_string_dec_uint(plane_index)); - encode_frame_pos(frames[1], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 0); - encode_frame_pos(frames[2], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 1); - encode_frame_identity(frames[3], plane_index, 1337); - - if (plane_index == 11) - plane_index = 0; - else - plane_index++; - - offs += 0.001; - }*/ - - memset(bin_ptr, 0, 256); // 112 bits * 2 parts = 224 should be enough - - raw_ptr = frames_[frame_index].get_raw_data(); - - // The preamble isn't manchester encoded - memcpy(bin_ptr, adsb_preamble, 16); - - // Convert to binary (1 byte per bit, faster for baseband code) - manchester_encode(bin_ptr + 16, raw_ptr, 112, 0); - - // Display in hex for debug - //text_frame.set(to_string_hex_array(frames[0].get_raw_data(), 14)); - - baseband::set_adsb(); - - chThdSleepMilliseconds(50); - - frame_index++; - if (frame_index >= frames_.size()) { - frame_index = 0; - //if (regen) - // regen--; - } - } + uint8_t* bin_ptr = shared_memory.bb_data.data; + uint8_t* raw_ptr; + uint32_t frame_index = 0; //, plane_index = 0; + // uint32_t regen = 0; + // float offs = 0; + + while (!chThdShouldTerminate()) { + /*if (!regen) { + regen = 10; + + encode_frame_id(frames[0], plane_index, "DEMO" + to_string_dec_uint(plane_index)); + encode_frame_pos(frames[1], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 0); + encode_frame_pos(frames[2], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 1); + encode_frame_identity(frames[3], plane_index, 1337); + + if (plane_index == 11) + plane_index = 0; + else + plane_index++; + + offs += 0.001; + }*/ + + memset(bin_ptr, 0, 256); // 112 bits * 2 parts = 224 should be enough + + raw_ptr = frames_[frame_index].get_raw_data(); + + // The preamble isn't manchester encoded + memcpy(bin_ptr, adsb_preamble, 16); + + // Convert to binary (1 byte per bit, faster for baseband code) + manchester_encode(bin_ptr + 16, raw_ptr, 112, 0); + + // Display in hex for debug + // text_frame.set(to_string_hex_array(frames[0].get_raw_data(), 14)); + + baseband::set_adsb(); + + chThdSleepMilliseconds(50); + + frame_index++; + if (frame_index >= frames_.size()) { + frame_index = 0; + // if (regen) + // regen--; + } + } } void ADSBTxView::focus() { - tab_view.focus(); + tab_view.focus(); } ADSBTxView::~ADSBTxView() { + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_adsb", &app_settings); - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_adsb", &app_settings); - - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. - } + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. +} void ADSBTxView::generate_frames() { - const uint32_t ICAO_address = sym_icao.value_hex_u64(); - - frames.clear(); - - /* This scheme kinda sucks. Each "tab"'s collect_frames method - * is called to generate its related frame(s). Getting values - * from each widget of each tab would be better ? - * */ - view_position.collect_frames(ICAO_address, frames); - view_callsign.collect_frames(ICAO_address, frames); - view_speed.collect_frames(ICAO_address, frames); - view_squawk.collect_frames(ICAO_address, frames); - - // Show how many frames were generated - //text_frame.set(to_string_dec_uint(frames.size()) + " frame(s)."); + const uint32_t ICAO_address = sym_icao.value_hex_u64(); + + frames.clear(); + + /* This scheme kinda sucks. Each "tab"'s collect_frames method + * is called to generate its related frame(s). Getting values + * from each widget of each tab would be better ? + * */ + view_position.collect_frames(ICAO_address, frames); + view_callsign.collect_frames(ICAO_address, frames); + view_speed.collect_frames(ICAO_address, frames); + view_squawk.collect_frames(ICAO_address, frames); + + // Show how many frames were generated + // text_frame.set(to_string_dec_uint(frames.size()) + " frame(s)."); } void ADSBTxView::start_tx() { - generate_frames(); - - transmitter_model.set_sampling_rate(4000000U); - transmitter_model.set_baseband_bandwidth(10000000); - transmitter_model.enable(); - - baseband::set_adsb(); - - tx_thread = std::make_unique(frames); + generate_frames(); + + transmitter_model.set_sampling_rate(4000000U); + transmitter_model.set_baseband_bandwidth(10000000); + transmitter_model.enable(); + + baseband::set_adsb(); + + tx_thread = std::make_unique(frames); } ADSBTxView::ADSBTxView( - NavigationView& nav -) : nav_ { nav } -{ - baseband::run_image(portapack::spi_flash::image_tag_adsb_tx); - - add_children({ - &tab_view, - &labels, - &sym_icao, - &view_position, - &view_callsign, - &view_speed, - &view_squawk, - &text_frame, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_adsb", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - transmitter_model.disable(); - }; + NavigationView& nav) + : nav_{nav} { + baseband::run_image(portapack::spi_flash::image_tag_adsb_tx); + + add_children({&tab_view, + &labels, + &sym_icao, + &view_position, + &view_callsign, + &view_speed, + &view_squawk, + &text_frame, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_adsb", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + transmitter_model.disable(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_adsb_tx.hpp b/firmware/application/apps/ui_adsb_tx.hpp index 3f2750746..28e4702ae 100644 --- a/firmware/application/apps/ui_adsb_tx.hpp +++ b/firmware/application/apps/ui_adsb_tx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -36,216 +36,213 @@ using namespace adsb; namespace ui { class Compass : public Widget { -public: - Compass(const Point parent_pos); + public: + Compass(const Point parent_pos); - void set_value(uint32_t new_value); + void set_value(uint32_t new_value); - void paint(Painter&) override; + void paint(Painter&) override; -private: - const range_t range { 0, 359 }; - uint32_t value_ { 0 }; + private: + const range_t range{0, 359}; + uint32_t value_{0}; }; class ADSBPositionView : public OptionTabView { -public: - ADSBPositionView(NavigationView& nav, Rect parent_rect); - - void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); - -private: - GeoPos geopos { - { 0, 2 * 16 }, - GeoPos::FEET - }; - - Button button_set_map { - { 8 * 8, 6 * 16, 14 * 8, 2 * 16 }, - "Set from map" - }; + public: + ADSBPositionView(NavigationView& nav, Rect parent_rect); + + void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); + + private: + GeoPos geopos{ + {0, 2 * 16}, + GeoPos::FEET}; + + Button button_set_map{ + {8 * 8, 6 * 16, 14 * 8, 2 * 16}, + "Set from map"}; }; class ADSBCallsignView : public OptionTabView { -public: - ADSBCallsignView(NavigationView& nav, Rect parent_rect); - - void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); - -private: - std::string callsign = "TEST1234"; - - Labels labels_callsign { - { { 2 * 8, 5 * 8 }, "Callsign:", Color::light_grey() } - }; - - Button button_callsign { - { 12 * 8, 2 * 16, 10 * 8, 2 * 16 }, - "" - }; + public: + ADSBCallsignView(NavigationView& nav, Rect parent_rect); + + void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); + + private: + std::string callsign = "TEST1234"; + + Labels labels_callsign{ + {{2 * 8, 5 * 8}, "Callsign:", Color::light_grey()}}; + + Button button_callsign{ + {12 * 8, 2 * 16, 10 * 8, 2 * 16}, + ""}; }; class ADSBSpeedView : public OptionTabView { -public: - ADSBSpeedView(Rect parent_rect); - - void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); - -private: - Labels labels_speed { - { { 1 * 8, 6 * 16 }, "Speed: kn Bearing: *", Color::light_grey() } - }; - - Labels labels_vert_rate { - { { 1 * 8, 8 * 16 }, "Vert. rate: ft/min, (+/-)", Color::light_grey() } - }; - - Compass compass { - { 21 * 8, 2 * 16 } - }; - - NumberField field_angle { - { 21 * 8 + 20, 6 * 16 }, 3, { 0, 359 }, 1, ' ', true - }; - - NumberField field_speed { - { 8 * 8, 6 * 16 }, 3, { 0, 999 }, 5, ' ' - }; - - NumberField field_vert_rate { - { 11 * 8, 8 * 16 }, 5, { -4096, 4096 }, 64, ' ' // Let's limit to +/-5k aprox , Ex. max safe descent vertical rate aprox -1000 ft/min on an instrument approach. , std step is 64 - }; + public: + ADSBSpeedView(Rect parent_rect); + + void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); + + private: + Labels labels_speed{ + {{1 * 8, 6 * 16}, "Speed: kn Bearing: *", Color::light_grey()}}; + + Labels labels_vert_rate{ + {{1 * 8, 8 * 16}, "Vert. rate: ft/min, (+/-)", Color::light_grey()}}; + + Compass compass{ + {21 * 8, 2 * 16}}; + + NumberField field_angle{ + {21 * 8 + 20, 6 * 16}, + 3, + {0, 359}, + 1, + ' ', + true}; + + NumberField field_speed{ + {8 * 8, 6 * 16}, + 3, + {0, 999}, + 5, + ' '}; + + NumberField field_vert_rate{ + {11 * 8, 8 * 16}, + 5, + {-4096, 4096}, + 64, + ' ' // Let's limit to +/-5k aprox , Ex. max safe descent vertical rate aprox -1000 ft/min on an instrument approach. , std step is 64 + }; }; class ADSBSquawkView : public OptionTabView { -public: - ADSBSquawkView(Rect parent_rect); - - void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); - -private: - Labels labels_squawk { - { { 2 * 8, 2 * 16 }, "Squawk:", Color::light_grey() } - }; - - SymField field_squawk { - { 10 * 8, 2 * 16 }, - 4, - SymField::SYMFIELD_OCT - }; + public: + ADSBSquawkView(Rect parent_rect); + + void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); + + private: + Labels labels_squawk{ + {{2 * 8, 2 * 16}, "Squawk:", Color::light_grey()}}; + + SymField field_squawk{ + {10 * 8, 2 * 16}, + 4, + SymField::SYMFIELD_OCT}; }; class ADSBTXThread { -public: - ADSBTXThread(std::vector frames); - ~ADSBTXThread(); - - ADSBTXThread(const ADSBTXThread&) = delete; - ADSBTXThread(ADSBTXThread&&) = delete; - ADSBTXThread& operator=(const ADSBTXThread&) = delete; - ADSBTXThread& operator=(ADSBTXThread&&) = delete; - -private: - std::vector frames_ { }; - Thread* thread { nullptr }; - - static msg_t static_fn(void* arg); - - void run(); + public: + ADSBTXThread(std::vector frames); + ~ADSBTXThread(); + + ADSBTXThread(const ADSBTXThread&) = delete; + ADSBTXThread(ADSBTXThread&&) = delete; + ADSBTXThread& operator=(const ADSBTXThread&) = delete; + ADSBTXThread& operator=(ADSBTXThread&&) = delete; + + private: + std::vector frames_{}; + Thread* thread{nullptr}; + + static msg_t static_fn(void* arg); + + void run(); }; class ADSBTxView : public View { -public: - ADSBTxView(NavigationView& nav); - ~ADSBTxView(); - - void focus() override; - - std::string title() const override { return "ADS-B TX"; }; - -private: - /*enum tx_modes { - IDLE = 0, - SINGLE, - SEQUENCE - };*/ - - /*const float plane_lats[12] = { - 0, - -1, - -2, - -3, - -4, - -5, - -4.5, - -5, - -4, - -3, - -2, - -1 - }; - const float plane_lons[12] = { - 0, - 1, - 1, - 1, - 2, - 1, - 0, - -1, - -2, - -1, - -1, - -1 - };*/ - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - //tx_modes tx_mode = IDLE; - NavigationView& nav_; - std::vector frames { }; - - void start_tx(); - void generate_frames(); - - Rect view_rect = { 0, 7 * 8, 240, 192 }; - - ADSBPositionView view_position { nav_, view_rect }; - ADSBCallsignView view_callsign { nav_, view_rect }; - ADSBSpeedView view_speed { view_rect }; - ADSBSquawkView view_squawk { view_rect }; - - TabView tab_view { - { "Position", Color::cyan(), &view_position }, - { "Callsign", Color::green(), &view_callsign }, - { "Speed", Color::yellow(), &view_speed }, - { "Squawk", Color::orange(), &view_squawk } - }; - - Labels labels { - { { 2 * 8, 4 * 8 }, "ICAO24:", Color::light_grey() } - }; - - SymField sym_icao { - { 10 * 8, 4 * 8 }, - 6, - SymField::SYMFIELD_HEX - }; - - Text text_frame { - { 1 * 8, 29 * 8, 14 * 8, 16 }, - "-" - }; - - TransmitterView tx_view { - 16 * 16, - 1000000, - 0 - }; - - std::unique_ptr tx_thread { }; + public: + ADSBTxView(NavigationView& nav); + ~ADSBTxView(); + + void focus() override; + + std::string title() const override { return "ADS-B TX"; }; + + private: + /*enum tx_modes { + IDLE = 0, + SINGLE, + SEQUENCE + };*/ + + /*const float plane_lats[12] = { + 0, + -1, + -2, + -3, + -4, + -5, + -4.5, + -5, + -4, + -3, + -2, + -1 + }; + const float plane_lons[12] = { + 0, + 1, + 1, + 1, + 2, + 1, + 0, + -1, + -2, + -1, + -1, + -1 + };*/ + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + // tx_modes tx_mode = IDLE; + NavigationView& nav_; + std::vector frames{}; + + void start_tx(); + void generate_frames(); + + Rect view_rect = {0, 7 * 8, 240, 192}; + + ADSBPositionView view_position{nav_, view_rect}; + ADSBCallsignView view_callsign{nav_, view_rect}; + ADSBSpeedView view_speed{view_rect}; + ADSBSquawkView view_squawk{view_rect}; + + TabView tab_view{ + {"Position", Color::cyan(), &view_position}, + {"Callsign", Color::green(), &view_callsign}, + {"Speed", Color::yellow(), &view_speed}, + {"Squawk", Color::orange(), &view_squawk}}; + + Labels labels{ + {{2 * 8, 4 * 8}, "ICAO24:", Color::light_grey()}}; + + SymField sym_icao{ + {10 * 8, 4 * 8}, + 6, + SymField::SYMFIELD_HEX}; + + Text text_frame{ + {1 * 8, 29 * 8, 14 * 8, 16}, + "-"}; + + TransmitterView tx_view{ + 16 * 16, + 1000000, + 0}; + + std::unique_ptr tx_thread{}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_afsk_rx.cpp b/firmware/application/apps/ui_afsk_rx.cpp index c1746b3c2..95011cece 100644 --- a/firmware/application/apps/ui_afsk_rx.cpp +++ b/firmware/application/apps/ui_afsk_rx.cpp @@ -34,152 +34,148 @@ using namespace portapack; using namespace modems; void AFSKLogger::log_raw_data(const std::string& data) { - rtc::RTC datetime; - rtcGetTime(&RTCD1, &datetime); - - log_file.write_entry(datetime, data); + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + + log_file.write_entry(datetime, data); } namespace ui { void AFSKRxView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void AFSKRxView::update_freq(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } AFSKRxView::AFSKRxView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); - - add_children({ - &rssi, - &channel, - &field_rf_amp, - &field_lna, - &field_vga, - &field_frequency, - &check_log, - &text_debug, - &button_modem_setup, - &console - }); - - // load app settings - auto rc = settings.load("rx_afsk", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - - // Auto-configure modem for LCR RX (will be removed later) - update_freq(467225500); // 462713300 - auto def_bell202 = &modem_defs[0]; - persistent_memory::set_modem_baudrate(def_bell202->baudrate); - serial_format_t serial_format; - serial_format.data_bits = 7; - serial_format.parity = EVEN; - serial_format.stop_bits = 1; - serial_format.bit_order = LSB_FIRST; - persistent_memory::set_serial_format(serial_format); - - field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(100); - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - field_frequency.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - check_log.set_value(logging); - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - }; - - button_modem_setup.on_select = [&nav](Button&) { - nav.push(); - }; - - logger = std::make_unique(); - if (logger) - logger->append(LOG_ROOT_DIR "/AFSK.TXT"); - - // Auto-configure modem for LCR RX (will be removed later) - baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); - - audio::set_rate(audio::Rate::Hz_24000); - audio::output::start(); - - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.enable(); + baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &check_log, + &text_debug, + &button_modem_setup, + &console}); + + // load app settings + auto rc = settings.load("rx_afsk", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } + + // Auto-configure modem for LCR RX (will be removed later) + update_freq(467225500); // 462713300 + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); + }; + + logger = std::make_unique(); + if (logger) + logger->append(LOG_ROOT_DIR "/AFSK.TXT"); + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + receiver_model.enable(); } void AFSKRxView::on_data(uint32_t value, bool is_data) { - std::string str_console = "\x1B"; - std::string str_byte = ""; - - if (is_data) { - // Colorize differently after message splits - str_console += (char)((console_color & 3) + 9); - - //value = deframe_word(value); - - value &= 0xFF; // ABCDEFGH - value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD - value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB - value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA - value &= 0x7F; // Ignore parity, which is the MSB now - - if ((value >= 32) && (value < 127)) { - str_console += (char)value; // Printable - str_byte += (char)value; - } else { - str_console += "[" + to_string_hex(value, 2) + "]"; // Not printable - str_byte += "[" + to_string_hex(value, 2) + "]"; - } - - //str_byte = to_string_bin(value & 0xFF, 8) + " "; - - console.write(str_console); - - if (logger && logging) str_log += str_byte; - - if ((value != 0x7F) && (prev_value == 0x7F)) { - // Message split - console.writeln(""); - console_color++; - - if (logger && logging) { - logger->log_raw_data(str_log); - str_log = ""; - } - } - prev_value = value; - } - else { - // Baudrate estimation - text_debug.set("Baudrate estimation: ~" + to_string_dec_uint(value)); - } + std::string str_console = "\x1B"; + std::string str_byte = ""; + + if (is_data) { + // Colorize differently after message splits + str_console += (char)((console_color & 3) + 9); + + // value = deframe_word(value); + + value &= 0xFF; // ABCDEFGH + value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + value &= 0x7F; // Ignore parity, which is the MSB now + + if ((value >= 32) && (value < 127)) { + str_console += (char)value; // Printable + str_byte += (char)value; + } else { + str_console += "[" + to_string_hex(value, 2) + "]"; // Not printable + str_byte += "[" + to_string_hex(value, 2) + "]"; + } + + // str_byte = to_string_bin(value & 0xFF, 8) + " "; + + console.write(str_console); + + if (logger && logging) str_log += str_byte; + + if ((value != 0x7F) && (prev_value == 0x7F)) { + // Message split + console.writeln(""); + console_color++; + + if (logger && logging) { + logger->log_raw_data(str_log); + str_log = ""; + } + } + prev_value = value; + } else { + // Baudrate estimation + text_debug.set("Baudrate estimation: ~" + to_string_dec_uint(value)); + } } AFSKRxView::~AFSKRxView() { + // save app settings + app_settings.rx_frequency = field_frequency.value(); + settings.save("rx_afsk", &app_settings); - // save app settings - app_settings.rx_frequency = field_frequency.value(); - settings.save("rx_afsk", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_afsk_rx.hpp b/firmware/application/apps/ui_afsk_rx.hpp index 5051f9c44..848c40568 100644 --- a/firmware/application/apps/ui_afsk_rx.hpp +++ b/firmware/application/apps/ui_afsk_rx.hpp @@ -26,101 +26,93 @@ #include "ui.hpp" #include "ui_navigation.hpp" #include "ui_receiver.hpp" -#include "ui_record_view.hpp" // DEBUG +#include "ui_record_view.hpp" // DEBUG #include "app_settings.hpp" #include "log_file.hpp" #include "utility.hpp" class AFSKLogger { -public: - Optional append(const std::string& filename) { - return log_file.append(filename); - } - - void log_raw_data(const std::string& data); - -private: - LogFile log_file { }; + public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const std::string& data); + + private: + LogFile log_file{}; }; namespace ui { class AFSKRxView : public View { -public: - AFSKRxView(NavigationView& nav); - ~AFSKRxView(); - - void focus() override; - - std::string title() const override { return "AFSK RX"; }; - -private: - void on_data(uint32_t value, bool is_data); - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - uint8_t console_color { 0 }; - uint32_t prev_value { 0 }; - std::string str_log { "" }; - bool logging { false }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 16 }, - }; - - Checkbox check_log { - { 0 * 8, 1 * 16 }, - 3, - "LOG", - false - }; - - Text text_debug { - { 0 * 8, 12 + 2 * 16, 240, 16 }, - "DEBUG" - }; - - Button button_modem_setup { - { 240 - 12 * 8, 1 * 16, 96, 24 }, - "Modem setup" - }; - - Console console { - { 0, 4 * 16, 240, 240 } - }; - - void update_freq(rf::Frequency f); - void on_data_afsk(const AFSKDataMessage& message); - - std::unique_ptr logger { }; - - MessageHandlerRegistration message_handler_packet { - Message::ID::AFSKData, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_data(message->value, message->is_data); - } - }; + public: + AFSKRxView(NavigationView& nav); + ~AFSKRxView(); + + void focus() override; + + std::string title() const override { return "AFSK RX"; }; + + private: + void on_data(uint32_t value, bool is_data); + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + uint8_t console_color{0}; + uint32_t prev_value{0}; + std::string str_log{""}; + bool logging{false}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 16}, + }; + + Checkbox check_log{ + {0 * 8, 1 * 16}, + 3, + "LOG", + false}; + + Text text_debug{ + {0 * 8, 12 + 2 * 16, 240, 16}, + "DEBUG"}; + + Button button_modem_setup{ + {240 - 12 * 8, 1 * 16, 96, 24}, + "Modem setup"}; + + Console console{ + {0, 4 * 16, 240, 240}}; + + void update_freq(rf::Frequency f); + void on_data_afsk(const AFSKDataMessage& message); + + std::unique_ptr logger{}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + }}; }; } /* namespace ui */ -#endif/*__UI_AFSK_RX_H__*/ +#endif /*__UI_AFSK_RX_H__*/ diff --git a/firmware/application/apps/ui_aprs_rx.cpp b/firmware/application/apps/ui_aprs_rx.cpp index 8908238cb..db40066df 100644 --- a/firmware/application/apps/ui_aprs_rx.cpp +++ b/firmware/application/apps/ui_aprs_rx.cpp @@ -31,392 +31,373 @@ using namespace portapack; void APRSLogger::log_raw_data(const std::string& data) { - rtc::RTC datetime; - rtcGetTime(&RTCD1, &datetime); - - log_file.write_entry(datetime, data); + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + + log_file.write_entry(datetime, data); } namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - char aged_color; - Color target_color; - // auto entry_age = entry.age; - - target_color = Color::green(); - - aged_color = 0x10; - std::string entry_string = "\x1B"; - entry_string += aged_color; - - entry_string += entry.source_formatted; - entry_string.append(10 - entry.source_formatted.size(),' '); - entry_string += " "; - entry_string += (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+"); - entry_string += " "; - entry_string += entry.time_string; - - painter.draw_string( - target_rect.location(), - style, - entry_string - ); - - if (entry.has_position){ - painter.draw_bitmap(target_rect.location() + Point(12 * 8, 0), bitmap_target, target_color, style.background); - } + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + char aged_color; + Color target_color; + // auto entry_age = entry.age; + + target_color = Color::green(); + + aged_color = 0x10; + std::string entry_string = "\x1B"; + entry_string += aged_color; + + entry_string += entry.source_formatted; + entry_string.append(10 - entry.source_formatted.size(), ' '); + entry_string += " "; + entry_string += (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+"); + entry_string += " "; + entry_string += entry.time_string; + + painter.draw_string( + target_rect.location(), + style, + entry_string); + + if (entry.has_position) { + painter.draw_bitmap(target_rect.location() + Point(12 * 8, 0), bitmap_target, target_color, style.background); + } } - void APRSRxView::focus() { - options_region.focus(); + options_region.focus(); } void APRSRxView::update_freq(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } -APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) : View(parent_rect) { - baseband::run_image(portapack::spi_flash::image_tag_aprs_rx); - - add_children({ - &rssi, - &channel, - &field_rf_amp, - &field_lna, - &field_vga, - &options_region, - &field_frequency, - &record_view, - &console - }); - - // load app settings - auto rc = settings.load("rx_aprs", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - - - // DEBUG - record_view.on_error = [&nav](std::string message) { - nav.display_modal("Error", message); - }; - - record_view.set_sampling_rate(24000); - - options_region.on_change = [this](size_t, int32_t i) { - if (i == 0){ - field_frequency.set_value(144390000); - } else if(i == 1){ - field_frequency.set_value(144800000); - } else if(i == 2){ - field_frequency.set_value(145175000); - } else if(i == 3){ - field_frequency.set_value(144575000); - } - }; - - field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(100); - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - field_frequency.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - options_region.set_selected_index(0, true); - - logger = std::make_unique(); - if (logger) - logger->append(LOG_ROOT_DIR "/APRS.TXT"); - - baseband::set_aprs(1200); - - audio::set_rate(audio::Rate::Hz_24000); - audio::output::start(); - - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.enable(); +APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) + : View(parent_rect) { + baseband::run_image(portapack::spi_flash::image_tag_aprs_rx); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &options_region, + &field_frequency, + &record_view, + &console}); + + // load app settings + auto rc = settings.load("rx_aprs", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } + + // DEBUG + record_view.on_error = [&nav](std::string message) { + nav.display_modal("Error", message); + }; + + record_view.set_sampling_rate(24000); + + options_region.on_change = [this](size_t, int32_t i) { + if (i == 0) { + field_frequency.set_value(144390000); + } else if (i == 1) { + field_frequency.set_value(144800000); + } else if (i == 2) { + field_frequency.set_value(145175000); + } else if (i == 3) { + field_frequency.set_value(144575000); + } + }; + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + options_region.set_selected_index(0, true); + + logger = std::make_unique(); + if (logger) + logger->append(LOG_ROOT_DIR "/APRS.TXT"); + + baseband::set_aprs(1200); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + receiver_model.enable(); } -void APRSRxView::on_packet(const APRSPacketMessage* message){ - std::string str_console = "\x1B"; +void APRSRxView::on_packet(const APRSPacketMessage* message) { + std::string str_console = "\x1B"; + + aprs::APRSPacket packet = message->packet; - aprs::APRSPacket packet = message->packet; + std::string stream_text = packet.get_stream_text(); + str_console += (char)((console_color++ & 3) + 9); + str_console += stream_text; - std::string stream_text = packet.get_stream_text(); - str_console += (char)((console_color++ & 3) + 9); - str_console += stream_text; + if (logger) { + logger->log_raw_data(stream_text); + } - if(logger){ - logger->log_raw_data(stream_text); - } - - //if(reset_console){ //having more than one console causes issues when switching tabs where one is disabled, and the other enabled breaking the scoll setup. - // console.on_hide(); - // console.on_show(); - // reset_console = false; - //} + // if(reset_console){ //having more than one console causes issues when switching tabs where one is disabled, and the other enabled breaking the scoll setup. + // console.on_hide(); + // console.on_show(); + // reset_console = false; + // } - console.writeln(str_console); + console.writeln(str_console); } void APRSRxView::on_data(uint32_t value, bool is_data) { - std::string str_console = "\x1B"; - std::string str_byte = ""; - - if (is_data) { - // Colorize differently after message splits - str_console += (char)((console_color & 3) + 9); - - if (value == '\n') { - // Message split - console.writeln(""); - console_color++; - - if (logger) { - logger->log_raw_data(str_log); - str_log = ""; - } - } - else { - if ((value >= 32) && (value < 127)) { - str_console += (char)value; // Printable - str_byte += (char)value; - } else { - str_console += "[" + to_string_hex(value, 2) + "]"; // Not printable - str_byte += "[" + to_string_hex(value, 2) + "]"; - } - - console.write(str_console); - - if (logger) str_log += str_byte; - } - } else { - - } + std::string str_console = "\x1B"; + std::string str_byte = ""; + + if (is_data) { + // Colorize differently after message splits + str_console += (char)((console_color & 3) + 9); + + if (value == '\n') { + // Message split + console.writeln(""); + console_color++; + + if (logger) { + logger->log_raw_data(str_log); + str_log = ""; + } + } else { + if ((value >= 32) && (value < 127)) { + str_console += (char)value; // Printable + str_byte += (char)value; + } else { + str_console += "[" + to_string_hex(value, 2) + "]"; // Not printable + str_byte += "[" + to_string_hex(value, 2) + "]"; + } + + console.write(str_console); + + if (logger) str_log += str_byte; + } + } else { + } } -void APRSRxView::on_show(){ - //some bug where the display scroll area is set to the entire screen when switching from the list tab with details showing back to the stream view. - //reset_console = true; +void APRSRxView::on_show() { + // some bug where the display scroll area is set to the entire screen when switching from the list tab with details showing back to the stream view. + // reset_console = true; } APRSRxView::~APRSRxView() { + // save app settings + settings.save("rx_aprs", &app_settings); - // save app settings - settings.save("rx_aprs", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } void APRSTableView::on_show_list() { - details_view.hidden(true); - recent_entries_view.hidden(false); - send_updates = false; - focus(); + details_view.hidden(true); + recent_entries_view.hidden(false); + send_updates = false; + focus(); } void APRSTableView::on_show_detail(const APRSRecentEntry& entry) { - recent_entries_view.hidden(true); - details_view.hidden(false); - details_view.set_entry(entry); - details_view.update(); - details_view.focus(); - detailed_entry_key = entry.key(); - send_updates = true; + recent_entries_view.hidden(true); + details_view.hidden(false); + details_view.set_entry(entry); + details_view.update(); + details_view.focus(); + detailed_entry_key = entry.key(); + send_updates = true; } -APRSTableView::APRSTableView(NavigationView& nav, Rect parent_rec) : View(parent_rec), nav_ {nav} { - add_children({ - &recent_entries_view, - &details_view - }); +APRSTableView::APRSTableView(NavigationView& nav, Rect parent_rec) + : View(parent_rec), nav_{nav} { + add_children({&recent_entries_view, + &details_view}); - hidden(true); + hidden(true); - details_view.hidden(true); - - recent_entries_view.set_parent_rect({0, 0, 240, 280}); - details_view.set_parent_rect({0, 0, 240, 280}); - - recent_entries_view.on_select = [this](const APRSRecentEntry& entry) { - this->on_show_detail(entry); - }; + details_view.hidden(true); - details_view.on_close = [this]() { - this->on_show_list(); - }; + recent_entries_view.set_parent_rect({0, 0, 240, 280}); + details_view.set_parent_rect({0, 0, 240, 280}); + recent_entries_view.on_select = [this](const APRSRecentEntry& entry) { + this->on_show_detail(entry); + }; -/* for(size_t i = 0; i <32 ; i++){ - std::string id = "test" + i; - auto& entry = ::on_packet(recent, i); - entry.set_source_formatted(id); - } + details_view.on_close = [this]() { + this->on_show_list(); + }; - recent_entries_view.set_dirty(); */ + /* for(size_t i = 0; i <32 ; i++){ + std::string id = "test" + i; + auto& entry = ::on_packet(recent, i); + entry.set_source_formatted(id); + } - /* + recent_entries_view.set_dirty(); */ - std::string str1 = "test1"; - std::string str12 = "test2"; - std::string str13 = "test2"; + /* - auto& entry = ::on_packet(recent, 0x1); - entry.set_source_formatted(str1); + std::string str1 = "test1"; + std::string str12 = "test2"; + std::string str13 = "test2"; - auto& entry2 = ::on_packet(recent, 0x2); - entry2.set_source_formatted(str12); + auto& entry = ::on_packet(recent, 0x1); + entry.set_source_formatted(str1); - auto& entry3 = ::on_packet(recent, 0x2); - entry2.set_source_formatted(str13); + auto& entry2 = ::on_packet(recent, 0x2); + entry2.set_source_formatted(str12); - recent_entries_view.set_dirty(); + auto& entry3 = ::on_packet(recent, 0x2); + entry2.set_source_formatted(str13); - */ -} + recent_entries_view.set_dirty(); -void APRSTableView::on_pkt(const APRSPacketMessage* message){ - aprs::APRSPacket packet = message->packet; - rtc::RTC datetime; - std::string str_timestamp; - std::string source_formatted = packet.get_source_formatted(); - std::string info_string = packet.get_stream_text(); - - rtcGetTime(&RTCD1, &datetime); - auto& entry = ::on_packet(recent, packet.get_source()); - entry.reset_age(); - entry.inc_hit(); - str_timestamp = to_string_datetime(datetime, HMS); - entry.set_time_string(str_timestamp); - entry.set_info_string(info_string); - - entry.set_source_formatted(source_formatted); - - if(entry.has_position && !packet.has_position()){ - //maintain position info - } - else { - entry.set_has_position(packet.has_position()); - entry.set_pos(packet.get_position()); - } - - if( entry.key() == details_view.entry().key() ) { - details_view.set_entry(entry); - details_view.update(); - } - - recent_entries_view.set_dirty(); + */ } -void APRSTableView::on_show(){ - on_show_list(); +void APRSTableView::on_pkt(const APRSPacketMessage* message) { + aprs::APRSPacket packet = message->packet; + rtc::RTC datetime; + std::string str_timestamp; + std::string source_formatted = packet.get_source_formatted(); + std::string info_string = packet.get_stream_text(); + + rtcGetTime(&RTCD1, &datetime); + auto& entry = ::on_packet(recent, packet.get_source()); + entry.reset_age(); + entry.inc_hit(); + str_timestamp = to_string_datetime(datetime, HMS); + entry.set_time_string(str_timestamp); + entry.set_info_string(info_string); + + entry.set_source_formatted(source_formatted); + + if (entry.has_position && !packet.has_position()) { + // maintain position info + } else { + entry.set_has_position(packet.has_position()); + entry.set_pos(packet.get_position()); + } + + if (entry.key() == details_view.entry().key()) { + details_view.set_entry(entry); + details_view.update(); + } + + recent_entries_view.set_dirty(); } -void APRSTableView::on_hide(){ - details_view.hidden(true); +void APRSTableView::on_show() { + on_show_list(); } -void APRSTableView::focus(){ - recent_entries_view.focus(); +void APRSTableView::on_hide() { + details_view.hidden(true); } -APRSTableView::~APRSTableView(){ +void APRSTableView::focus() { + recent_entries_view.focus(); +} +APRSTableView::~APRSTableView() { } void APRSDetailsView::focus() { - button_done.focus(); + button_done.focus(); } -void APRSDetailsView::set_entry(const APRSRecentEntry& entry){ - entry_copy = entry; +void APRSDetailsView::set_entry(const APRSRecentEntry& entry) { + entry_copy = entry; } -void APRSDetailsView::update() { - if(!hidden()){ - //uint32_t age = entry_copy.age; - - console.clear(true); - console.write(entry_copy.info_string); - - button_see_map.hidden(!entry_copy.has_position); - } - - if (send_updates) - geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0); +void APRSDetailsView::update() { + if (!hidden()) { + // uint32_t age = entry_copy.age; + + console.clear(true); + console.write(entry_copy.info_string); + + button_see_map.hidden(!entry_copy.has_position); + } + + if (send_updates) + geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0); } APRSDetailsView::~APRSDetailsView() { } APRSDetailsView::APRSDetailsView( - NavigationView& nav -) -{ - add_children({ - &console, - &button_done, - &button_see_map - }); - - button_done.on_select = [this, &nav](Button&) { - console.clear(true); - this->on_close(); - }; - - button_see_map.on_select = [this, &nav](Button&) { - geomap_view = nav.push( - entry_copy.source_formatted, - 0, //entry_copy.pos.altitude, - GeoPos::alt_unit::FEET, - entry_copy.pos.latitude, - entry_copy.pos.longitude, - 0, /*entry_copy.velo.heading,*/ - [this]() { - send_updates = false; - hidden(false); - update(); - }); - send_updates = true; - hidden(true); - - }; + NavigationView& nav) { + add_children({&console, + &button_done, + &button_see_map}); + + button_done.on_select = [this, &nav](Button&) { + console.clear(true); + this->on_close(); + }; + + button_see_map.on_select = [this, &nav](Button&) { + geomap_view = nav.push( + entry_copy.source_formatted, + 0, // entry_copy.pos.altitude, + GeoPos::alt_unit::FEET, + entry_copy.pos.latitude, + entry_copy.pos.longitude, + 0, /*entry_copy.velo.heading,*/ + [this]() { + send_updates = false; + hidden(false); + update(); + }); + send_updates = true; + hidden(true); + }; }; -APRSRXView::APRSRXView(NavigationView& nav) : nav_ {nav} { - add_children({ - &tab_view, - &view_stream, - &view_table - }); +APRSRXView::APRSRXView(NavigationView& nav) + : nav_{nav} { + add_children({&tab_view, + &view_stream, + &view_table}); } -void APRSRXView::focus(){ - tab_view.focus(); +void APRSRXView::focus() { + tab_view.focus(); } APRSRXView::~APRSRXView() { - } } /* namespace ui */ diff --git a/firmware/application/apps/ui_aprs_rx.hpp b/firmware/application/apps/ui_aprs_rx.hpp index 06030ad21..b9f577947 100644 --- a/firmware/application/apps/ui_aprs_rx.hpp +++ b/firmware/application/apps/ui_aprs_rx.hpp @@ -26,7 +26,7 @@ #include "ui.hpp" #include "ui_navigation.hpp" #include "ui_receiver.hpp" -#include "ui_record_view.hpp" // DEBUG +#include "ui_record_view.hpp" // DEBUG #include "ui_geomap.hpp" #include "app_settings.hpp" #include "recent_entries.hpp" @@ -36,248 +36,235 @@ #include "utility.hpp" class APRSLogger { -public: - Optional append(const std::string& filename) { - return log_file.append(filename); - } - - void log_raw_data(const std::string& data); - -private: - LogFile log_file { }; + public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const std::string& data); + + private: + LogFile log_file{}; }; namespace ui { struct APRSRecentEntry { - using Key = uint64_t; - - static constexpr Key invalid_key = 0xffffffffffffffff; - - uint16_t hits { 0 }; - uint32_t age { 0 }; - - uint64_t source { 0 }; - std::string source_formatted { " " }; - std::string time_string { "" }; - std::string info_string { "" }; - - aprs::aprs_pos pos { 0 , 0 , 0 , 0 }; - bool has_position = false; - APRSRecentEntry(uint64_t src) - { - source = src; - } - - Key key() const { - return source; - } - - void set_source_formatted(std::string& new_source) { - source_formatted = new_source; - } - - void inc_hit() { - hits++; - } - - void set_info_string(std::string& new_info_string) { - info_string = new_info_string; - } - - void set_time_string(std::string& new_time_string) { - time_string = new_time_string; - } - - void set_pos(aprs::aprs_pos pos_in){ - pos = pos_in; - } - - void set_has_position(bool has_pos){ - has_position = has_pos; - } - - void reset_age() { - age = 0; - } - - void inc_age() { - age++; - } + using Key = uint64_t; + + static constexpr Key invalid_key = 0xffffffffffffffff; + + uint16_t hits{0}; + uint32_t age{0}; + + uint64_t source{0}; + std::string source_formatted{" "}; + std::string time_string{""}; + std::string info_string{""}; + + aprs::aprs_pos pos{0, 0, 0, 0}; + bool has_position = false; + APRSRecentEntry(uint64_t src) { + source = src; + } + + Key key() const { + return source; + } + + void set_source_formatted(std::string& new_source) { + source_formatted = new_source; + } + + void inc_hit() { + hits++; + } + + void set_info_string(std::string& new_info_string) { + info_string = new_info_string; + } + + void set_time_string(std::string& new_time_string) { + time_string = new_time_string; + } + + void set_pos(aprs::aprs_pos pos_in) { + pos = pos_in; + } + + void set_has_position(bool has_pos) { + has_position = has_pos; + } + + void reset_age() { + age = 0; + } + + void inc_age() { + age++; + } }; class APRSDetailsView : public View { -public: - APRSDetailsView(NavigationView&); - ~APRSDetailsView(); - - APRSDetailsView(const APRSDetailsView&) = delete; - APRSDetailsView(APRSDetailsView&&) = delete; - APRSDetailsView& operator=(const APRSDetailsView&) = delete; - APRSDetailsView& operator=(APRSDetailsView&&) = delete; - - void focus() override; - - void update(); - void set_entry(const APRSRecentEntry& entry); - - const APRSRecentEntry& entry() const { return entry_copy; }; - - std::string title() const override { return "Details"; }; - std::function on_close { }; - -private: - APRSRecentEntry entry_copy { 0 }; - GeoMapView* geomap_view { nullptr }; - bool send_updates { false }; - - - Console console { - { 0, 0 * 16, 240, 224 } - }; - - Button button_done { - { 160, 14 * 16, 8 * 8, 3 * 16 }, - "Close" - }; - - Button button_see_map { - { 80, 14 * 16, 8 * 8, 3 * 16 }, - "Map" - }; + public: + APRSDetailsView(NavigationView&); + ~APRSDetailsView(); + + APRSDetailsView(const APRSDetailsView&) = delete; + APRSDetailsView(APRSDetailsView&&) = delete; + APRSDetailsView& operator=(const APRSDetailsView&) = delete; + APRSDetailsView& operator=(APRSDetailsView&&) = delete; + + void focus() override; + + void update(); + void set_entry(const APRSRecentEntry& entry); + + const APRSRecentEntry& entry() const { return entry_copy; }; + + std::string title() const override { return "Details"; }; + std::function on_close{}; + + private: + APRSRecentEntry entry_copy{0}; + GeoMapView* geomap_view{nullptr}; + bool send_updates{false}; + + Console console{ + {0, 0 * 16, 240, 224}}; + + Button button_done{ + {160, 14 * 16, 8 * 8, 3 * 16}, + "Close"}; + + Button button_see_map{ + {80, 14 * 16, 8 * 8, 3 * 16}, + "Map"}; }; using APRSRecentEntries = RecentEntries; -class APRSTableView: public View { -public: - APRSTableView(NavigationView& nav, Rect parent_rec); - ~APRSTableView(); - - void on_show() override; - void on_hide() override; - void focus() override; - void on_pkt(const APRSPacketMessage* message); - - std::string title() const override { return "Stations"; }; -private: - NavigationView& nav_; - const RecentEntriesColumns columns { { - { "Source", 9 }, - { "Loc", 6 }, - { "Hits", 4 }, - { "Time", 8 } - } }; - APRSRecentEntries recent { }; - RecentEntriesView> recent_entries_view { columns, recent }; - APRSDetailsView details_view { nav_ }; - uint32_t detailed_entry_key { 0 }; - bool send_updates { false }; - - void on_show_list(); - void on_show_detail(const APRSRecentEntry& entry); +class APRSTableView : public View { + public: + APRSTableView(NavigationView& nav, Rect parent_rec); + ~APRSTableView(); + + void on_show() override; + void on_hide() override; + void focus() override; + void on_pkt(const APRSPacketMessage* message); + + std::string title() const override { return "Stations"; }; + + private: + NavigationView& nav_; + const RecentEntriesColumns columns{{{"Source", 9}, + {"Loc", 6}, + {"Hits", 4}, + {"Time", 8}}}; + APRSRecentEntries recent{}; + RecentEntriesView> recent_entries_view{columns, recent}; + APRSDetailsView details_view{nav_}; + uint32_t detailed_entry_key{0}; + bool send_updates{false}; + + void on_show_list(); + void on_show_detail(const APRSRecentEntry& entry); }; class APRSRxView : public View { -public: - APRSRxView(NavigationView& nav, Rect parent_rect); - ~APRSRxView(); - - void on_show() override; - void focus() override; - - std::string title() const override { return "APRS RX"; }; - void on_packet(const APRSPacketMessage* message); - -private: - void on_data(uint32_t value, bool is_data); - bool reset_console = false; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - - uint8_t console_color { 0 }; - std::string str_log { "" }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - OptionsField options_region { - { 0 * 8, 0 * 8 }, - 3, - { - { "NA ", 0 }, - { "EUR", 1 }, - { "AUS", 2 }, - { "NZ ", 3 } - } - }; - - FrequencyField field_frequency { - { 3 * 8, 0 * 16 }, - }; - - // DEBUG - RecordView record_view { - { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }, - u"AFS_????.WAV", u"APRS", RecordView::FileType::WAV, 4096, 4 - }; - - Console console { - { 0, 2 * 16, 240, 240 } - }; - - void update_freq(rf::Frequency f); - - std::unique_ptr logger { }; + public: + APRSRxView(NavigationView& nav, Rect parent_rect); + ~APRSRxView(); + + void on_show() override; + void focus() override; + + std::string title() const override { return "APRS RX"; }; + void on_packet(const APRSPacketMessage* message); + + private: + void on_data(uint32_t value, bool is_data); + bool reset_console = false; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + uint8_t console_color{0}; + std::string str_log{""}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + OptionsField options_region{ + {0 * 8, 0 * 8}, + 3, + {{"NA ", 0}, + {"EUR", 1}, + {"AUS", 2}, + {"NZ ", 3}}}; + + FrequencyField field_frequency{ + {3 * 8, 0 * 16}, + }; + + // DEBUG + RecordView record_view{ + {0 * 8, 1 * 16, 30 * 8, 1 * 16}, + u"AFS_????.WAV", + u"APRS", + RecordView::FileType::WAV, + 4096, + 4}; + + Console console{ + {0, 2 * 16, 240, 240}}; + + void update_freq(rf::Frequency f); + + std::unique_ptr logger{}; }; class APRSRXView : public View { -public: - APRSRXView(NavigationView& nav); - ~APRSRXView(); - - void focus() override; - - std::string title() const override { return "APRS RX"; }; - -private: - NavigationView& nav_; - Rect view_rect = { 0, 3 * 8, 240, 280 }; - - APRSRxView view_stream { nav_, view_rect }; - APRSTableView view_table { nav_, view_rect }; - - TabView tab_view { - { "Stream", Color::cyan(), &view_stream }, - { "List", Color::yellow(), &view_table } - }; - - MessageHandlerRegistration message_handler_packet { - Message::ID::APRSPacket, - [this](Message* const p) { - const auto message = static_cast(p); - this->view_stream.on_packet(message); - this->view_table.on_pkt(message); - } - }; + public: + APRSRXView(NavigationView& nav); + ~APRSRXView(); + + void focus() override; + + std::string title() const override { return "APRS RX"; }; + + private: + NavigationView& nav_; + Rect view_rect = {0, 3 * 8, 240, 280}; + + APRSRxView view_stream{nav_, view_rect}; + APRSTableView view_table{nav_, view_rect}; + + TabView tab_view{ + {"Stream", Color::cyan(), &view_stream}, + {"List", Color::yellow(), &view_table}}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::APRSPacket, + [this](Message* const p) { + const auto message = static_cast(p); + this->view_stream.on_packet(message); + this->view_table.on_pkt(message); + }}; }; } /* namespace ui */ -#endif/*__UI_APRS_RX_H__*/ +#endif /*__UI_APRS_RX_H__*/ diff --git a/firmware/application/apps/ui_aprs_tx.cpp b/firmware/application/apps/ui_aprs_tx.cpp index 281fb0d97..783d7865b 100644 --- a/firmware/application/apps/ui_aprs_tx.cpp +++ b/firmware/application/apps/ui_aprs_tx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -40,103 +40,97 @@ using namespace portapack; namespace ui { void APRSTXView::focus() { - tx_view.focus(); + tx_view.focus(); } APRSTXView::~APRSTXView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_aprs", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_aprs", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void APRSTXView::start_tx() { - make_aprs_frame( - sym_source.value_string().c_str(), num_ssid_source.value(), - sym_dest.value_string().c_str(), num_ssid_dest.value(), - payload); - - //uint8_t * bb_data_ptr = shared_memory.bb_data.data; - //text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15)); - - transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency()); - transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_afsk_data( - AFSK_TX_SAMPLERATE / 1200, - 1200, - 2200, - 1, - 10000, //APRS uses fixed 10k bandwidth - 8 - ); + make_aprs_frame( + sym_source.value_string().c_str(), num_ssid_source.value(), + sym_dest.value_string().c_str(), num_ssid_dest.value(), + payload); + + // uint8_t * bb_data_ptr = shared_memory.bb_data.data; + // text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15)); + + transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency()); + transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_afsk_data( + AFSK_TX_SAMPLERATE / 1200, + 1200, + 2200, + 1, + 10000, // APRS uses fixed 10k bandwidth + 8); } void APRSTXView::on_tx_progress(const uint32_t progress, const bool done) { - (void)progress; - - if (done) { - transmitter_model.disable(); - tx_view.set_transmitting(false); - } + (void)progress; + + if (done) { + transmitter_model.disable(); + tx_view.set_transmitting(false); + } } APRSTXView::APRSTXView(NavigationView& nav) { - - baseband::run_image(portapack::spi_flash::image_tag_afsk); - - add_children({ - &labels, - &sym_source, - &num_ssid_source, - &sym_dest, - &num_ssid_dest, - &text_payload, - &button_set, - &tx_view - }); - - - // load app settings - auto rc = settings.load("tx_aprs", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - button_set.on_select = [this, &nav](Button&) { - text_prompt( - nav, - payload, - 30, - [this](std::string& s) { - text_payload.set(s); - } - ); - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - transmitter_model.disable(); - }; + baseband::run_image(portapack::spi_flash::image_tag_afsk); + + add_children({&labels, + &sym_source, + &num_ssid_source, + &sym_dest, + &num_ssid_dest, + &text_payload, + &button_set, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_aprs", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + button_set.on_select = [this, &nav](Button&) { + text_prompt( + nav, + payload, + 30, + [this](std::string& s) { + text_payload.set(s); + }); + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + transmitter_model.disable(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_aprs_tx.hpp b/firmware/application/apps/ui_aprs_tx.hpp index 16872b11a..02c3c17c7 100644 --- a/firmware/application/apps/ui_aprs_tx.hpp +++ b/firmware/application/apps/ui_aprs_tx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -35,82 +35,74 @@ namespace ui { class APRSTXView : public View { -public: - APRSTXView(NavigationView& nav); - ~APRSTXView(); - - void focus() override; - - std::string title() const override { return "APRS TX"; }; - -private: - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - std::string payload { "" }; - - void start_tx(); - void generate_frame(); - void generate_frame_pos(); - void on_tx_progress(const uint32_t progress, const bool done); - - Labels labels { - { { 0 * 8, 1 * 16 }, "Source: SSID:", Color::light_grey() }, // 6 alphanum + SSID - { { 0 * 8, 2 * 16 }, " Dest.: SSID:", Color::light_grey() }, - { { 0 * 8, 4 * 16 }, "Info field:", Color::light_grey() }, - }; - - SymField sym_source { - { 7 * 8, 1 * 16 }, - 6, - SymField::SYMFIELD_ALPHANUM - }; - NumberField num_ssid_source { - { 19 * 8, 1 * 16 }, - 2, - { 0, 15 }, - 1, - ' ' - }; - - SymField sym_dest { - { 7 * 8, 2 * 16 }, - 6, - SymField::SYMFIELD_ALPHANUM - }; - - NumberField num_ssid_dest { - { 19 * 8, 2 * 16 }, - 2, - { 0, 15 }, - 1, - ' ' - }; - - Text text_payload { - { 0 * 8, 5 * 16, 30 * 8, 16 }, - "-" - }; - Button button_set { - { 0 * 8, 6 * 16, 80, 32 }, - "Set" - }; - - TransmitterView tx_view { - 16 * 16, - 5000, - 0 // disable setting bandwith, since APRS used fixed 10k bandwidth - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + APRSTXView(NavigationView& nav); + ~APRSTXView(); + + void focus() override; + + std::string title() const override { return "APRS TX"; }; + + private: + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + std::string payload{""}; + + void start_tx(); + void generate_frame(); + void generate_frame_pos(); + void on_tx_progress(const uint32_t progress, const bool done); + + Labels labels{ + {{0 * 8, 1 * 16}, "Source: SSID:", Color::light_grey()}, // 6 alphanum + SSID + {{0 * 8, 2 * 16}, " Dest.: SSID:", Color::light_grey()}, + {{0 * 8, 4 * 16}, "Info field:", Color::light_grey()}, + }; + + SymField sym_source{ + {7 * 8, 1 * 16}, + 6, + SymField::SYMFIELD_ALPHANUM}; + NumberField num_ssid_source{ + {19 * 8, 1 * 16}, + 2, + {0, 15}, + 1, + ' '}; + + SymField sym_dest{ + {7 * 8, 2 * 16}, + 6, + SymField::SYMFIELD_ALPHANUM}; + + NumberField num_ssid_dest{ + {19 * 8, 2 * 16}, + 2, + {0, 15}, + 1, + ' '}; + + Text text_payload{ + {0 * 8, 5 * 16, 30 * 8, 16}, + "-"}; + Button button_set{ + {0 * 8, 6 * 16, 80, 32}, + "Set"}; + + TransmitterView tx_view{ + 16 * 16, + 5000, + 0 // disable setting bandwith, since APRS used fixed 10k bandwidth + }; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_bht_tx.cpp b/firmware/application/apps/ui_bht_tx.cpp index dd8e9e868..da5e73fbd 100644 --- a/firmware/application/apps/ui_bht_tx.cpp +++ b/firmware/application/apps/ui_bht_tx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -32,341 +32,327 @@ using namespace portapack; namespace ui { void BHTView::focus() { - tx_view.focus(); + tx_view.focus(); } void BHTView::start_tx() { - baseband::shutdown(); - - transmitter_model.set_baseband_bandwidth(1750000); - - if (target_system == XYLOS) { - - baseband::run_image(portapack::spi_flash::image_tag_tones); - - view_xylos.generate_message(); - - //if (tx_mode == SINGLE) { - progressbar.set_max(XY_TONE_COUNT); - /*} else if (tx_mode == SCAN) { - progressbar.set_max(XY_TONE_COUNT * view_xylos.get_scan_remaining()); - }*/ - - transmitter_model.set_sampling_rate(TONES_SAMPLERATE); - transmitter_model.enable(); - - // Setup tones - for (size_t c = 0; c < ccir_deltas.size(); c++) - baseband::set_tone(c, ccir_deltas[c], XY_TONE_DURATION); - - baseband::set_tones_config(transmitter_model.channel_bandwidth(), XY_SILENCE, XY_TONE_COUNT, false, false); - - } else if (target_system == EPAR) { - - baseband::run_image(portapack::spi_flash::image_tag_ook); - - auto bitstream_length = view_EPAR.generate_message(); - - //if (tx_mode == SINGLE) { - progressbar.set_max(2 * EPAR_REPEAT_COUNT); - /*} else if (tx_mode == SCAN) { - progressbar.set_max(2 * EPAR_REPEAT_COUNT * view_EPAR.get_scan_remaining()); - }*/ - - transmitter_model.set_sampling_rate(OOK_SAMPLERATE); - transmitter_model.enable(); - - baseband::set_ook_data( - bitstream_length, - EPAR_BIT_DURATION, - EPAR_REPEAT_COUNT, - encoder_defs[ENCODER_UM3750].pause_symbols - ); - } + baseband::shutdown(); + + transmitter_model.set_baseband_bandwidth(1750000); + + if (target_system == XYLOS) { + baseband::run_image(portapack::spi_flash::image_tag_tones); + + view_xylos.generate_message(); + + // if (tx_mode == SINGLE) { + progressbar.set_max(XY_TONE_COUNT); + /*} else if (tx_mode == SCAN) { + progressbar.set_max(XY_TONE_COUNT * view_xylos.get_scan_remaining()); + }*/ + + transmitter_model.set_sampling_rate(TONES_SAMPLERATE); + transmitter_model.enable(); + + // Setup tones + for (size_t c = 0; c < ccir_deltas.size(); c++) + baseband::set_tone(c, ccir_deltas[c], XY_TONE_DURATION); + + baseband::set_tones_config(transmitter_model.channel_bandwidth(), XY_SILENCE, XY_TONE_COUNT, false, false); + + } else if (target_system == EPAR) { + baseband::run_image(portapack::spi_flash::image_tag_ook); + + auto bitstream_length = view_EPAR.generate_message(); + + // if (tx_mode == SINGLE) { + progressbar.set_max(2 * EPAR_REPEAT_COUNT); + /*} else if (tx_mode == SCAN) { + progressbar.set_max(2 * EPAR_REPEAT_COUNT * view_EPAR.get_scan_remaining()); + }*/ + + transmitter_model.set_sampling_rate(OOK_SAMPLERATE); + transmitter_model.enable(); + + baseband::set_ook_data( + bitstream_length, + EPAR_BIT_DURATION, + EPAR_REPEAT_COUNT, + encoder_defs[ENCODER_UM3750].pause_symbols); + } } void BHTView::stop_tx() { - transmitter_model.disable(); - baseband::shutdown(); - tx_mode = IDLE; - tx_view.set_transmitting(false); - progressbar.set_value(0); + transmitter_model.disable(); + baseband::shutdown(); + tx_mode = IDLE; + tx_view.set_transmitting(false); + progressbar.set_value(0); } void BHTView::on_tx_progress(const uint32_t progress, const bool done) { - if (target_system == XYLOS) { - if (done) { - if (tx_mode == SINGLE) { - if (checkbox_flashing.value()) { - // TODO: Thread ! - chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :( - view_xylos.flip_relays(); - start_tx(); - } else - stop_tx(); - } else if (tx_mode == SCAN) { - if (view_xylos.increment_address()) - start_tx(); - else - stop_tx(); // Reached end of scan range - } - } else - progressbar.set_value(progress); - } else if (target_system == EPAR) { - if (done) { - - if (!view_EPAR.half) { - view_EPAR.half = true; - start_tx(); // Start second half of transmission - } else { - view_EPAR.half = false; - if (tx_mode == SINGLE) { - if (checkbox_flashing.value()) { - // TODO: Thread ! - chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :( - view_EPAR.flip_relays(); - start_tx(); - } else - stop_tx(); - } else if (tx_mode == SCAN) { - if (view_EPAR.increment_address()) - start_tx(); - else - stop_tx(); // Reached end of scan range - } - } - } else - progressbar.set_value(progress); - } + if (target_system == XYLOS) { + if (done) { + if (tx_mode == SINGLE) { + if (checkbox_flashing.value()) { + // TODO: Thread ! + chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :( + view_xylos.flip_relays(); + start_tx(); + } else + stop_tx(); + } else if (tx_mode == SCAN) { + if (view_xylos.increment_address()) + start_tx(); + else + stop_tx(); // Reached end of scan range + } + } else + progressbar.set_value(progress); + } else if (target_system == EPAR) { + if (done) { + if (!view_EPAR.half) { + view_EPAR.half = true; + start_tx(); // Start second half of transmission + } else { + view_EPAR.half = false; + if (tx_mode == SINGLE) { + if (checkbox_flashing.value()) { + // TODO: Thread ! + chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :( + view_EPAR.flip_relays(); + start_tx(); + } else + stop_tx(); + } else if (tx_mode == SCAN) { + if (view_EPAR.increment_address()) + start_tx(); + else + stop_tx(); // Reached end of scan range + } + } + } else + progressbar.set_value(progress); + } } BHTView::~BHTView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_bht", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_bht", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } BHTView::BHTView(NavigationView& nav) { - add_children({ - &tab_view, - &labels, - &view_xylos, - &view_EPAR, - &checkbox_scan, - &checkbox_flashing, - &field_speed, - &progressbar, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_bht", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - field_speed.set_value(1); - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (tx_mode == IDLE) { - if (checkbox_scan.value()) { - tx_mode = SCAN; - } else { - tx_mode = SINGLE; - } - - progressbar.set_value(0); - tx_view.set_transmitting(true); - target_system = (target_system_t)tab_view.selected(); - view_EPAR.half = false; - - start_tx(); - } - }; - - tx_view.on_stop = [this]() { - stop_tx(); - }; + add_children({&tab_view, + &labels, + &view_xylos, + &view_EPAR, + &checkbox_scan, + &checkbox_flashing, + &field_speed, + &progressbar, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_bht", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + field_speed.set_value(1); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (tx_mode == IDLE) { + if (checkbox_scan.value()) { + tx_mode = SCAN; + } else { + tx_mode = SINGLE; + } + + progressbar.set_value(0); + tx_view.set_transmitting(true); + target_system = (target_system_t)tab_view.selected(); + view_EPAR.half = false; + + start_tx(); + } + }; + + tx_view.on_stop = [this]() { + stop_tx(); + }; } bool EPARView::increment_address() { - auto city_code = field_city.value(); - - if (city_code < EPAR_MAX_CITY) { - field_city.set_value(city_code + 1); - return true; - } else - return false; + auto city_code = field_city.value(); + + if (city_code < EPAR_MAX_CITY) { + field_city.set_value(city_code + 1); + return true; + } else + return false; } uint32_t EPARView::get_scan_remaining() { - return EPAR_MAX_CITY - field_city.value(); + return EPAR_MAX_CITY - field_city.value(); } void EPARView::flip_relays() { - // Invert first relay's state - relay_states[0].set_selected_index(relay_states[0].selected_index() ^ 1); + // Invert first relay's state + relay_states[0].set_selected_index(relay_states[0].selected_index() ^ 1); } size_t EPARView::generate_message() { - // R2, then R1 - return gen_message_ep(field_city.value(), field_group.selected_index_value(), - half ? 0 : 1, relay_states[half].selected_index()); + // R2, then R1 + return gen_message_ep(field_city.value(), field_group.selected_index_value(), + half ? 0 : 1, relay_states[half].selected_index()); } EPARView::EPARView( - Rect parent_rect -) : View(parent_rect) { - - hidden(true); - - add_children({ - &labels, - &field_city, - &field_group - }); - - field_city.set_value(0); - field_group.set_selected_index(2); - - field_city.on_change = [this](int32_t) { generate_message(); }; - field_group.on_change = [this](size_t, int32_t) { generate_message(); }; - - const auto relay_state_fn = [this](size_t, OptionsField::value_t) { - generate_message(); - }; - - size_t n = 0; - for (auto& relay_state : relay_states) { - relay_state.on_change = relay_state_fn; - relay_state.set_parent_rect({ - static_cast(90 + (n * 36)), - 80, - 24, 24 - }); - relay_state.set_options(relay_options); - add_child(&relay_state); - n++; - } + Rect parent_rect) + : View(parent_rect) { + hidden(true); + + add_children({&labels, + &field_city, + &field_group}); + + field_city.set_value(0); + field_group.set_selected_index(2); + + field_city.on_change = [this](int32_t) { generate_message(); }; + field_group.on_change = [this](size_t, int32_t) { generate_message(); }; + + const auto relay_state_fn = [this](size_t, OptionsField::value_t) { + generate_message(); + }; + + size_t n = 0; + for (auto& relay_state : relay_states) { + relay_state.on_change = relay_state_fn; + relay_state.set_parent_rect({static_cast(90 + (n * 36)), + 80, + 24, 24}); + relay_state.set_options(relay_options); + add_child(&relay_state); + n++; + } } void EPARView::focus() { - field_city.focus(); + field_city.focus(); } void XylosView::flip_relays() { - // Invert first relay's state if not ignored - size_t rs = relay_states[0].selected_index(); - - if (rs > 0) - relay_states[0].set_selected_index(rs ^ 3); + // Invert first relay's state if not ignored + size_t rs = relay_states[0].selected_index(); + + if (rs > 0) + relay_states[0].set_selected_index(rs ^ 3); } bool XylosView::increment_address() { - auto city_code = field_city.value(); - - if (city_code < XY_MAX_CITY) { - field_city.set_value(city_code + 1); - return true; - } else - return false; + auto city_code = field_city.value(); + + if (city_code < XY_MAX_CITY) { + field_city.set_value(city_code + 1); + return true; + } else + return false; } uint32_t XylosView::get_scan_remaining() { - return XY_MAX_CITY - field_city.value(); + return XY_MAX_CITY - field_city.value(); } void XylosView::generate_message() { - gen_message_xy(field_header_a.value(), field_header_b.value(), field_city.value(), field_family.value(), - checkbox_wcsubfamily.value(), field_subfamily.value(), checkbox_wcid.value(), field_receiver.value(), - relay_states[0].selected_index(), relay_states[1].selected_index(), - relay_states[2].selected_index(), relay_states[3].selected_index()); + gen_message_xy(field_header_a.value(), field_header_b.value(), field_city.value(), field_family.value(), + checkbox_wcsubfamily.value(), field_subfamily.value(), checkbox_wcid.value(), field_receiver.value(), + relay_states[0].selected_index(), relay_states[1].selected_index(), + relay_states[2].selected_index(), relay_states[3].selected_index()); } XylosView::XylosView( - Rect parent_rect -) : View(parent_rect) { - - hidden(true); - - add_children({ - &labels, - &field_header_a, - &field_header_b, - &field_city, - &field_family, - &field_subfamily, - &checkbox_wcsubfamily, - &field_receiver, - &checkbox_wcid, - //&button_seq, - }); - - field_header_a.set_value(0); - field_header_b.set_value(0); - field_city.set_value(10); - field_family.set_value(1); - field_subfamily.set_value(1); - field_receiver.set_value(1); - - const auto field_fn = [this](int32_t) { - generate_message(); - }; - - field_header_a.on_change = field_fn; - field_header_b.on_change = [this](int32_t) { generate_message(); }; - field_city.on_change = [this](int32_t) { generate_message(); }; - field_family.on_change = [this](int32_t) { generate_message(); }; - field_subfamily.on_change = [this](int32_t) { generate_message(); }; - field_receiver.on_change = [this](int32_t) { generate_message(); }; - - checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) { - field_subfamily.set_focusable(!v); - generate_message(); - }; - - checkbox_wcid.on_select = [this](Checkbox&, bool v) { - field_receiver.set_focusable(!v); - generate_message(); - }; - - checkbox_wcsubfamily.set_value(true); - checkbox_wcid.set_value(true); - - const auto relay_state_fn = [this](size_t, OptionsField::value_t) { - generate_message(); - }; - - size_t n = 0; - for (auto& relay_state : relay_states) { - relay_state.on_change = relay_state_fn; - relay_state.set_parent_rect({ - static_cast(54 + (n * 36)), - 134, - 24, 24 - }); - relay_state.set_options(relay_options); - add_child(&relay_state); - n++; - } + Rect parent_rect) + : View(parent_rect) { + hidden(true); + + add_children({ + &labels, + &field_header_a, + &field_header_b, + &field_city, + &field_family, + &field_subfamily, + &checkbox_wcsubfamily, + &field_receiver, + &checkbox_wcid, + //&button_seq, + }); + + field_header_a.set_value(0); + field_header_b.set_value(0); + field_city.set_value(10); + field_family.set_value(1); + field_subfamily.set_value(1); + field_receiver.set_value(1); + + const auto field_fn = [this](int32_t) { + generate_message(); + }; + + field_header_a.on_change = field_fn; + field_header_b.on_change = [this](int32_t) { generate_message(); }; + field_city.on_change = [this](int32_t) { generate_message(); }; + field_family.on_change = [this](int32_t) { generate_message(); }; + field_subfamily.on_change = [this](int32_t) { generate_message(); }; + field_receiver.on_change = [this](int32_t) { generate_message(); }; + + checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) { + field_subfamily.set_focusable(!v); + generate_message(); + }; + + checkbox_wcid.on_select = [this](Checkbox&, bool v) { + field_receiver.set_focusable(!v); + generate_message(); + }; + + checkbox_wcsubfamily.set_value(true); + checkbox_wcid.set_value(true); + + const auto relay_state_fn = [this](size_t, OptionsField::value_t) { + generate_message(); + }; + + size_t n = 0; + for (auto& relay_state : relay_states) { + relay_state.on_change = relay_state_fn; + relay_state.set_parent_rect({static_cast(54 + (n * 36)), + 134, + 24, 24}); + relay_state.set_options(relay_options); + add_child(&relay_state); + n++; + } } void XylosView::focus() { - field_city.focus(); + field_city.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_bht_tx.hpp b/firmware/application/apps/ui_bht_tx.hpp index 920d67855..ed69e2ff8 100644 --- a/firmware/application/apps/ui_bht_tx.hpp +++ b/firmware/application/apps/ui_bht_tx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -38,223 +38,200 @@ namespace ui { class XylosView : public View { -public: - XylosView(Rect parent_rect); - - void focus() override; - - void flip_relays(); - void generate_message(); - bool increment_address(); - uint32_t get_scan_remaining(); - -private: - Labels labels { - { { 8 * 8, 1 * 8 }, "Header:", Color::light_grey() }, - { { 4 * 8, 3 * 8 }, "City code:", Color::light_grey() }, - { { 7 * 8, 5 * 8 }, "Family:", Color::light_grey() }, - { { 2 * 8, 7 * 8 + 2 }, "Subfamily:", Color::light_grey() }, - { { 2 * 8, 11 * 8 }, "Receiver ID:", Color::light_grey() }, - { { 2 * 8, 14 * 8 }, "Relay:", Color::light_grey() } - }; - - NumberField field_header_a { - { 16 * 8, 1 * 8 }, - 2, - { 0, 99 }, - 1, - '0' - }; - NumberField field_header_b { - { 18 * 8, 1 * 8 }, - 2, - { 0, 99 }, - 1, - '0' - }; - - NumberField field_city { - { 16 * 8, 3 * 8 }, - 2, - { 0, XY_MAX_CITY }, - 1, - ' ' - }; - - NumberField field_family { - { 16 * 8, 5 * 8 }, - 1, - { 0, 9 }, - 1, - ' ' - }; - - NumberField field_subfamily { - { 16 * 8, 7 * 8 + 2 }, - 1, - { 0, 9 }, - 1, - ' ' - }; - - Checkbox checkbox_wcsubfamily { - { 20 * 8, 6 * 8 + 6 }, - 3, - "All" - }; - - NumberField field_receiver { - { 16 * 8, 11 * 8 }, - 2, - { 0, 99 }, - 1, - '0' - }; - Checkbox checkbox_wcid { - { 20 * 8, 10 * 8 + 4 }, - 3, - "All" - }; - - std::array relay_states { }; - - ImageOptionsField::options_t relay_options = { - { &bitmap_bulb_ignore, 0 }, - { &bitmap_bulb_off, 1 }, - { &bitmap_bulb_on, 2 } - }; + public: + XylosView(Rect parent_rect); + + void focus() override; + + void flip_relays(); + void generate_message(); + bool increment_address(); + uint32_t get_scan_remaining(); + + private: + Labels labels{ + {{8 * 8, 1 * 8}, "Header:", Color::light_grey()}, + {{4 * 8, 3 * 8}, "City code:", Color::light_grey()}, + {{7 * 8, 5 * 8}, "Family:", Color::light_grey()}, + {{2 * 8, 7 * 8 + 2}, "Subfamily:", Color::light_grey()}, + {{2 * 8, 11 * 8}, "Receiver ID:", Color::light_grey()}, + {{2 * 8, 14 * 8}, "Relay:", Color::light_grey()}}; + + NumberField field_header_a{ + {16 * 8, 1 * 8}, + 2, + {0, 99}, + 1, + '0'}; + NumberField field_header_b{ + {18 * 8, 1 * 8}, + 2, + {0, 99}, + 1, + '0'}; + + NumberField field_city{ + {16 * 8, 3 * 8}, + 2, + {0, XY_MAX_CITY}, + 1, + ' '}; + + NumberField field_family{ + {16 * 8, 5 * 8}, + 1, + {0, 9}, + 1, + ' '}; + + NumberField field_subfamily{ + {16 * 8, 7 * 8 + 2}, + 1, + {0, 9}, + 1, + ' '}; + + Checkbox checkbox_wcsubfamily{ + {20 * 8, 6 * 8 + 6}, + 3, + "All"}; + + NumberField field_receiver{ + {16 * 8, 11 * 8}, + 2, + {0, 99}, + 1, + '0'}; + Checkbox checkbox_wcid{ + {20 * 8, 10 * 8 + 4}, + 3, + "All"}; + + std::array relay_states{}; + + ImageOptionsField::options_t relay_options = { + {&bitmap_bulb_ignore, 0}, + {&bitmap_bulb_off, 1}, + {&bitmap_bulb_on, 2}}; }; class EPARView : public View { -public: - EPARView(Rect parent_rect); - - void focus() override; - - void flip_relays(); - size_t generate_message(); - bool increment_address(); - uint32_t get_scan_remaining(); - - bool half { false }; - -private: - Labels labels { - { { 4 * 8, 1 * 8 }, "City code:", Color::light_grey() }, - { { 8 * 8, 3 * 8 }, "Group:", Color::light_grey() }, - { { 8 * 8, 7 * 8 }, "Relay:", Color::light_grey() } - }; - - NumberField field_city { - { 16 * 8, 1 * 8 }, - 3, - { 0, EPAR_MAX_CITY }, - 1, - '0' - }; - - OptionsField field_group { - { 16 * 8, 3 * 8 }, - 2, - { - { "A ", 2 }, // See receiver PCB - { "B ", 1 }, - { "C ", 0 }, - { "TP", 3 } - } - }; - - std::array relay_states { }; - - ImageOptionsField::options_t relay_options = { - { &bitmap_bulb_off, 0 }, - { &bitmap_bulb_on, 1 } - }; + public: + EPARView(Rect parent_rect); + + void focus() override; + + void flip_relays(); + size_t generate_message(); + bool increment_address(); + uint32_t get_scan_remaining(); + + bool half{false}; + + private: + Labels labels{ + {{4 * 8, 1 * 8}, "City code:", Color::light_grey()}, + {{8 * 8, 3 * 8}, "Group:", Color::light_grey()}, + {{8 * 8, 7 * 8}, "Relay:", Color::light_grey()}}; + + NumberField field_city{ + {16 * 8, 1 * 8}, + 3, + {0, EPAR_MAX_CITY}, + 1, + '0'}; + + OptionsField field_group{ + {16 * 8, 3 * 8}, + 2, + {{"A ", 2}, // See receiver PCB + {"B ", 1}, + {"C ", 0}, + {"TP", 3}}}; + + std::array relay_states{}; + + ImageOptionsField::options_t relay_options = { + {&bitmap_bulb_off, 0}, + {&bitmap_bulb_on, 1}}; }; class BHTView : public View { -public: - BHTView(NavigationView& nav); - ~BHTView(); - - void focus() override; - - std::string title() const override { return "BHT TX"; }; - -private: - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - void on_tx_progress(const uint32_t progress, const bool done); - void start_tx(); - void stop_tx(); - - enum target_system_t { - XYLOS = 0, - EPAR = 1 - }; - - target_system_t target_system = { }; - - enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - tx_modes tx_mode = IDLE; - - Rect view_rect = { 0, 3 * 8, 240, 176 }; - - XylosView view_xylos { view_rect }; - EPARView view_EPAR { view_rect }; - - TabView tab_view { - { "Xylos", Color::cyan(), &view_xylos }, - { "EPAR", Color::green(), &view_EPAR } - }; - - Labels labels { - { { 29 * 8, 14 * 16 + 4 }, "s", Color::light_grey() } - }; - - Checkbox checkbox_scan { - { 1 * 8, 25 * 8 }, - 4, - "Scan" - }; - - Checkbox checkbox_flashing { - { 16 * 8, 25 * 8 }, - 8, - "Flashing" - }; - NumberField field_speed { - { 26 * 8, 25 * 8 + 4 }, - 2, - { 1, 99 }, - 1, - ' ' - }; - - ProgressBar progressbar { - { 0 * 8, 29 * 8, 30 * 8, 16 }, - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + BHTView(NavigationView& nav); + ~BHTView(); + + void focus() override; + + std::string title() const override { return "BHT TX"; }; + + private: + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + void on_tx_progress(const uint32_t progress, const bool done); + void start_tx(); + void stop_tx(); + + enum target_system_t { + XYLOS = 0, + EPAR = 1 + }; + + target_system_t target_system = {}; + + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + tx_modes tx_mode = IDLE; + + Rect view_rect = {0, 3 * 8, 240, 176}; + + XylosView view_xylos{view_rect}; + EPARView view_EPAR{view_rect}; + + TabView tab_view{ + {"Xylos", Color::cyan(), &view_xylos}, + {"EPAR", Color::green(), &view_EPAR}}; + + Labels labels{ + {{29 * 8, 14 * 16 + 4}, "s", Color::light_grey()}}; + + Checkbox checkbox_scan{ + {1 * 8, 25 * 8}, + 4, + "Scan"}; + + Checkbox checkbox_flashing{ + {16 * 8, 25 * 8}, + 8, + "Flashing"}; + NumberField field_speed{ + {26 * 8, 25 * 8 + 4}, + 2, + {1, 99}, + 1, + ' '}; + + ProgressBar progressbar{ + {0 * 8, 29 * 8, 30 * 8, 16}, + }; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_btle_rx.cpp b/firmware/application/apps/ui_btle_rx.cpp index 082a016a8..9a31ceb84 100644 --- a/firmware/application/apps/ui_btle_rx.cpp +++ b/firmware/application/apps/ui_btle_rx.cpp @@ -37,128 +37,124 @@ using namespace modems; namespace ui { void BTLERxView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void BTLERxView::update_freq(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } BTLERxView::BTLERxView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_btle_rx); - - add_children({ - &rssi, - &channel, - &field_rf_amp, - &field_lna, - &field_vga, - &field_frequency, - &button_modem_setup, - &console - }); - - // load app settings - auto rc = settings.load("rx_btle", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - - // Auto-configure modem for LCR RX (will be removed later) - update_freq(2426000000); - auto def_bell202 = &modem_defs[0]; - persistent_memory::set_modem_baudrate(def_bell202->baudrate); - serial_format_t serial_format; - serial_format.data_bits = 7; - serial_format.parity = EVEN; - serial_format.stop_bits = 1; - serial_format.bit_order = LSB_FIRST; - persistent_memory::set_serial_format(serial_format); - - field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(100); - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - field_frequency.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - button_modem_setup.on_select = [&nav](Button&) { - nav.push(); - }; - - // Auto-configure modem for LCR RX (will be removed later) - baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false); - - audio::set_rate(audio::Rate::Hz_24000); - audio::output::start(); - - receiver_model.set_sampling_rate(4000000); - receiver_model.set_baseband_bandwidth(4000000); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - receiver_model.enable(); + baseband::run_image(portapack::spi_flash::image_tag_btle_rx); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &button_modem_setup, + &console}); + + // load app settings + auto rc = settings.load("rx_btle", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } + + // Auto-configure modem for LCR RX (will be removed later) + update_freq(2426000000); + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); + }; + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(4000000); + receiver_model.set_baseband_bandwidth(4000000); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.enable(); } void BTLERxView::on_data(uint32_t value, bool is_data) { - //std::string str_console = "\x1B"; - std::string str_console = ""; - if (is_data) { - // Colorize differently after message splits - //str_console += (char)((console_color & 3) + 9); - - //value &= 0xFF; // ABCDEFGH - //value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD - //value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB - //value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA - //value &= 0x7F; // Ignore parity, which is the MSB now - - //if ((value >= 32) && (value < 127)) { - // str_console += (char)value; // Printable - //} - - //str_console += (char)'A'; - //str_console += (char)value; - //str_console += "[" + to_string_hex(value, 2) + "]"; - str_console += ":" + to_string_hex(value, 2) ; - console.write(str_console); - - - - /*if ((value != 0x7F) && (prev_value == 0x7F)) { - // Message split - console.writeln(""); - console_color++; - - - }*/ - //prev_value = value; - } else { - // Baudrate estimation - //text_debug.set("~" + to_string_dec_uint(value)); - if (value == 'A') - {console.write("mac");} - else if (value == 'B') - {console.writeln("");} - //console.writeln(""); - } + // std::string str_console = "\x1B"; + std::string str_console = ""; + if (is_data) { + // Colorize differently after message splits + // str_console += (char)((console_color & 3) + 9); + + // value &= 0xFF; // ABCDEFGH + // value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + // value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + // value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + // value &= 0x7F; // Ignore parity, which is the MSB now + + // if ((value >= 32) && (value < 127)) { + // str_console += (char)value; // Printable + // } + + // str_console += (char)'A'; + // str_console += (char)value; + // str_console += "[" + to_string_hex(value, 2) + "]"; + str_console += ":" + to_string_hex(value, 2); + console.write(str_console); + + /*if ((value != 0x7F) && (prev_value == 0x7F)) { + // Message split + console.writeln(""); + console_color++; + + + }*/ + // prev_value = value; + } else { + // Baudrate estimation + // text_debug.set("~" + to_string_dec_uint(value)); + if (value == 'A') { + console.write("mac"); + } else if (value == 'B') { + console.writeln(""); + } + // console.writeln(""); + } } BTLERxView::~BTLERxView() { + // save app settings + app_settings.rx_frequency = field_frequency.value(); + settings.save("rx_btle", &app_settings); - // save app settings - app_settings.rx_frequency = field_frequency.value(); - settings.save("rx_btle", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_btle_rx.hpp b/firmware/application/apps/ui_btle_rx.hpp index 088661a2b..0eb2b6d14 100644 --- a/firmware/application/apps/ui_btle_rx.hpp +++ b/firmware/application/apps/ui_btle_rx.hpp @@ -28,73 +28,66 @@ #include "ui_navigation.hpp" #include "ui_receiver.hpp" #include "app_settings.hpp" -#include "ui_record_view.hpp" // DEBUG +#include "ui_record_view.hpp" // DEBUG #include "utility.hpp" namespace ui { class BTLERxView : public View { -public: - BTLERxView(NavigationView& nav); - ~BTLERxView(); - - void focus() override; - - std::string title() const override { return "BTLE RX"; }; - -private: - void on_data(uint32_t value, bool is_data); - - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - uint8_t console_color { 0 }; - uint32_t prev_value { 0 }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 16 }, - }; - - Button button_modem_setup { - { 240 - 12 * 8, 1 * 16, 96, 24 }, - "Modem setup" - }; - - Console console { - { 0, 4 * 16, 240, 240 } - }; - - void update_freq(rf::Frequency f); - //void on_data_afsk(const AFSKDataMessage& message); - - MessageHandlerRegistration message_handler_packet { - Message::ID::AFSKData, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_data(message->value, message->is_data); - } - }; + public: + BTLERxView(NavigationView& nav); + ~BTLERxView(); + + void focus() override; + + std::string title() const override { return "BTLE RX"; }; + + private: + void on_data(uint32_t value, bool is_data); + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + uint8_t console_color{0}; + uint32_t prev_value{0}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 16}, + }; + + Button button_modem_setup{ + {240 - 12 * 8, 1 * 16, 96, 24}, + "Modem setup"}; + + Console console{ + {0, 4 * 16, 240, 240}}; + + void update_freq(rf::Frequency f); + // void on_data_afsk(const AFSKDataMessage& message); + + MessageHandlerRegistration message_handler_packet{ + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + }}; }; } /* namespace ui */ -#endif/*__UI_BTLE_RX_H__*/ +#endif /*__UI_BTLE_RX_H__*/ diff --git a/firmware/application/apps/ui_coasterp.cpp b/firmware/application/apps/ui_coasterp.cpp index bbe1871b8..f1f1ff505 100644 --- a/firmware/application/apps/ui_coasterp.cpp +++ b/firmware/application/apps/ui_coasterp.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -34,136 +34,134 @@ using namespace portapack; namespace ui { void CoasterPagerView::focus() { - sym_data.focus(); + sym_data.focus(); } CoasterPagerView::~CoasterPagerView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_coaster", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_coaster", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void CoasterPagerView::generate_frame() { - uint8_t frame[19]; - uint32_t c; - - // Preamble (8 bytes) - for (c = 0; c < 8; c++) - frame[c] = 0x55; // Isn't this 0xAA ? - - // Sync word - frame[8] = 0x2D; - frame[9] = 0xD4; - - // Data length - frame[10] = 8; - - // Data - for (c = 0; c < 8; c++) - frame[c + 11] = (sym_data.get_sym(c * 2) << 4) | sym_data.get_sym(c * 2 + 1); - - // Copy for baseband - memcpy(shared_memory.bb_data.data, frame, 19); + uint8_t frame[19]; + uint32_t c; + + // Preamble (8 bytes) + for (c = 0; c < 8; c++) + frame[c] = 0x55; // Isn't this 0xAA ? + + // Sync word + frame[8] = 0x2D; + frame[9] = 0xD4; + + // Data length + frame[10] = 8; + + // Data + for (c = 0; c < 8; c++) + frame[c + 11] = (sym_data.get_sym(c * 2) << 4) | sym_data.get_sym(c * 2 + 1); + + // Copy for baseband + memcpy(shared_memory.bb_data.data, frame, 19); } void CoasterPagerView::start_tx() { - generate_frame(); - - transmitter_model.set_sampling_rate(2280000); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); + generate_frame(); + + transmitter_model.set_sampling_rate(2280000); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); - baseband::set_fsk_data(19 * 8, 2280000 / 1000, 5000, 32); + baseband::set_fsk_data(19 * 8, 2280000 / 1000, 5000, 32); } void CoasterPagerView::on_tx_progress(const uint32_t progress, const bool done) { - (void)progress; - - uint16_t address = 0; - uint32_t c; - - if (done) { - if (tx_mode == SINGLE) { - transmitter_model.disable(); - tx_mode = IDLE; - tx_view.set_transmitting(false); - } else if (tx_mode == SCAN) { - // Increment address - - for (c = 0; c < 4; c++) { - address <<= 4; - address |= sym_data.get_sym(12 + c); - } - - address++; - - for (c = 0; c < 4; c++) { - sym_data.set_sym(15 - c, address & 0x0F); - address >>= 4; - } - - start_tx(); - } - } + (void)progress; + + uint16_t address = 0; + uint32_t c; + + if (done) { + if (tx_mode == SINGLE) { + transmitter_model.disable(); + tx_mode = IDLE; + tx_view.set_transmitting(false); + } else if (tx_mode == SCAN) { + // Increment address + + for (c = 0; c < 4; c++) { + address <<= 4; + address |= sym_data.get_sym(12 + c); + } + + address++; + + for (c = 0; c < 4; c++) { + sym_data.set_sym(15 - c, address & 0x0F); + address >>= 4; + } + + start_tx(); + } + } } CoasterPagerView::CoasterPagerView(NavigationView& nav) { - const uint8_t data_init[8] = { 0x44, 0x01, 0x3B, 0x30, 0x30, 0x30, 0x34, 0xBC }; - uint32_t c; - - baseband::run_image(portapack::spi_flash::image_tag_fsktx); - - add_children({ - &labels, - &sym_data, - &checkbox_scan, - &text_message, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_coaster", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - // Bytes to nibbles - for (c = 0; c < 16; c++) - sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F); - - checkbox_scan.set_value(false); - - generate_frame(); - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (tx_mode == IDLE) { - if (checkbox_scan.value()) - tx_mode = SCAN; - else - tx_mode = SINGLE; - tx_view.set_transmitting(true); - start_tx(); - } - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - tx_mode = IDLE; - }; + const uint8_t data_init[8] = {0x44, 0x01, 0x3B, 0x30, 0x30, 0x30, 0x34, 0xBC}; + uint32_t c; + + baseband::run_image(portapack::spi_flash::image_tag_fsktx); + + add_children({&labels, + &sym_data, + &checkbox_scan, + &text_message, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_coaster", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + // Bytes to nibbles + for (c = 0; c < 16; c++) + sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F); + + checkbox_scan.set_value(false); + + generate_frame(); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (tx_mode == IDLE) { + if (checkbox_scan.value()) + tx_mode = SCAN; + else + tx_mode = SINGLE; + tx_view.set_transmitting(true); + start_tx(); + } + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + tx_mode = IDLE; + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_coasterp.hpp b/firmware/application/apps/ui_coasterp.hpp index af6a27b48..59a70f747 100644 --- a/firmware/application/apps/ui_coasterp.hpp +++ b/firmware/application/apps/ui_coasterp.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -34,69 +34,63 @@ namespace ui { class CoasterPagerView : public View { -public: - CoasterPagerView(NavigationView& nav); - ~CoasterPagerView(); - - void focus() override; - - std::string title() const override { return "BurgerPgrTX"; }; - -private: - enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - tx_modes tx_mode = IDLE; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - void start_tx(); - void generate_frame(); - void on_tx_progress(const uint32_t progress, const bool done); - - Labels labels { - { { 1 * 8, 3 * 8 }, "Syscall pager TX beta", Color::light_grey() }, - { { 1 * 8, 8 * 8 }, "Data:", Color::light_grey() } - }; - - SymField sym_data { - { 7 * 8, 8 * 8 }, - 16, // 14 ? 12 ? - SymField::SYMFIELD_HEX - }; - - Checkbox checkbox_scan { - { 10 * 8, 14 * 8 }, - 4, - "Scan" - }; - - /*ProgressBar progressbar { - { 5 * 8, 12 * 16, 20 * 8, 16 }, - };*/ - Text text_message { - { 5 * 8, 13 * 16, 20 * 8, 16 }, - "" - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + CoasterPagerView(NavigationView& nav); + ~CoasterPagerView(); + + void focus() override; + + std::string title() const override { return "BurgerPgrTX"; }; + + private: + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + tx_modes tx_mode = IDLE; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + void start_tx(); + void generate_frame(); + void on_tx_progress(const uint32_t progress, const bool done); + + Labels labels{ + {{1 * 8, 3 * 8}, "Syscall pager TX beta", Color::light_grey()}, + {{1 * 8, 8 * 8}, "Data:", Color::light_grey()}}; + + SymField sym_data{ + {7 * 8, 8 * 8}, + 16, // 14 ? 12 ? + SymField::SYMFIELD_HEX}; + + Checkbox checkbox_scan{ + {10 * 8, 14 * 8}, + 4, + "Scan"}; + + /*ProgressBar progressbar { + { 5 * 8, 12 * 16, 20 * 8, 16 }, + };*/ + Text text_message{ + {5 * 8, 13 * 16, 20 * 8, 16}, + ""}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 79866b709..552511f67 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -41,395 +41,375 @@ namespace ui { /* DebugMemoryView *******************************************************/ DebugMemoryView::DebugMemoryView(NavigationView& nav) { - add_children({ - &text_title, - &text_label_m0_core_free, - &text_label_m0_core_free_value, - &text_label_m0_heap_fragmented_free, - &text_label_m0_heap_fragmented_free_value, - &text_label_m0_heap_fragments, - &text_label_m0_heap_fragments_value, - &button_done - }); - - const auto m0_core_free = chCoreStatus(); - text_label_m0_core_free_value.set(to_string_dec_uint(m0_core_free, 5)); - - size_t m0_fragmented_free_space = 0; - const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space); - text_label_m0_heap_fragmented_free_value.set(to_string_dec_uint(m0_fragmented_free_space, 5)); - text_label_m0_heap_fragments_value.set(to_string_dec_uint(m0_fragments, 5)); - - button_done.on_select = [&nav](Button&){ nav.pop(); }; + add_children({&text_title, + &text_label_m0_core_free, + &text_label_m0_core_free_value, + &text_label_m0_heap_fragmented_free, + &text_label_m0_heap_fragmented_free_value, + &text_label_m0_heap_fragments, + &text_label_m0_heap_fragments_value, + &button_done}); + + const auto m0_core_free = chCoreStatus(); + text_label_m0_core_free_value.set(to_string_dec_uint(m0_core_free, 5)); + + size_t m0_fragmented_free_space = 0; + const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space); + text_label_m0_heap_fragmented_free_value.set(to_string_dec_uint(m0_fragmented_free_space, 5)); + text_label_m0_heap_fragments_value.set(to_string_dec_uint(m0_fragments, 5)); + + button_done.on_select = [&nav](Button&) { nav.pop(); }; } void DebugMemoryView::focus() { - button_done.focus(); + button_done.focus(); } /* TemperatureWidget *****************************************************/ void TemperatureWidget::paint(Painter& painter) { - const auto logger = portapack::temperature_logger; - - const auto rect = screen_rect(); - const Color color_background { 0, 0, 64 }; - const Color color_foreground = Color::green(); - const Color color_reticle { 128, 128, 128 }; - - const auto graph_width = static_cast(logger.capacity()) * bar_width; - const Rect graph_rect { - rect.left() + (rect.width() - graph_width) / 2, rect.top() + 8, - graph_width, rect.height() - }; - const Rect frame_rect { - graph_rect.left() - 1, graph_rect.top() - 1, - graph_rect.width() + 2, graph_rect.height() + 2 - }; - painter.draw_rectangle(frame_rect, color_reticle); - painter.fill_rectangle(graph_rect, color_background); - - const auto history = logger.history(); - for(size_t i=0; i(logger.capacity()) * bar_width; + const Rect graph_rect{ + rect.left() + (rect.width() - graph_width) / 2, rect.top() + 8, + graph_width, rect.height()}; + const Rect frame_rect{ + graph_rect.left() - 1, graph_rect.top() - 1, + graph_rect.width() + 2, graph_rect.height() + 2}; + painter.draw_rectangle(frame_rect, color_reticle); + painter.fill_rectangle(graph_rect, color_background); + + const auto history = logger.history(); + for (size_t i = 0; i < history.size(); i++) { + const Coord x = graph_rect.right() - (history.size() - i) * bar_width; + const auto sample = history[i]; + const auto temp = temperature(sample); + const auto y = screen_y(temp, graph_rect); + const Dim bar_height = graph_rect.bottom() - y; + painter.fill_rectangle({x, y, bar_width, bar_height}, color_foreground); + } + + if (!history.empty()) { + const auto sample = history.back(); + const auto temp = temperature(sample); + const auto last_y = screen_y(temp, graph_rect); + const Coord x = graph_rect.right() + 8; + const Coord y = last_y - 8; + + painter.draw_string({x, y}, style(), temperature_str(temp)); + } + + const auto display_temp_max = display_temp_min + (graph_rect.height() / display_temp_scale); + for (auto temp = display_temp_min; temp <= display_temp_max; temp += 10) { + const int32_t tick_length = 6; + const auto tick_x = graph_rect.left() - tick_length; + const auto tick_y = screen_y(temp, graph_rect); + painter.fill_rectangle({tick_x, tick_y, tick_length, 1}, color_reticle); + const auto text_x = graph_rect.left() - temp_len * 8 - 8; + const auto text_y = tick_y - 8; + painter.draw_string({text_x, text_y}, style(), temperature_str(temp)); + } } TemperatureWidget::temperature_t TemperatureWidget::temperature(const sample_t sensor_value) const { - /*It seems to be a temperature difference of 25C*/ - return -40 +(sensor_value * 4.31)+25; //max2837 datasheet temp 25ºC has sensor value: 15 + /*It seems to be a temperature difference of 25C*/ + return -40 + (sensor_value * 4.31) + 25; // max2837 datasheet temp 25ºC has sensor value: 15 } std::string TemperatureWidget::temperature_str(const temperature_t temperature) const { - return to_string_dec_int(temperature, temp_len - 1) + "C"; + return to_string_dec_int(temperature, temp_len - 1) + "C"; } Coord TemperatureWidget::screen_y( - const temperature_t temperature, - const Rect& rect -) const { - int y_raw = rect.bottom() - ((temperature - display_temp_min) * display_temp_scale); - const auto y_limit = std::min(rect.bottom(), std::max(rect.top(), y_raw)); - return y_limit; + const temperature_t temperature, + const Rect& rect) const { + int y_raw = rect.bottom() - ((temperature - display_temp_min) * display_temp_scale); + const auto y_limit = std::min(rect.bottom(), std::max(rect.top(), y_raw)); + return y_limit; } /* TemperatureView *******************************************************/ TemperatureView::TemperatureView(NavigationView& nav) { - add_children({ - &text_title, - &temperature_widget, - &button_done, - }); + add_children({ + &text_title, + &temperature_widget, + &button_done, + }); - button_done.on_select = [&nav](Button&){ nav.pop(); }; + button_done.on_select = [&nav](Button&) { nav.pop(); }; } void TemperatureView::focus() { - button_done.focus(); + button_done.focus(); } /* RegistersWidget *******************************************************/ RegistersWidget::RegistersWidget( - RegistersWidgetConfig&& config, - std::function&& reader -) : Widget { }, - config(std::move(config)), - reader(std::move(reader)) -{ + RegistersWidgetConfig&& config, + std::function&& reader) + : Widget{}, + config(std::move(config)), + reader(std::move(reader)) { } void RegistersWidget::update() { - set_dirty(); + set_dirty(); } void RegistersWidget::paint(Painter& painter) { - const Coord left = (size().width() - config.row_width()) / 2; + const Coord left = (size().width() - config.row_width()) / 2; - draw_legend(left, painter); - draw_values(left, painter); + draw_legend(left, painter); + draw_values(left, painter); } void RegistersWidget::draw_legend(const Coord left, Painter& painter) { - const auto pos = screen_pos(); - - for(size_t i=0; i((i / config.registers_per_row()) * row_height) - }; - - const auto text = to_string_hex(i, config.legend_length()); - painter.draw_string( - pos + offset, - style().invert(), - text - ); - } + const auto pos = screen_pos(); + + for (size_t i = 0; i < config.registers_count; i += config.registers_per_row()) { + const Point offset{ + left, static_cast((i / config.registers_per_row()) * row_height)}; + + const auto text = to_string_hex(i, config.legend_length()); + painter.draw_string( + pos + offset, + style().invert(), + text); + } } void RegistersWidget::draw_values( - const Coord left, - Painter& painter -) { - const auto pos = screen_pos(); - - for(size_t i=0; i(left + config.legend_width() + 8 + (i % config.registers_per_row()) * (config.value_width() + 8)), - static_cast((i / config.registers_per_row()) * row_height) - }; - - const auto value = reader(i); - - const auto text = to_string_hex(value, config.value_length()); - painter.draw_string( - pos + offset, - style(), - text - ); - } + const Coord left, + Painter& painter) { + const auto pos = screen_pos(); + + for (size_t i = 0; i < config.registers_count; i++) { + const Point offset = { + static_cast(left + config.legend_width() + 8 + (i % config.registers_per_row()) * (config.value_width() + 8)), + static_cast((i / config.registers_per_row()) * row_height)}; + + const auto value = reader(i); + + const auto text = to_string_hex(value, config.value_length()); + painter.draw_string( + pos + offset, + style(), + text); + } } /* RegistersView *********************************************************/ RegistersView::RegistersView( - NavigationView& nav, - const std::string& title, - RegistersWidgetConfig&& config, - std::function&& reader -) : registers_widget { std::move(config), std::move(reader) } -{ - add_children({ - &text_title, - ®isters_widget, - &button_update, - &button_done, - }); - - button_update.on_select = [this](Button&){ - this->registers_widget.update(); - }; - button_done.on_select = [&nav](Button&){ nav.pop(); }; - - registers_widget.set_parent_rect({ 0, 48, 240, 192 }); - - text_title.set_parent_rect({ - (240 - static_cast(title.size()) * 8) / 2, 16, - static_cast(title.size()) * 8, 16 - }); - text_title.set(title); + NavigationView& nav, + const std::string& title, + RegistersWidgetConfig&& config, + std::function&& reader) + : registers_widget{std::move(config), std::move(reader)} { + add_children({ + &text_title, + ®isters_widget, + &button_update, + &button_done, + }); + + button_update.on_select = [this](Button&) { + this->registers_widget.update(); + }; + button_done.on_select = [&nav](Button&) { nav.pop(); }; + + registers_widget.set_parent_rect({0, 48, 240, 192}); + + text_title.set_parent_rect({(240 - static_cast(title.size()) * 8) / 2, 16, + static_cast(title.size()) * 8, 16}); + text_title.set(title); } void RegistersView::focus() { - button_done.focus(); + button_done.focus(); } /* ControlsSwitchesWidget ************************************************/ void ControlsSwitchesWidget::on_show() { - display.fill_rectangle( - screen_rect(), - Color::black() - ); + display.fill_rectangle( + screen_rect(), + Color::black()); } bool ControlsSwitchesWidget::on_key(const KeyEvent key) { - key_event_mask = 1 << toUType(key); - return true; + key_event_mask = 1 << toUType(key); + return true; } void ControlsSwitchesWidget::paint(Painter& painter) { - const auto pos = screen_pos(); - - const std::array button_rects { { - { 64, 32, 16, 16 }, // Right - { 0, 32, 16, 16 }, // Left - { 32, 64, 16, 16 }, // Down - { 32, 0, 16, 16 }, // Up - { 32, 32, 16, 16 }, // Select - { 16, 96, 16, 16 }, // Encoder phase 0 - { 48, 96, 16, 16 }, // Encoder phase 1 - { 96, 0, 16, 16 }, // Dfu - } }; - - for(const auto r : button_rects) { - painter.fill_rectangle(r + pos, Color::blue()); - } - - const std::array raw_rects { { - { 64 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Right - { 0 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Left - { 32 + 1, 64 + 1, 16 - 2, 16 - 2 }, // Down - { 32 + 1, 0 + 1, 16 - 2, 16 - 2 }, // Up - { 32 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Select - { 16 + 1, 96 + 1, 16 - 2, 16 - 2 }, // Encoder phase 0 - { 48 + 1, 96 + 1, 16 - 2, 16 - 2 }, // Encoder phase 1 - { 96 + 1, 0 + 1, 16 - 2, 16 - 2 }, // Dfu - } }; - - auto switches_raw = control::debug::switches(); - for(const auto r : raw_rects) { - if (switches_raw & 1) - painter.fill_rectangle(r + pos, Color::yellow()); - - switches_raw >>= 1; - } - - const std::array debounced_rects { { - { 64 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Right - { 0 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Left - { 32 + 2, 64 + 2, 16 - 4, 16 - 4 }, // Down - { 32 + 2, 0 + 2, 16 - 4, 16 - 4 }, // Up - { 32 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Select - { 96 + 2, 0 + 2, 16 - 4, 16 - 4 }, // Dfu - } }; - - auto switches_debounced = get_switches_state().to_ulong(); - for(const auto r : debounced_rects) { - if (switches_debounced & 1) - painter.fill_rectangle(r + pos, Color::green()); - - switches_debounced >>= 1; - } - - const std::array events_rects { { - { 64 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Right - { 0 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Left - { 32 + 3, 64 + 3, 16 - 6, 16 - 6 }, // Down - { 32 + 3, 0 + 3, 16 - 6, 16 - 6 }, // Up - { 32 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Select - { 96 + 3, 0 + 3, 16 - 6, 16 - 6 }, // Dfu - } }; - - auto switches_event = key_event_mask; - for(const auto r : events_rects) { - if (switches_event & 1) - painter.fill_rectangle(r + pos, Color::red()); - - switches_event >>= 1; - } + const auto pos = screen_pos(); + + const std::array button_rects{{ + {64, 32, 16, 16}, // Right + {0, 32, 16, 16}, // Left + {32, 64, 16, 16}, // Down + {32, 0, 16, 16}, // Up + {32, 32, 16, 16}, // Select + {16, 96, 16, 16}, // Encoder phase 0 + {48, 96, 16, 16}, // Encoder phase 1 + {96, 0, 16, 16}, // Dfu + }}; + + for (const auto r : button_rects) { + painter.fill_rectangle(r + pos, Color::blue()); + } + + const std::array raw_rects{{ + {64 + 1, 32 + 1, 16 - 2, 16 - 2}, // Right + {0 + 1, 32 + 1, 16 - 2, 16 - 2}, // Left + {32 + 1, 64 + 1, 16 - 2, 16 - 2}, // Down + {32 + 1, 0 + 1, 16 - 2, 16 - 2}, // Up + {32 + 1, 32 + 1, 16 - 2, 16 - 2}, // Select + {16 + 1, 96 + 1, 16 - 2, 16 - 2}, // Encoder phase 0 + {48 + 1, 96 + 1, 16 - 2, 16 - 2}, // Encoder phase 1 + {96 + 1, 0 + 1, 16 - 2, 16 - 2}, // Dfu + }}; + + auto switches_raw = control::debug::switches(); + for (const auto r : raw_rects) { + if (switches_raw & 1) + painter.fill_rectangle(r + pos, Color::yellow()); + + switches_raw >>= 1; + } + + const std::array debounced_rects{{ + {64 + 2, 32 + 2, 16 - 4, 16 - 4}, // Right + {0 + 2, 32 + 2, 16 - 4, 16 - 4}, // Left + {32 + 2, 64 + 2, 16 - 4, 16 - 4}, // Down + {32 + 2, 0 + 2, 16 - 4, 16 - 4}, // Up + {32 + 2, 32 + 2, 16 - 4, 16 - 4}, // Select + {96 + 2, 0 + 2, 16 - 4, 16 - 4}, // Dfu + }}; + + auto switches_debounced = get_switches_state().to_ulong(); + for (const auto r : debounced_rects) { + if (switches_debounced & 1) + painter.fill_rectangle(r + pos, Color::green()); + + switches_debounced >>= 1; + } + + const std::array events_rects{{ + {64 + 3, 32 + 3, 16 - 6, 16 - 6}, // Right + {0 + 3, 32 + 3, 16 - 6, 16 - 6}, // Left + {32 + 3, 64 + 3, 16 - 6, 16 - 6}, // Down + {32 + 3, 0 + 3, 16 - 6, 16 - 6}, // Up + {32 + 3, 32 + 3, 16 - 6, 16 - 6}, // Select + {96 + 3, 0 + 3, 16 - 6, 16 - 6}, // Dfu + }}; + + auto switches_event = key_event_mask; + for (const auto r : events_rects) { + if (switches_event & 1) + painter.fill_rectangle(r + pos, Color::red()); + + switches_event >>= 1; + } } void ControlsSwitchesWidget::on_frame_sync() { - set_dirty(); + set_dirty(); } /* DebugControlsView *****************************************************/ DebugControlsView::DebugControlsView(NavigationView& nav) { - add_children({ - &text_title, - &switches_widget, - &button_done, - }); + add_children({ + &text_title, + &switches_widget, + &button_done, + }); - button_done.on_select = [&nav](Button&){ nav.pop(); }; + button_done.on_select = [&nav](Button&) { nav.pop(); }; } void DebugControlsView::focus() { - switches_widget.focus(); + switches_widget.focus(); } /* DebugPeripheralsMenuView **********************************************/ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) { - const char * max283x = hackrf_r9 ? "MAX2839" : "MAX2837"; - const char * si5351x = hackrf_r9 ? "Si5351A" : "Si5351C"; - add_items({ - { "RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push( - "RFFC5072", RegistersWidgetConfig { 31, 16 }, - [](const size_t register_number) { return radio::debug::first_if::register_read(register_number); } - ); } }, - { max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push( - max283x, RegistersWidgetConfig { 32, 10 }, - [](const size_t register_number) { return radio::debug::second_if::register_read(register_number); } - ); } }, - { si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push( - si5351x, RegistersWidgetConfig { 96, 8 }, - [](const size_t register_number) { return portapack::clock_generator.read_register(register_number); } - ); } }, - { audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push( - audio::debug::codec_name(), RegistersWidgetConfig { audio::debug::reg_count(), audio::debug::reg_bits() }, - [](const size_t register_number) { return audio::debug::reg_read(register_number); } - ); } }, - }); - set_max_rows(2); // allow wider buttons + const char* max283x = hackrf_r9 ? "MAX2839" : "MAX2837"; + const char* si5351x = hackrf_r9 ? "Si5351A" : "Si5351C"; + add_items({ + {"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push( + "RFFC5072", RegistersWidgetConfig{31, 16}, + [](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }); }}, + {max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push( + max283x, RegistersWidgetConfig{32, 10}, + [](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }); }}, + {si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push( + si5351x, RegistersWidgetConfig{96, 8}, + [](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }); }}, + {audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push( + audio::debug::codec_name(), RegistersWidgetConfig{audio::debug::reg_count(), audio::debug::reg_bits()}, + [](const size_t register_number) { return audio::debug::reg_read(register_number); }); }}, + }); + set_max_rows(2); // allow wider buttons } /* DebugMenuView *********************************************************/ DebugMenuView::DebugMenuView(NavigationView& nav) { - if( portapack::persistent_memory::show_gui_return_icon() ) - { - add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } ); + if (portapack::persistent_memory::show_gui_return_icon()) { + add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } - add_items({ - { "Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav](){ nav.push(); } }, - //{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push(); } }, - { "SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav](){ nav.push(); } }, - { "Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav](){ nav.push(); } }, - { "Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav](){ nav.push(); } }, - { "Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav](){ nav.push(); } }, - }); - set_max_rows(2); // allow wider buttons + add_items({ + {"Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push(); }}, + //{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push(); } }, + {"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push(); }}, + {"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push(); }}, + {"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push(); }}, + {"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push(); }}, + }); + set_max_rows(2); // allow wider buttons } /*DebugLCRView::DebugLCRView(NavigationView& nav, std::string lcr_string) { - - std::string debug_text; - - add_children({ - &console, - &button_exit - }); - - for(const auto c : lcr_string) { - if ((c < 32) || (c > 126)) - debug_text += "[" + to_string_dec_uint(c) + "]"; - else - debug_text += c; - } - - debug_text += "\n\n"; - debug_text += "Length: " + to_string_dec_uint(lcr_string.length()) + '\n'; - debug_text += "Checksum: " + to_string_dec_uint(lcr_string.back()) + '\n'; - - console.write(debug_text); - - button_exit.on_select = [this, &nav](Button&){ - nav.pop(); - }; + + std::string debug_text; + + add_children({ + &console, + &button_exit + }); + + for(const auto c : lcr_string) { + if ((c < 32) || (c > 126)) + debug_text += "[" + to_string_dec_uint(c) + "]"; + else + debug_text += c; + } + + debug_text += "\n\n"; + debug_text += "Length: " + to_string_dec_uint(lcr_string.length()) + '\n'; + debug_text += "Checksum: " + to_string_dec_uint(lcr_string.back()) + '\n'; + + console.write(debug_text); + + button_exit.on_select = [this, &nav](Button&){ + nav.pop(); + }; } - + void DebugLCRView::focus() { - button_exit.focus(); + button_exit.focus(); }*/ } /* namespace ui */ diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 43acc8883..b2a593b6b 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -37,273 +37,263 @@ namespace ui { class DebugMemoryView : public View { -public: - DebugMemoryView(NavigationView& nav); + public: + DebugMemoryView(NavigationView& nav); - void focus() override; + void focus() override; - std::string title() const override { return "Memory"; }; + std::string title() const override { return "Memory"; }; -private: - Text text_title { - { 96, 96, 48, 16 }, - "Memory", - }; - - Text text_label_m0_core_free { - { 0, 128, 144, 16 }, - "M0 Core Free Bytes", - }; - - Text text_label_m0_core_free_value { - { 200, 128, 40, 16 }, - }; - - Text text_label_m0_heap_fragmented_free { - { 0, 144, 184, 16 }, - "M0 Heap Fragmented Free", - }; - - Text text_label_m0_heap_fragmented_free_value { - { 200, 144, 40, 16 }, - }; - - Text text_label_m0_heap_fragments { - { 0, 160, 136, 16 }, - "M0 Heap Fragments", - }; - - Text text_label_m0_heap_fragments_value { - { 200, 160, 40, 16 }, - }; - - Button button_done { - { 72, 192, 96, 24 }, - "Done" - }; + private: + Text text_title{ + {96, 96, 48, 16}, + "Memory", + }; + + Text text_label_m0_core_free{ + {0, 128, 144, 16}, + "M0 Core Free Bytes", + }; + + Text text_label_m0_core_free_value{ + {200, 128, 40, 16}, + }; + + Text text_label_m0_heap_fragmented_free{ + {0, 144, 184, 16}, + "M0 Heap Fragmented Free", + }; + + Text text_label_m0_heap_fragmented_free_value{ + {200, 144, 40, 16}, + }; + + Text text_label_m0_heap_fragments{ + {0, 160, 136, 16}, + "M0 Heap Fragments", + }; + + Text text_label_m0_heap_fragments_value{ + {200, 160, 40, 16}, + }; + + Button button_done{ + {72, 192, 96, 24}, + "Done"}; }; class TemperatureWidget : public Widget { -public: - explicit TemperatureWidget( - Rect parent_rect - ) : Widget { parent_rect } - { - } + public: + explicit TemperatureWidget( + Rect parent_rect) + : Widget{parent_rect} { + } - void paint(Painter& painter) override; + void paint(Painter& painter) override; -private: - using sample_t = uint32_t; - using temperature_t = int32_t; + private: + using sample_t = uint32_t; + using temperature_t = int32_t; - temperature_t temperature(const sample_t sensor_value) const; - Coord screen_y(const temperature_t temperature, const Rect& screen_rect) const; + temperature_t temperature(const sample_t sensor_value) const; + Coord screen_y(const temperature_t temperature, const Rect& screen_rect) const; - std::string temperature_str(const temperature_t temperature) const; + std::string temperature_str(const temperature_t temperature) const; - static constexpr temperature_t display_temp_min = -10; //Accomodate negative values, present in cold startup cases - static constexpr temperature_t display_temp_scale = 3; - static constexpr int bar_width = 1; - static constexpr int temp_len = 4; //Now scale shows up to 4 chars ("-10C") + static constexpr temperature_t display_temp_min = -10; // Accomodate negative values, present in cold startup cases + static constexpr temperature_t display_temp_scale = 3; + static constexpr int bar_width = 1; + static constexpr int temp_len = 4; // Now scale shows up to 4 chars ("-10C") }; class TemperatureView : public View { -public: - explicit TemperatureView(NavigationView& nav); + public: + explicit TemperatureView(NavigationView& nav); - void focus() override; + void focus() override; - std::string title() const override { return "Temperature"; }; + std::string title() const override { return "Temperature"; }; -private: - Text text_title { - { 76, 16, 240, 16 }, - "Temperature", - }; - - TemperatureWidget temperature_widget { - { 0, 40, 240, 180 }, - }; - - Button button_done { - { 72, 264, 96, 24 }, - "Done" - }; + private: + Text text_title{ + {76, 16, 240, 16}, + "Temperature", + }; + + TemperatureWidget temperature_widget{ + {0, 40, 240, 180}, + }; + + Button button_done{ + {72, 264, 96, 24}, + "Done"}; }; struct RegistersWidgetConfig { - size_t registers_count; - size_t register_bits; + size_t registers_count; + size_t register_bits; - constexpr size_t legend_length() const { - return (registers_count >= 0x10) ? 2 : 1; - } + constexpr size_t legend_length() const { + return (registers_count >= 0x10) ? 2 : 1; + } - constexpr size_t legend_width() const { - return legend_length() * 8; - } + constexpr size_t legend_width() const { + return legend_length() * 8; + } - constexpr size_t value_length() const { - return (register_bits + 3) / 4; - } + constexpr size_t value_length() const { + return (register_bits + 3) / 4; + } - constexpr size_t value_width() const { - return value_length() * 8; - } + constexpr size_t value_width() const { + return value_length() * 8; + } - constexpr size_t registers_per_row() const { - return (value_length() >= 3) ? 4 : 8; - } + constexpr size_t registers_per_row() const { + return (value_length() >= 3) ? 4 : 8; + } - constexpr size_t registers_row_length() const { - return (registers_per_row() * (value_length() + 1)) - 1; - } + constexpr size_t registers_row_length() const { + return (registers_per_row() * (value_length() + 1)) - 1; + } - constexpr size_t registers_row_width() const { - return registers_row_length() * 8; - } + constexpr size_t registers_row_width() const { + return registers_row_length() * 8; + } - constexpr size_t row_width() const { - return legend_width() + 8 + registers_row_width(); - } + constexpr size_t row_width() const { + return legend_width() + 8 + registers_row_width(); + } - constexpr size_t rows() const { - return registers_count / registers_per_row(); - } + constexpr size_t rows() const { + return registers_count / registers_per_row(); + } }; class RegistersWidget : public Widget { -public: - RegistersWidget( - RegistersWidgetConfig&& config, - std::function&& reader - ); + public: + RegistersWidget( + RegistersWidgetConfig&& config, + std::function&& reader); - void update(); + void update(); - void paint(Painter& painter) override; + void paint(Painter& painter) override; -private: - const RegistersWidgetConfig config; - const std::function reader; + private: + const RegistersWidgetConfig config; + const std::function reader; - static constexpr size_t row_height = 16; + static constexpr size_t row_height = 16; - void draw_legend(const Coord left, Painter& painter); - void draw_values(const Coord left, Painter& painter); + void draw_legend(const Coord left, Painter& painter); + void draw_values(const Coord left, Painter& painter); }; class RegistersView : public View { -public: - RegistersView( - NavigationView& nav, - const std::string& title, - RegistersWidgetConfig&& config, - std::function&& reader - ); + public: + RegistersView( + NavigationView& nav, + const std::string& title, + RegistersWidgetConfig&& config, + std::function&& reader); - void focus(); + void focus(); -private: - Text text_title { }; + private: + Text text_title{}; - RegistersWidget registers_widget; + RegistersWidget registers_widget; - Button button_update { - { 16, 256, 96, 24 }, - "Update" - }; + Button button_update{ + {16, 256, 96, 24}, + "Update"}; - Button button_done { - { 128, 256, 96, 24 }, - "Done" - }; + Button button_done{ + {128, 256, 96, 24}, + "Done"}; }; class ControlsSwitchesWidget : public Widget { -public: - ControlsSwitchesWidget( - Rect parent_rect - ) : Widget { parent_rect }, - key_event_mask(0) - { - set_focusable(true); - } + public: + ControlsSwitchesWidget( + Rect parent_rect) + : Widget{parent_rect}, + key_event_mask(0) { + set_focusable(true); + } - void on_show() override; - bool on_key(const KeyEvent key) override; + void on_show() override; + bool on_key(const KeyEvent key) override; - void paint(Painter& painter) override; + void paint(Painter& painter) override; -private: - uint8_t key_event_mask; + private: + uint8_t key_event_mask; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->on_frame_sync(); - } - }; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->on_frame_sync(); + }}; - void on_frame_sync(); + void on_frame_sync(); }; class DebugControlsView : public View { -public: - explicit DebugControlsView(NavigationView& nav); + public: + explicit DebugControlsView(NavigationView& nav); - void focus() override; + void focus() override; - std::string title() const override { return "Buttons Test"; }; + std::string title() const override { return "Buttons Test"; }; -private: - Text text_title { - { 64, 16, 184, 16 }, - "Controls State", - }; - - ControlsSwitchesWidget switches_widget { - { 80, 80, 80, 112 }, - }; - - Button button_done { - { 72, 264, 96, 24 }, - "Done" - }; + private: + Text text_title{ + {64, 16, 184, 16}, + "Controls State", + }; + + ControlsSwitchesWidget switches_widget{ + {80, 80, 80, 112}, + }; + + Button button_done{ + {72, 264, 96, 24}, + "Done"}; }; /*class DebugLCRView : public View { public: - DebugLCRView(NavigationView& nav, std::string lcrstring); + DebugLCRView(NavigationView& nav, std::string lcrstring); - void focus() override; - - std::string title() const override { return "LCR debug"; }; + void focus() override; + + std::string title() const override { return "LCR debug"; }; private: - Console console { - { 8, 16, 224, 240 } - }; - - Button button_exit { - { 72, 264, 96, 32 }, - "Exit" - }; + Console console { + { 8, 16, 224, 240 } + }; + + Button button_exit { + { 72, 264, 96, 32 }, + "Exit" + }; };*/ class DebugPeripheralsMenuView : public BtnGridView { -public: - DebugPeripheralsMenuView(NavigationView& nav); - std::string title() const override { return "Peripherals"; }; + public: + DebugPeripheralsMenuView(NavigationView& nav); + std::string title() const override { return "Peripherals"; }; }; class DebugMenuView : public BtnGridView { -public: - DebugMenuView(NavigationView& nav); - std::string title() const override { return "Debug"; }; + public: + DebugMenuView(NavigationView& nav); + std::string title() const override { return "Debug"; }; }; } /* namespace ui */ -#endif/*__UI_DEBUG_H__*/ +#endif /*__UI_DEBUG_H__*/ diff --git a/firmware/application/apps/ui_dfu_menu.cpp b/firmware/application/apps/ui_dfu_menu.cpp index 8f549ac6a..3db7841b5 100644 --- a/firmware/application/apps/ui_dfu_menu.cpp +++ b/firmware/application/apps/ui_dfu_menu.cpp @@ -25,75 +25,59 @@ namespace ui { -DfuMenu::DfuMenu(NavigationView& nav) : nav_ (nav) { - add_children({ - &text_head, - &labels, - &text_info_line_1, - &text_info_line_2, - &text_info_line_3, - &text_info_line_4, - &text_info_line_5, - &text_info_line_6, - &text_info_line_7, - &text_info_line_8 - }); +DfuMenu::DfuMenu(NavigationView& nav) + : nav_(nav) { + add_children({&text_head, + &labels, + &text_info_line_1, + &text_info_line_2, + &text_info_line_3, + &text_info_line_4, + &text_info_line_5, + &text_info_line_6, + &text_info_line_7, + &text_info_line_8}); } void DfuMenu::paint(Painter& painter) { - auto utilisation = get_cpu_utilisation_in_percent(); + auto utilisation = get_cpu_utilisation_in_percent(); - text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6)); - text_info_line_2.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6)); - text_info_line_3.set(to_string_dec_uint(utilisation, 6)); - text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6)); - text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6)); - text_info_line_6.set(to_string_dec_uint(shared_memory.m4_cpu_usage, 6)); - text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6)); - text_info_line_8.set(to_string_dec_uint(chTimeNow()/1000, 6)); + text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6)); + text_info_line_2.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6)); + text_info_line_3.set(to_string_dec_uint(utilisation, 6)); + text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6)); + text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6)); + text_info_line_6.set(to_string_dec_uint(shared_memory.m4_cpu_usage, 6)); + text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6)); + text_info_line_8.set(to_string_dec_uint(chTimeNow() / 1000, 6)); - constexpr auto margin = 5; - constexpr auto lines = 8 + 2; + constexpr auto margin = 5; + constexpr auto lines = 8 + 2; - painter.fill_rectangle( - { - {6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin}, - {15 * CHARACTER_WIDTH + margin * 2, lines * LINE_HEIGHT + margin * 2} - }, - ui::Color::black() - ); + painter.fill_rectangle( + {{6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin}, + {15 * CHARACTER_WIDTH + margin * 2, lines * LINE_HEIGHT + margin * 2}}, + ui::Color::black()); - painter.fill_rectangle( - { - {5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin}, - {CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2} - }, - ui::Color::dark_cyan() - ); + painter.fill_rectangle( + {{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin}, + {CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}}, + ui::Color::dark_cyan()); - painter.fill_rectangle( - { - {21 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin}, - {CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2} - }, - ui::Color::dark_cyan() - ); + painter.fill_rectangle( + {{21 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin}, + {CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}}, + ui::Color::dark_cyan()); - painter.fill_rectangle( - { - {5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8}, - {17 * CHARACTER_WIDTH + margin * 2, 8} - }, - ui::Color::dark_cyan() - ); + painter.fill_rectangle( + {{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8}, + {17 * CHARACTER_WIDTH + margin * 2, 8}}, + ui::Color::dark_cyan()); - painter.fill_rectangle( - { - {5 * CHARACTER_WIDTH - margin, (lines+3) * LINE_HEIGHT + margin}, - {17 * CHARACTER_WIDTH + margin * 2, 8} - }, - ui::Color::dark_cyan() - ); + painter.fill_rectangle( + {{5 * CHARACTER_WIDTH - margin, (lines + 3) * LINE_HEIGHT + margin}, + {17 * CHARACTER_WIDTH + margin * 2, 8}}, + ui::Color::dark_cyan()); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_dfu_menu.hpp b/firmware/application/apps/ui_dfu_menu.hpp index 78c816ab4..01f8d7a4f 100644 --- a/firmware/application/apps/ui_dfu_menu.hpp +++ b/firmware/application/apps/ui_dfu_menu.hpp @@ -36,38 +36,37 @@ namespace ui { class NavigationView; class DfuMenu : public View { -public: - DfuMenu(NavigationView& nav); - ~DfuMenu() = default; + public: + DfuMenu(NavigationView& nav); + ~DfuMenu() = default; - void paint(Painter& painter) override; + void paint(Painter& painter) override; -private: - NavigationView& nav_; - - Text text_head {{ 6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 11 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, "Performance"}; + private: + NavigationView& nav_; - Labels labels { - { { 6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT }, "M0 heap:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT }, "M0 stack:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT }, "M0 cpu %:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT }, "M4 heap:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT }, "M4 stack:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH,10 * LINE_HEIGHT }, "M4 cpu %:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH,11 * LINE_HEIGHT }, "M4 miss:", Color::dark_cyan() }, - { { 6 * CHARACTER_WIDTH,12 * LINE_HEIGHT }, "uptime:", Color::dark_cyan() } - }; + Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 11 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Performance"}; - Text text_info_line_1 {{ 15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_2 {{ 15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_3 {{ 15 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_4 {{ 15 * CHARACTER_WIDTH, 8 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_5 {{ 15 * CHARACTER_WIDTH, 9 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_6 {{ 15 * CHARACTER_WIDTH,10 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_7 {{ 15 * CHARACTER_WIDTH,11 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; - Text text_info_line_8 {{ 15 * CHARACTER_WIDTH,12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""}; + Labels labels{ + {{6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "M0 heap:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "M0 stack:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "M0 cpu %:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "M4 heap:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "M4 stack:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "M4 cpu %:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 miss:", Color::dark_cyan()}, + {{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "uptime:", Color::dark_cyan()}}; + + Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_3{{15 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_4{{15 * CHARACTER_WIDTH, 8 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_5{{15 * CHARACTER_WIDTH, 9 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_6{{15 * CHARACTER_WIDTH, 10 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_7{{15 * CHARACTER_WIDTH, 11 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; }; } /* namespace ui */ -#endif/*__UI_DFU_MENU_H__*/ +#endif /*__UI_DFU_MENU_H__*/ diff --git a/firmware/application/apps/ui_encoders.cpp b/firmware/application/apps/ui_encoders.cpp index ffdae2c4d..c28c67ccf 100644 --- a/firmware/application/apps/ui_encoders.cpp +++ b/firmware/application/apps/ui_encoders.cpp @@ -31,299 +31,291 @@ using namespace portapack; namespace ui { EncodersConfigView::EncodersConfigView( - NavigationView&, Rect parent_rect -) { - using option_t = std::pair; - std::vector enc_options; - size_t i; - - set_parent_rect(parent_rect); - hidden(true); - - // Default encoder def - encoder_def = &encoder_defs[0]; - - add_children({ - &labels, - &options_enctype, - &field_repeat_min, - &field_clk, - &field_clk_step, - &field_frameduration, - &field_frameduration_step, - &symfield_word, - &text_format, - &waveform - }); - - // Load encoder types in option field - for (i = 0; i < ENC_TYPES_COUNT; i++) - enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i)); - - options_enctype.on_change = [this](size_t index, int32_t) { - on_type_change(index); - }; - - options_enctype.set_options(enc_options); - options_enctype.set_selected_index(0); - - symfield_word.on_change = [this]() { - generate_frame(); - }; - - // Selecting input clock changes symbol and word duration - field_clk.on_change = [this](int32_t value) { - // value is in kHz, new_value is in us - int32_t new_value = (encoder_def->clk_per_symbol * 1000000) / (value * 1000); - if (new_value != field_frameduration.value()) - field_frameduration.set_value(new_value * encoder_def->word_length, false); - }; - - field_clk_step.on_change = [this](size_t, int32_t value) { - field_clk.set_step(value); - }; - - // Selecting word duration changes input clock and symbol duration - field_frameduration.on_change = [this](int32_t value) { - // value is in us, new_value is in kHz - int32_t new_value = (value * 1000) / (encoder_def->word_length * encoder_def->clk_per_symbol); - if (new_value != field_clk.value()) - field_clk.set_value(1000000 / new_value, false); - }; - - field_frameduration_step.on_change = [this](size_t, int32_t value) { - field_frameduration.set_step(value); - }; + NavigationView&, + Rect parent_rect) { + using option_t = std::pair; + std::vector enc_options; + size_t i; + + set_parent_rect(parent_rect); + hidden(true); + + // Default encoder def + encoder_def = &encoder_defs[0]; + + add_children({&labels, + &options_enctype, + &field_repeat_min, + &field_clk, + &field_clk_step, + &field_frameduration, + &field_frameduration_step, + &symfield_word, + &text_format, + &waveform}); + + // Load encoder types in option field + for (i = 0; i < ENC_TYPES_COUNT; i++) + enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i)); + + options_enctype.on_change = [this](size_t index, int32_t) { + on_type_change(index); + }; + + options_enctype.set_options(enc_options); + options_enctype.set_selected_index(0); + + symfield_word.on_change = [this]() { + generate_frame(); + }; + + // Selecting input clock changes symbol and word duration + field_clk.on_change = [this](int32_t value) { + // value is in kHz, new_value is in us + int32_t new_value = (encoder_def->clk_per_symbol * 1000000) / (value * 1000); + if (new_value != field_frameduration.value()) + field_frameduration.set_value(new_value * encoder_def->word_length, false); + }; + + field_clk_step.on_change = [this](size_t, int32_t value) { + field_clk.set_step(value); + }; + + // Selecting word duration changes input clock and symbol duration + field_frameduration.on_change = [this](int32_t value) { + // value is in us, new_value is in kHz + int32_t new_value = (value * 1000) / (encoder_def->word_length * encoder_def->clk_per_symbol); + if (new_value != field_clk.value()) + field_clk.set_value(1000000 / new_value, false); + }; + + field_frameduration_step.on_change = [this](size_t, int32_t value) { + field_frameduration.set_step(value); + }; } void EncodersConfigView::focus() { - options_enctype.focus(); + options_enctype.focus(); } void EncodersConfigView::on_type_change(size_t index) { - std::string format_string = ""; - size_t word_length; - char symbol_type; - - encoder_def = &encoder_defs[index]; - - field_clk.set_value(encoder_def->default_speed / 1000); - field_repeat_min.set_value(encoder_def->repeat_min); - - // SymField setup - word_length = encoder_def->word_length; - symfield_word.set_length(word_length); - size_t n = 0, i = 0; - while (n < word_length) { - symbol_type = encoder_def->word_format[i++]; - if (symbol_type == 'A') { - symfield_word.set_symbol_list(n++, encoder_def->address_symbols); - format_string += 'A'; - } else if (symbol_type == 'D') { - symfield_word.set_symbol_list(n++, encoder_def->data_symbols); - format_string += 'D'; - } - } - - // Ugly :( Pad to erase - format_string.append(24 - format_string.size(), ' '); - - text_format.set(format_string); - - generate_frame(); + std::string format_string = ""; + size_t word_length; + char symbol_type; + + encoder_def = &encoder_defs[index]; + + field_clk.set_value(encoder_def->default_speed / 1000); + field_repeat_min.set_value(encoder_def->repeat_min); + + // SymField setup + word_length = encoder_def->word_length; + symfield_word.set_length(word_length); + size_t n = 0, i = 0; + while (n < word_length) { + symbol_type = encoder_def->word_format[i++]; + if (symbol_type == 'A') { + symfield_word.set_symbol_list(n++, encoder_def->address_symbols); + format_string += 'A'; + } else if (symbol_type == 'D') { + symfield_word.set_symbol_list(n++, encoder_def->data_symbols); + format_string += 'D'; + } + } + + // Ugly :( Pad to erase + format_string.append(24 - format_string.size(), ' '); + + text_format.set(format_string); + + generate_frame(); } void EncodersConfigView::on_show() { - options_enctype.set_selected_index(0); - on_type_change(0); + options_enctype.set_selected_index(0); + on_type_change(0); } void EncodersConfigView::draw_waveform() { - size_t length = frame_fragments.length(); + size_t length = frame_fragments.length(); - for (size_t n = 0; n < length; n++) - waveform_buffer[n] = (frame_fragments[n] == '0') ? 0 : 1; + for (size_t n = 0; n < length; n++) + waveform_buffer[n] = (frame_fragments[n] == '0') ? 0 : 1; - waveform.set_length(length); - waveform.set_dirty(); + waveform.set_length(length); + waveform.set_dirty(); } void EncodersConfigView::generate_frame() { - size_t i = 0; + size_t i = 0; - frame_fragments.clear(); + frame_fragments.clear(); - for (auto c : encoder_def->word_format) { - if (c == 'S') - frame_fragments += encoder_def->sync; - else if (!c) - break; - else - frame_fragments += encoder_def->bit_format[symfield_word.get_sym(i++)]; - } + for (auto c : encoder_def->word_format) { + if (c == 'S') + frame_fragments += encoder_def->sync; + else if (!c) + break; + else + frame_fragments += encoder_def->bit_format[symfield_word.get_sym(i++)]; + } - draw_waveform(); + draw_waveform(); } uint8_t EncodersConfigView::repeat_min() { - return field_repeat_min.value(); + return field_repeat_min.value(); } uint32_t EncodersConfigView::samples_per_bit() { - return OOK_SAMPLERATE / ((field_clk.value() * 1000) / encoder_def->clk_per_fragment); + return OOK_SAMPLERATE / ((field_clk.value() * 1000) / encoder_def->clk_per_fragment); } uint32_t EncodersConfigView::pause_symbols() { - return encoder_def->pause_symbols; + return encoder_def->pause_symbols; } void EncodersScanView::focus() { - field_length.focus(); + field_length.focus(); } EncodersScanView::EncodersScanView( - NavigationView&, Rect parent_rect -) { - set_parent_rect(parent_rect); - hidden(true); - - add_children({ - &labels, - &field_length, - &bit_length_10, - &bit_length - }); - - field_length.set_value(8); - bit_length_10.set_value(40); - bit_length.set_value(0); + NavigationView&, + Rect parent_rect) { + set_parent_rect(parent_rect); + hidden(true); + + add_children({&labels, + &field_length, + &bit_length_10, + &bit_length}); + + field_length.set_value(8); + bit_length_10.set_value(40); + bit_length.set_value(0); } void EncodersView::focus() { - tab_view.focus(); + tab_view.focus(); } EncodersView::~EncodersView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_ook", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_ook", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit . - baseband::shutdown(); // better this function after load_sram() + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit . + baseband::shutdown(); // better this function after load_sram() } void EncodersView::update_progress() { - std::string str_buffer; - - if (tx_mode == SINGLE || tx_mode == SCAN) { - str_buffer = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(repeat_min); - text_status.set(str_buffer); - progressbar.set_value(repeat_index); - } else { - text_status.set("Ready"); - progressbar.set_value(0); - } + std::string str_buffer; + + if (tx_mode == SINGLE || tx_mode == SCAN) { + str_buffer = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(repeat_min); + text_status.set(str_buffer); + progressbar.set_value(repeat_index); + } else { + text_status.set("Ready"); + progressbar.set_value(0); + } } void EncodersView::on_tx_progress(const uint32_t progress, const bool done) { - if (!done) { - // Repeating... - repeat_index = progress + 1; - update_progress(); - } else { - // make sure all samples are transmitted before disabling radio - chThdSleepMilliseconds(10); - - // Done transmitting - transmitter_model.disable(); - tx_mode = IDLE; - text_status.set("Done"); - progressbar.set_value(0); - tx_view.set_transmitting(false); - } + if (!done) { + // Repeating... + repeat_index = progress + 1; + update_progress(); + } else { + // make sure all samples are transmitted before disabling radio + chThdSleepMilliseconds(10); + + // Done transmitting + transmitter_model.disable(); + tx_mode = IDLE; + text_status.set("Done"); + progressbar.set_value(0); + tx_view.set_transmitting(false); + } } void EncodersView::start_tx(const bool scan) { - size_t bitstream_length = 0; - int scan_width = 0; - uint32_t samples_per_bit; - - if (scan) { - tx_mode = SCAN; - scan_width = view_scan.field_length.value(); - samples_per_bit = - ((view_scan.bit_length_10.value() * 10 + view_scan.bit_length.value()) * OOK_SAMPLERATE) / 1000000UL; - constexpr auto sym_len = 4; - const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) * sym_len / 2048UL; - progressbar.set_max(seq_len); - repeat_min = seq_len; - } else { - tx_mode = SINGLE; - samples_per_bit = view_config.samples_per_bit(); - view_config.generate_frame(); - bitstream_length = make_bitstream(view_config.frame_fragments); - progressbar.set_max(repeat_min); - repeat_min = view_config.repeat_min(); - } - - repeat_index = 1; - update_progress(); - - transmitter_model.set_sampling_rate(OOK_SAMPLERATE); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_ook_data( - bitstream_length, - samples_per_bit, - repeat_min, - view_config.pause_symbols(), - scan_width - ); + size_t bitstream_length = 0; + int scan_width = 0; + uint32_t samples_per_bit; + + if (scan) { + tx_mode = SCAN; + scan_width = view_scan.field_length.value(); + samples_per_bit = + ((view_scan.bit_length_10.value() * 10 + view_scan.bit_length.value()) * OOK_SAMPLERATE) / 1000000UL; + constexpr auto sym_len = 4; + const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t)samples_per_bit) * sym_len / 2048UL; + progressbar.set_max(seq_len); + repeat_min = seq_len; + } else { + tx_mode = SINGLE; + samples_per_bit = view_config.samples_per_bit(); + view_config.generate_frame(); + bitstream_length = make_bitstream(view_config.frame_fragments); + progressbar.set_max(repeat_min); + repeat_min = view_config.repeat_min(); + } + + repeat_index = 1; + update_progress(); + + transmitter_model.set_sampling_rate(OOK_SAMPLERATE); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_ook_data( + bitstream_length, + samples_per_bit, + repeat_min, + view_config.pause_symbols(), + scan_width); } EncodersView::EncodersView( - NavigationView& nav -) : nav_ { nav } -{ - baseband::run_image(portapack::spi_flash::image_tag_ook); - - add_children({ - &tab_view, - &view_config, - &view_scan, - &text_status, - &progressbar, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_ook", &app_settings); - if (rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(transmitter_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - tx_view.set_transmitting(true); - start_tx(tab_view.selected()); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - baseband::kill_ook(); - transmitter_model.disable(); - }; + NavigationView& nav) + : nav_{nav} { + baseband::run_image(portapack::spi_flash::image_tag_ook); + + add_children({&tab_view, + &view_config, + &view_scan, + &text_status, + &progressbar, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_ook", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + tx_view.set_transmitting(true); + start_tx(tab_view.selected()); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + baseband::kill_ook(); + transmitter_model.disable(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_encoders.hpp b/firmware/application/apps/ui_encoders.hpp index f8afc02a4..23a320568 100644 --- a/firmware/application/apps/ui_encoders.hpp +++ b/firmware/application/apps/ui_encoders.hpp @@ -33,230 +33,207 @@ using namespace encoders; namespace ui { class EncodersConfigView : public View { -public: - EncodersConfigView(NavigationView& nav, Rect parent_rect); - - EncodersConfigView(const EncodersConfigView&) = delete; - EncodersConfigView(EncodersConfigView&&) = delete; - EncodersConfigView& operator=(const EncodersConfigView&) = delete; - EncodersConfigView& operator=(EncodersConfigView&&) = delete; - - void focus() override; - void on_show() override; - - uint8_t repeat_min(); - uint32_t samples_per_bit(); - uint32_t pause_symbols(); - void generate_frame(); - - std::string frame_fragments = "0"; - -private: - int16_t waveform_buffer[550]; - const encoder_def_t * encoder_def { }; - - void draw_waveform(); - void on_bitfield(); - void on_type_change(size_t index); - - Labels labels { - { { 1 * 8, 0 }, "Type:", Color::light_grey() }, - { { 17 * 8, 0 }, "Repeat:", Color::light_grey() }, - { { 1 * 8, 2 * 8 }, "Clk:", Color::light_grey() }, - { { 10 * 8, 2 * 8 }, "kHz", Color::light_grey() }, - { { 17 * 8, 2 * 8 }, "Step:", Color::light_grey() }, - { { 1 * 8, 4 * 8 }, "Frame:", Color::light_grey() }, - { { 13 * 8, 4 * 8 }, "us", Color::light_grey() }, - { { 17 * 8, 4 * 8 }, "Step:", Color::light_grey() }, - { { 2 * 8, 7 * 8 }, "Symbols:", Color::light_grey() }, - { { 1 * 8, 14 * 8 }, "Waveform:", Color::light_grey() } - }; - - OptionsField options_enctype { // Options are loaded at runtime - { 6 * 8, 0 }, - 7, - { - } - }; - - NumberField field_clk { - { 5 * 8, 2 * 8 }, - 4, - { 1, 1000 }, - 1, - ' ' - }; - - NumberField field_repeat_min { - { 24 * 8, 0 }, - 2, - { 1, 99 }, - 1, - ' ' - }; - - OptionsField field_clk_step { - { 22 * 8, 2 * 8 }, - 7, - { - { "1", 1 }, - { "10", 10 }, - { "100", 100 } - } - }; - - NumberField field_frameduration { - { 7 * 8, 4 * 8 }, - 5, - { 300, 99999 }, - 100, - ' ' - }; - - OptionsField field_frameduration_step { - { 22 * 8, 4 * 8 }, - 7, - { - { "1", 1 }, - { "10", 10 }, - { "100", 100 }, - { "1000", 1000 } - } - }; - - SymField symfield_word { - { 2 * 8, 9 * 8 }, - 20, - SymField::SYMFIELD_DEF - }; - - Text text_format { - { 2 * 8, 11 * 8, 24 * 8, 16 }, - "" - }; - - Waveform waveform { - { 0, 17 * 8, 240, 32 }, - waveform_buffer, - 0, - 0, - true, - Color::yellow() - }; + public: + EncodersConfigView(NavigationView& nav, Rect parent_rect); + + EncodersConfigView(const EncodersConfigView&) = delete; + EncodersConfigView(EncodersConfigView&&) = delete; + EncodersConfigView& operator=(const EncodersConfigView&) = delete; + EncodersConfigView& operator=(EncodersConfigView&&) = delete; + + void focus() override; + void on_show() override; + + uint8_t repeat_min(); + uint32_t samples_per_bit(); + uint32_t pause_symbols(); + void generate_frame(); + + std::string frame_fragments = "0"; + + private: + int16_t waveform_buffer[550]; + const encoder_def_t* encoder_def{}; + + void draw_waveform(); + void on_bitfield(); + void on_type_change(size_t index); + + Labels labels{ + {{1 * 8, 0}, "Type:", Color::light_grey()}, + {{17 * 8, 0}, "Repeat:", Color::light_grey()}, + {{1 * 8, 2 * 8}, "Clk:", Color::light_grey()}, + {{10 * 8, 2 * 8}, "kHz", Color::light_grey()}, + {{17 * 8, 2 * 8}, "Step:", Color::light_grey()}, + {{1 * 8, 4 * 8}, "Frame:", Color::light_grey()}, + {{13 * 8, 4 * 8}, "us", Color::light_grey()}, + {{17 * 8, 4 * 8}, "Step:", Color::light_grey()}, + {{2 * 8, 7 * 8}, "Symbols:", Color::light_grey()}, + {{1 * 8, 14 * 8}, "Waveform:", Color::light_grey()}}; + + OptionsField options_enctype{// Options are loaded at runtime + {6 * 8, 0}, + 7, + {}}; + + NumberField field_clk{ + {5 * 8, 2 * 8}, + 4, + {1, 1000}, + 1, + ' '}; + + NumberField field_repeat_min{ + {24 * 8, 0}, + 2, + {1, 99}, + 1, + ' '}; + + OptionsField field_clk_step{ + {22 * 8, 2 * 8}, + 7, + {{"1", 1}, + {"10", 10}, + {"100", 100}}}; + + NumberField field_frameduration{ + {7 * 8, 4 * 8}, + 5, + {300, 99999}, + 100, + ' '}; + + OptionsField field_frameduration_step{ + {22 * 8, 4 * 8}, + 7, + {{"1", 1}, + {"10", 10}, + {"100", 100}, + {"1000", 1000}}}; + + SymField symfield_word{ + {2 * 8, 9 * 8}, + 20, + SymField::SYMFIELD_DEF}; + + Text text_format{ + {2 * 8, 11 * 8, 24 * 8, 16}, + ""}; + + Waveform waveform{ + {0, 17 * 8, 240, 32}, + waveform_buffer, + 0, + 0, + true, + Color::yellow()}; }; - class EncodersScanView : public View { -public: - EncodersScanView(NavigationView& nav, Rect parent_rect); - - NumberField field_length { - { 8 * 8, 0 }, - 2, - { 3, 24 }, - 1, - ' ' - }; - - NumberField bit_length_10 { - { 12 * 8, 2 * 8 }, - 2, - { 1, 88 }, - 1, - ' ' - }; - - NumberField bit_length { - { 14 * 8, 2 * 8 }, - 1, - { 0, 9 }, - 1, - ' ' - }; - - void focus() override; - -private: - Labels labels { - { { 1 * 8, 0 * 8 }, "Length:", Color::light_grey() }, - { { 1 * 8, 2 * 8 }, "Bit length:", Color::light_grey() }, - { { 16 * 8, 2 * 8 }, "us", Color::light_grey() }, - }; + public: + EncodersScanView(NavigationView& nav, Rect parent_rect); + + NumberField field_length{ + {8 * 8, 0}, + 2, + {3, 24}, + 1, + ' '}; + + NumberField bit_length_10{ + {12 * 8, 2 * 8}, + 2, + {1, 88}, + 1, + ' '}; + + NumberField bit_length{ + {14 * 8, 2 * 8}, + 1, + {0, 9}, + 1, + ' '}; + + void focus() override; + + private: + Labels labels{ + {{1 * 8, 0 * 8}, "Length:", Color::light_grey()}, + {{1 * 8, 2 * 8}, "Bit length:", Color::light_grey()}, + {{16 * 8, 2 * 8}, "us", Color::light_grey()}, + }; }; class EncodersView : public View { -public: - EncodersView(NavigationView& nav); - ~EncodersView(); - - void focus() override; - - std::string title() const override { return "OOK TX"; }; - -private: - NavigationView& nav_; - - enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - tx_modes tx_mode = IDLE; - uint32_t repeat_index { 0 }; - uint32_t repeat_min { 0 }; - - void update_progress(); - void start_tx(const bool scan); - void on_tx_progress(const uint32_t progress, const bool done); - - /*const Style style_address { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - const Style style_data { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::blue(), - };*/ - - Rect view_rect = { 0, 4 * 8, 240, 168 }; - - EncodersConfigView view_config { nav_, view_rect }; - EncodersScanView view_scan { nav_, view_rect }; - - TabView tab_view { - { "Config", Color::cyan(), &view_config }, - { "de Bruijn", Color::green(), &view_scan }, - }; - - Text text_status { - { 2 * 8, 13 * 16, 128, 16 }, - "Ready" - }; - - ProgressBar progressbar { - { 2 * 8, 13 * 16 + 20, 208, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 50000, - 9 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + EncodersView(NavigationView& nav); + ~EncodersView(); + + void focus() override; + + std::string title() const override { return "OOK TX"; }; + + private: + NavigationView& nav_; + + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + tx_modes tx_mode = IDLE; + uint32_t repeat_index{0}; + uint32_t repeat_min{0}; + + void update_progress(); + void start_tx(const bool scan); + void on_tx_progress(const uint32_t progress, const bool done); + + /*const Style style_address { + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + const Style style_data { + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::blue(), + };*/ + + Rect view_rect = {0, 4 * 8, 240, 168}; + + EncodersConfigView view_config{nav_, view_rect}; + EncodersScanView view_scan{nav_, view_rect}; + + TabView tab_view{ + {"Config", Color::cyan(), &view_config}, + {"de Bruijn", Color::green(), &view_scan}, + }; + + Text text_status{ + {2 * 8, 13 * 16, 128, 16}, + "Ready"}; + + ProgressBar progressbar{ + {2 * 8, 13 * 16 + 20, 208, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 50000, + 9}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_fileman.cpp b/firmware/application/apps/ui_fileman.cpp index dc7e6a40e..bbfee1374 100644 --- a/firmware/application/apps/ui_fileman.cpp +++ b/firmware/application/apps/ui_fileman.cpp @@ -37,83 +37,82 @@ namespace { using namespace ui; bool is_hidden_file(const fs::path& path) { - return !path.empty() && path.native()[0] == u'.'; + return !path.empty() && path.native()[0] == u'.'; } // Gets a truncated name from a path for display. std::string truncate(const fs::path& path, size_t max_length) { - return ::truncate(path.string(), max_length); + return ::truncate(path.string(), max_length); } // Gets a human readable file size string. std::string get_pretty_size(uint32_t file_size) { - static const std::string suffix[5] = { "B", "kB", "MB", "GB", "??" }; - size_t suffix_index = 0; - - while (file_size >= 1024) { - file_size /= 1024; - suffix_index++; - } - - if (suffix_index > 4) - suffix_index = 4; - - return to_string_dec_uint(file_size) + suffix[suffix_index]; + static const std::string suffix[5] = {"B", "kB", "MB", "GB", "??"}; + size_t suffix_index = 0; + + while (file_size >= 1024) { + file_size /= 1024; + suffix_index++; + } + + if (suffix_index > 4) + suffix_index = 4; + + return to_string_dec_uint(file_size) + suffix[suffix_index]; } // Case insensitive path equality on underlying "native" string. bool iequal( - const fs::path& lhs, - const fs::path& rhs -) { - const auto& lhs_str = lhs.native(); - const auto& rhs_str = rhs.native(); - - // NB: Not correct for Unicode/locales. - if (lhs_str.length() == rhs_str.length()) { - for (size_t i = 0; i < lhs_str.length(); ++i) - if (towupper(lhs_str[i]) != towupper(rhs_str[i])) - return false; - - return true; - } - - return false; + const fs::path& lhs, + const fs::path& rhs) { + const auto& lhs_str = lhs.native(); + const auto& rhs_str = rhs.native(); + + // NB: Not correct for Unicode/locales. + if (lhs_str.length() == rhs_str.length()) { + for (size_t i = 0; i < lhs_str.length(); ++i) + if (towupper(lhs_str[i]) != towupper(rhs_str[i])) + return false; + + return true; + } + + return false; } // Inserts the entry into the entry list sorted directories first then by file name. void insert_sorted(std::vector& entries, fileman_entry&& entry) { - auto it = std::lower_bound(std::begin(entries), std::end(entries), entry, - [](const fileman_entry& lhs, const fileman_entry& rhs) { - if (lhs.is_directory && !rhs.is_directory) - return true; - else if (!lhs.is_directory && rhs.is_directory) - return false; - else - return lhs.path < rhs.path; - }); - - entries.insert(it, std::move(entry)); + auto it = std::lower_bound(std::begin(entries), std::end(entries), entry, + [](const fileman_entry& lhs, const fileman_entry& rhs) { + if (lhs.is_directory && !rhs.is_directory) + return true; + else if (!lhs.is_directory && rhs.is_directory) + return false; + else + return lhs.path < rhs.path; + }); + + entries.insert(it, std::move(entry)); } // Returns the partner file path or an empty path if no partner is found. fs::path get_partner_file(fs::path path) { - if (fs::is_directory(path)) - return { }; - - const fs::path txt_path{ u".TXT" }; - const fs::path c16_path{ u".C16" }; - auto ext = path.extension(); - - if (iequal(ext, txt_path)) - ext = c16_path; - else if (iequal(ext, c16_path)) - ext = txt_path; - else - return { }; - - path.replace_extension(ext); - return fs::file_exists(path) && !fs::is_directory(path) ? path : fs::path{ }; + if (fs::is_directory(path)) + return {}; + + const fs::path txt_path{u".TXT"}; + const fs::path c16_path{u".C16"}; + auto ext = path.extension(); + + if (iequal(ext, txt_path)) + ext = c16_path; + else if (iequal(ext, c16_path)) + ext = txt_path; + else + return {}; + + path.replace_extension(ext); + return fs::file_exists(path) && !fs::is_directory(path) ? path : fs::path{}; } // Modal prompt to update the partner file if it exists. @@ -121,497 +120,478 @@ fs::path get_partner_file(fs::path path) { // Returns true is a partner is found, otherwise false. // Path must be the full path to the file. bool partner_file_prompt( - NavigationView& nav, - const fs::path& path, - std::string action_name, - std::function on_partner_action -) { - auto partner = get_partner_file(path); - - if (partner.empty()) - return false; - - nav.push_under_current( - "Partner File", - partner.filename().string() + "\n" + action_name + " this file too?", - YESNO, - [&nav, partner, on_partner_action](bool choice) { - if (on_partner_action) - on_partner_action(partner, choice); - } - ); - - return true; + NavigationView& nav, + const fs::path& path, + std::string action_name, + std::function on_partner_action) { + auto partner = get_partner_file(path); + + if (partner.empty()) + return false; + + nav.push_under_current( + "Partner File", + partner.filename().string() + "\n" + action_name + " this file too?", + YESNO, + [&nav, partner, on_partner_action](bool choice) { + if (on_partner_action) + on_partner_action(partner, choice); + }); + + return true; } fs::path get_unique_filename(const fs::path& path, const fs::path& file) { - auto stem = file.stem(); - auto ext = file.extension(); - auto serial = 1; - fs::path new_path = file; - - while (fs::file_exists(path / new_path)) { - new_path = stem; - new_path += fs::path{ u"_" }; - new_path += to_string_dec_int(serial++); - new_path += ext; - } - - return new_path; -} + auto stem = file.stem(); + auto ext = file.extension(); + auto serial = 1; + fs::path new_path = file; + while (fs::file_exists(path / new_path)) { + new_path = stem; + new_path += fs::path{u"_"}; + new_path += to_string_dec_int(serial++); + new_path += ext; + } + + return new_path; } +} // namespace + namespace ui { /* FileManBaseView ***********************************************************/ void FileManBaseView::load_directory_contents(const fs::path& dir_path) { - current_path = dir_path; - entry_list.clear(); - auto filtering = !extension_filter.empty(); - - text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24)); - - for (const auto& entry : fs::directory_iterator(dir_path, u"*")) { - // Hide files starting with '.' (hidden / tmp). - if (is_hidden_file(entry.path())) - continue; - - if (fs::is_regular_file(entry.status())) { - if (!filtering || iequal(entry.path().extension(), extension_filter)) - insert_sorted(entry_list, { entry.path(), (uint32_t)entry.size(), false }); - } else if (fs::is_directory(entry.status())) { - insert_sorted(entry_list, { entry.path(), 0, true }); - } - } - - // Add "parent" directory if not at the root. - if (!dir_path.empty()) - entry_list.insert(entry_list.begin(), { parent_dir_path, 0, true }); + current_path = dir_path; + entry_list.clear(); + auto filtering = !extension_filter.empty(); + + text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24)); + + for (const auto& entry : fs::directory_iterator(dir_path, u"*")) { + // Hide files starting with '.' (hidden / tmp). + if (is_hidden_file(entry.path())) + continue; + + if (fs::is_regular_file(entry.status())) { + if (!filtering || iequal(entry.path().extension(), extension_filter)) + insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false}); + } else if (fs::is_directory(entry.status())) { + insert_sorted(entry_list, {entry.path(), 0, true}); + } + } + + // Add "parent" directory if not at the root. + if (!dir_path.empty()) + entry_list.insert(entry_list.begin(), {parent_dir_path, 0, true}); } fs::path FileManBaseView::get_selected_full_path() const { - if (get_selected_entry().path == parent_dir_path) - return current_path.parent_path(); + if (get_selected_entry().path == parent_dir_path) + return current_path.parent_path(); - return current_path / get_selected_entry().path; + return current_path / get_selected_entry().path; } const fileman_entry& FileManBaseView::get_selected_entry() const { - // TODO: return reference to an "empty" entry on OOB? - return entry_list[menu_view.highlighted_index()]; + // TODO: return reference to an "empty" entry on OOB? + return entry_list[menu_view.highlighted_index()]; } FileManBaseView::FileManBaseView( - NavigationView& nav, - std::string filter -) : nav_{ nav }, - extension_filter{ filter } -{ - add_children({ - &labels, - &text_current, - &text_info, - &button_exit - }); - - button_exit.on_select = [this, &nav](Button&) { - nav.pop(); - }; - - if (!sdcIsCardInserted(&SDCD1)) { - empty_ = EmptyReason::NoSDC; - text_current.set("NO SD CARD!"); - return; - } - - load_directory_contents(current_path); - - if (!entry_list.size()) { - empty_ = EmptyReason::NoFiles; - text_current.set("EMPTY SD CARD!"); - } else { - menu_view.on_left = [this]() { - pop_dir(); - }; - } + NavigationView& nav, + std::string filter) + : nav_{nav}, + extension_filter{filter} { + add_children({&labels, + &text_current, + &text_info, + &button_exit}); + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; + + if (!sdcIsCardInserted(&SDCD1)) { + empty_ = EmptyReason::NoSDC; + text_current.set("NO SD CARD!"); + return; + } + + load_directory_contents(current_path); + + if (!entry_list.size()) { + empty_ = EmptyReason::NoFiles; + text_current.set("EMPTY SD CARD!"); + } else { + menu_view.on_left = [this]() { + pop_dir(); + }; + } } void FileManBaseView::focus() { - if (empty_ != EmptyReason::NotEmpty) { - button_exit.focus(); - } else { - menu_view.focus(); - } + if (empty_ != EmptyReason::NotEmpty) { + button_exit.focus(); + } else { + menu_view.focus(); + } } void FileManBaseView::push_dir(const fs::path& path) { - if (path == parent_dir_path) { - pop_dir(); - } else { - current_path /= path; - saved_index_stack.push_back(menu_view.highlighted_index()); - menu_view.set_highlighted(0); - reload_current(); - } + if (path == parent_dir_path) { + pop_dir(); + } else { + current_path /= path; + saved_index_stack.push_back(menu_view.highlighted_index()); + menu_view.set_highlighted(0); + reload_current(); + } } void FileManBaseView::pop_dir() { - if (saved_index_stack.empty()) - return; + if (saved_index_stack.empty()) + return; - current_path = current_path.parent_path(); - reload_current(); - menu_view.set_highlighted(saved_index_stack.back()); - saved_index_stack.pop_back(); + current_path = current_path.parent_path(); + reload_current(); + menu_view.set_highlighted(saved_index_stack.back()); + saved_index_stack.pop_back(); } void FileManBaseView::refresh_list() { - if (on_refresh_widgets) - on_refresh_widgets(false); - - auto prev_highlight = menu_view.highlighted_index(); - menu_view.clear(); - - for (const auto& entry : entry_list) { - auto entry_name = truncate(entry.path, 20); - - if (entry.is_directory) { - menu_view.add_item({ - entry_name, - ui::Color::yellow(), - &bitmap_icon_dir, - [this](KeyEvent key) { - if (on_select_entry) - on_select_entry(key); - } - }); - - } else { - const auto& assoc = get_assoc(entry.path.extension()); - auto size_str = get_pretty_size(entry.size); - - menu_view.add_item({ - entry_name + std::string(21 - entry_name.length(), ' ') + size_str, - assoc.color, - assoc.icon, - [this](KeyEvent key) { - if (on_select_entry) - on_select_entry(key); - } - }); - } - - // HACK: Should page menu items instead of limiting the number. - if (menu_view.item_count() >= max_items_shown) - break; - } - - text_info.set(menu_view.item_count() >= max_items_shown ? "Too many files!" : ""); - menu_view.set_highlighted(prev_highlight); + if (on_refresh_widgets) + on_refresh_widgets(false); + + auto prev_highlight = menu_view.highlighted_index(); + menu_view.clear(); + + for (const auto& entry : entry_list) { + auto entry_name = truncate(entry.path, 20); + + if (entry.is_directory) { + menu_view.add_item({entry_name, + ui::Color::yellow(), + &bitmap_icon_dir, + [this](KeyEvent key) { + if (on_select_entry) + on_select_entry(key); + }}); + + } else { + const auto& assoc = get_assoc(entry.path.extension()); + auto size_str = get_pretty_size(entry.size); + + menu_view.add_item({entry_name + std::string(21 - entry_name.length(), ' ') + size_str, + assoc.color, + assoc.icon, + [this](KeyEvent key) { + if (on_select_entry) + on_select_entry(key); + }}); + } + + // HACK: Should page menu items instead of limiting the number. + if (menu_view.item_count() >= max_items_shown) + break; + } + + text_info.set(menu_view.item_count() >= max_items_shown ? "Too many files!" : ""); + menu_view.set_highlighted(prev_highlight); } void FileManBaseView::reload_current() { - load_directory_contents(current_path); - refresh_list(); + load_directory_contents(current_path); + refresh_list(); } const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc( - const fs::path& ext) const -{ - size_t index = 0; + const fs::path& ext) const { + size_t index = 0; - for (; index < file_types.size() - 1; ++index) - if (iequal(ext, file_types[index].extension)) - return file_types[index]; + for (; index < file_types.size() - 1; ++index) + if (iequal(ext, file_types[index].extension)) + return file_types[index]; - // Default to last entry in the list. - return file_types[index]; + // Default to last entry in the list. + return file_types[index]; } /* FileLoadView **************************************************************/ FileLoadView::FileLoadView( - NavigationView& nav, - std::string filter -) : FileManBaseView(nav, filter) -{ - on_refresh_widgets = [this](bool v) { - refresh_widgets(v); - }; - - add_children({ - &menu_view - }); - - // Resize menu view to fill screen - menu_view.set_parent_rect({ 0, 3 * 8, 240, 29 * 8 }); - - refresh_list(); - - on_select_entry = [this](KeyEvent) { - if (get_selected_entry().is_directory) { - push_dir(get_selected_entry().path); - } else { - nav_.pop(); - if (on_changed) - on_changed(get_selected_full_path()); - } - }; + NavigationView& nav, + std::string filter) + : FileManBaseView(nav, filter) { + on_refresh_widgets = [this](bool v) { + refresh_widgets(v); + }; + + add_children({&menu_view}); + + // Resize menu view to fill screen + menu_view.set_parent_rect({0, 3 * 8, 240, 29 * 8}); + + refresh_list(); + + on_select_entry = [this](KeyEvent) { + if (get_selected_entry().is_directory) { + push_dir(get_selected_entry().path); + } else { + nav_.pop(); + if (on_changed) + on_changed(get_selected_full_path()); + } + }; } void FileLoadView::refresh_widgets(const bool) { - set_dirty(); + set_dirty(); } /* FileSaveView **************************************************************/ /* FileSaveView::FileSaveView( - NavigationView& nav, - const fs::path& path, - const fs::path& file + NavigationView& nav, + const fs::path& path, + const fs::path& file ) : nav_{ nav }, - path_{ path }, - file_{ file } + path_{ path }, + file_{ file } { - add_children({ - &labels, - &text_path, - &button_edit_path, - &text_name, - &button_edit_name, - &button_save, - &button_cancel, - }); - - button_edit_path.on_select = [this](Button&) { - buffer_ = path_.string(); - text_prompt(nav_, buffer_, max_filename_length, - [this](std::string&) { - path_ = buffer_; - refresh_widgets(); - }); - }; - - button_edit_name.on_select = [this](Button&) { - buffer_ = file_.string(); - text_prompt(nav_, buffer_, max_filename_length, - [this](std::string&) { - file_ = buffer_; - refresh_widgets(); - }); - }; - - button_save.on_select = [this](Button&) { - if (on_save) - on_save(path_ / file_); - else - nav_.pop(); - }; - - button_cancel.on_select = [this](Button&) { - nav_.pop(); - }; - - refresh_widgets(); + add_children({ + &labels, + &text_path, + &button_edit_path, + &text_name, + &button_edit_name, + &button_save, + &button_cancel, + }); + + button_edit_path.on_select = [this](Button&) { + buffer_ = path_.string(); + text_prompt(nav_, buffer_, max_filename_length, + [this](std::string&) { + path_ = buffer_; + refresh_widgets(); + }); + }; + + button_edit_name.on_select = [this](Button&) { + buffer_ = file_.string(); + text_prompt(nav_, buffer_, max_filename_length, + [this](std::string&) { + file_ = buffer_; + refresh_widgets(); + }); + }; + + button_save.on_select = [this](Button&) { + if (on_save) + on_save(path_ / file_); + else + nav_.pop(); + }; + + button_cancel.on_select = [this](Button&) { + nav_.pop(); + }; + + refresh_widgets(); } void FileSaveView::refresh_widgets() { - text_path.set(truncate(path_, 30)); - text_name.set(truncate(file_, 30)); - set_dirty(); + text_path.set(truncate(path_, 30)); + text_name.set(truncate(file_, 30)); + set_dirty(); } */ /* FileManagerView ***********************************************************/ void FileManagerView::on_rename() { - auto& entry = get_selected_entry(); - name_buffer = entry.path.filename().string(); - uint32_t cursor_pos = (uint32_t)name_buffer.length(); - - if (auto pos = name_buffer.find_last_of("."); - pos != name_buffer.npos && !entry.is_directory) - cursor_pos = pos; - - text_prompt(nav_, name_buffer, cursor_pos, max_filename_length, - [this](std::string& renamed) { - auto renamed_path = fs::path{ renamed }; - rename_file(get_selected_full_path(), current_path / renamed_path); - - auto has_partner = partner_file_prompt(nav_, get_selected_full_path(), "Rename", - [this, renamed_path](const fs::path& partner, bool should_rename) mutable { - if (should_rename) { - auto new_name = renamed_path.replace_extension(partner.extension()); - rename_file(partner, current_path / new_name); - } - reload_current(); - } - ); - - if (!has_partner) - reload_current(); - }); + auto& entry = get_selected_entry(); + name_buffer = entry.path.filename().string(); + uint32_t cursor_pos = (uint32_t)name_buffer.length(); + + if (auto pos = name_buffer.find_last_of("."); + pos != name_buffer.npos && !entry.is_directory) + cursor_pos = pos; + + text_prompt(nav_, name_buffer, cursor_pos, max_filename_length, + [this](std::string& renamed) { + auto renamed_path = fs::path{renamed}; + rename_file(get_selected_full_path(), current_path / renamed_path); + + auto has_partner = partner_file_prompt(nav_, get_selected_full_path(), "Rename", + [this, renamed_path](const fs::path& partner, bool should_rename) mutable { + if (should_rename) { + auto new_name = renamed_path.replace_extension(partner.extension()); + rename_file(partner, current_path / new_name); + } + reload_current(); + }); + + if (!has_partner) + reload_current(); + }); } void FileManagerView::on_delete() { - auto name = get_selected_entry().path.filename().string(); - nav_.push("Delete", "Delete " + name + "\nAre you sure?", YESNO, - [this](bool choice) { - if (choice) { - delete_file(get_selected_full_path()); - - auto has_partner = partner_file_prompt( - nav_, get_selected_full_path(), "Delete", - [this](const fs::path& partner, bool should_delete) { - if (should_delete) - delete_file(partner); - reload_current(); - } - ); - - if (!has_partner) - reload_current(); - } - } - ); + auto name = get_selected_entry().path.filename().string(); + nav_.push("Delete", "Delete " + name + "\nAre you sure?", YESNO, + [this](bool choice) { + if (choice) { + delete_file(get_selected_full_path()); + + auto has_partner = partner_file_prompt( + nav_, get_selected_full_path(), "Delete", + [this](const fs::path& partner, bool should_delete) { + if (should_delete) + delete_file(partner); + reload_current(); + }); + + if (!has_partner) + reload_current(); + } + }); } void FileManagerView::on_new_dir() { - name_buffer = ""; - text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) { - make_new_directory(current_path / dir_name); - reload_current(); - }); + name_buffer = ""; + text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) { + make_new_directory(current_path / dir_name); + reload_current(); + }); } void FileManagerView::on_new_file() { - name_buffer = ""; - text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) { - make_new_file(current_path / file_name); - reload_current(); - }); + name_buffer = ""; + text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) { + make_new_file(current_path / file_name); + reload_current(); + }); } void FileManagerView::on_paste() { - // TODO: handle partner file. Need to fix nav stack first. - auto new_name = get_unique_filename(current_path, clipboard_path.filename()); - fs::filesystem_error result; - - if (clipboard_mode == ClipboardMode::Cut) - result = rename_file(clipboard_path, current_path / new_name); - - else if (clipboard_mode == ClipboardMode::Copy) - result = copy_file(clipboard_path, current_path / new_name); - - if (result.code() != FR_OK) - nav_.display_modal("Paste Failed", result.what()); - - clipboard_path = fs::path{ }; - clipboard_mode = ClipboardMode::None; - menu_view.focus(); - reload_current(); + // TODO: handle partner file. Need to fix nav stack first. + auto new_name = get_unique_filename(current_path, clipboard_path.filename()); + fs::filesystem_error result; + + if (clipboard_mode == ClipboardMode::Cut) + result = rename_file(clipboard_path, current_path / new_name); + + else if (clipboard_mode == ClipboardMode::Copy) + result = copy_file(clipboard_path, current_path / new_name); + + if (result.code() != FR_OK) + nav_.display_modal("Paste Failed", result.what()); + + clipboard_path = fs::path{}; + clipboard_mode = ClipboardMode::None; + menu_view.focus(); + reload_current(); } bool FileManagerView::selected_is_valid() const { - return !entry_list.empty() && - get_selected_entry().path != parent_dir_path; + return !entry_list.empty() && + get_selected_entry().path != parent_dir_path; } void FileManagerView::refresh_widgets(const bool v) { - button_rename.hidden(v); - button_delete.hidden(v); - button_cut.hidden(v); - button_copy.hidden(v); - button_paste.hidden(v); - button_new_dir.hidden(v); - button_new_file.hidden(v); - - set_dirty(); -} + button_rename.hidden(v); + button_delete.hidden(v); + button_cut.hidden(v); + button_copy.hidden(v); + button_paste.hidden(v); + button_new_dir.hidden(v); + button_new_file.hidden(v); -FileManagerView::FileManagerView( - NavigationView& nav -) : FileManBaseView(nav, "") -{ - // Don't bother with the UI in the case of no SDC. - if (empty_ == EmptyReason::NoSDC) - return; - - on_refresh_widgets = [this](bool v) { - refresh_widgets(v); - }; - - add_children({ - &menu_view, - &labels, - &text_date, - &button_rename, - &button_delete, - &button_cut, - &button_copy, - &button_paste, - &button_new_dir, - &button_new_file - }); - - menu_view.on_highlight = [this]() { - if (selected_is_valid()) - text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path()))); - else - text_date.set(""); - }; - - refresh_list(); - - on_select_entry = [this](KeyEvent key) { - if (key == KeyEvent::Select && get_selected_entry().is_directory) { - push_dir(get_selected_entry().path); - } else { - button_rename.focus(); - } - }; - - button_rename.on_select = [this]() { - if (selected_is_valid()) - on_rename(); - }; - - button_delete.on_select = [this]() { - if (selected_is_valid()) - on_delete(); - }; - - button_cut.on_select = [this]() { - if (selected_is_valid() && !get_selected_entry().is_directory) { - clipboard_path = get_selected_full_path(); - clipboard_mode = ClipboardMode::Cut; - } else - nav_.display_modal("Cut", "Can't cut that."); - }; - - button_copy.on_select = [this]() { - if (selected_is_valid() && !get_selected_entry().is_directory) { - clipboard_path = get_selected_full_path(); - clipboard_mode = ClipboardMode::Copy; - } else - nav_.display_modal("Copy", "Can't copy that."); - }; - - button_paste.on_select = [this]() { - if (clipboard_mode != ClipboardMode::None) - on_paste(); - else - nav_.display_modal("Paste", "Cut or copy a file first."); - }; - - button_new_dir.on_select = [this]() { - on_new_dir(); - }; - - button_new_file.on_select = [this]() { - on_new_file(); - }; + set_dirty(); } -} +FileManagerView::FileManagerView( + NavigationView& nav) + : FileManBaseView(nav, "") { + // Don't bother with the UI in the case of no SDC. + if (empty_ == EmptyReason::NoSDC) + return; + + on_refresh_widgets = [this](bool v) { + refresh_widgets(v); + }; + + add_children({&menu_view, + &labels, + &text_date, + &button_rename, + &button_delete, + &button_cut, + &button_copy, + &button_paste, + &button_new_dir, + &button_new_file}); + + menu_view.on_highlight = [this]() { + if (selected_is_valid()) + text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path()))); + else + text_date.set(""); + }; + + refresh_list(); + + on_select_entry = [this](KeyEvent key) { + if (key == KeyEvent::Select && get_selected_entry().is_directory) { + push_dir(get_selected_entry().path); + } else { + button_rename.focus(); + } + }; + + button_rename.on_select = [this]() { + if (selected_is_valid()) + on_rename(); + }; + + button_delete.on_select = [this]() { + if (selected_is_valid()) + on_delete(); + }; + + button_cut.on_select = [this]() { + if (selected_is_valid() && !get_selected_entry().is_directory) { + clipboard_path = get_selected_full_path(); + clipboard_mode = ClipboardMode::Cut; + } else + nav_.display_modal("Cut", "Can't cut that."); + }; + + button_copy.on_select = [this]() { + if (selected_is_valid() && !get_selected_entry().is_directory) { + clipboard_path = get_selected_full_path(); + clipboard_mode = ClipboardMode::Copy; + } else + nav_.display_modal("Copy", "Can't copy that."); + }; + + button_paste.on_select = [this]() { + if (clipboard_mode != ClipboardMode::None) + on_paste(); + else + nav_.display_modal("Paste", "Cut or copy a file first."); + }; + + button_new_dir.on_select = [this]() { + on_new_dir(); + }; + + button_new_file.on_select = [this]() { + on_new_file(); + }; +} + +} // namespace ui diff --git a/firmware/application/apps/ui_fileman.hpp b/firmware/application/apps/ui_fileman.hpp index c3312a7c4..a08334c1a 100644 --- a/firmware/application/apps/ui_fileman.hpp +++ b/firmware/application/apps/ui_fileman.hpp @@ -31,113 +31,108 @@ namespace ui { struct fileman_entry { - std::filesystem::path path { }; - uint32_t size { }; - bool is_directory { }; + std::filesystem::path path{}; + uint32_t size{}; + bool is_directory{}; }; enum class EmptyReason : uint8_t { - NotEmpty, - NoFiles, - NoSDC + NotEmpty, + NoFiles, + NoSDC }; enum class ClipboardMode : uint8_t { - None, - Cut, - Copy + None, + Cut, + Copy }; class FileManBaseView : public View { -public: - FileManBaseView( - NavigationView& nav, - std::string filter - ); - - virtual ~FileManBaseView() { } - - void focus() override; - std::string title() const override { return "Fileman"; }; - void push_dir(const std::filesystem::path& path); - -protected: - static constexpr size_t max_filename_length = 64; - static constexpr size_t max_items_shown = 100; - - struct file_assoc_t { - std::filesystem::path extension; - const Bitmap* icon; - ui::Color color; - }; - - const std::vector file_types = { - { u".TXT", &bitmap_icon_file_text, ui::Color::white() }, - { u".PNG", &bitmap_icon_file_image, ui::Color::green() }, - { u".BMP", &bitmap_icon_file_image, ui::Color::green() }, - { u".C8", &bitmap_icon_file_iq, ui::Color::dark_cyan() }, - { u".C16", &bitmap_icon_file_iq, ui::Color::dark_cyan() }, - { u".WAV", &bitmap_icon_file_wav, ui::Color::dark_magenta() }, - { u"", &bitmap_icon_file, ui::Color::light_grey() } // NB: Must be last. - }; - - std::filesystem::path get_selected_full_path() const; - const fileman_entry& get_selected_entry() const; - - void pop_dir(); - void refresh_list(); - void reload_current(); - void load_directory_contents(const std::filesystem::path& dir_path); - const file_assoc_t& get_assoc(const std::filesystem::path& ext) const; - - NavigationView& nav_; - - EmptyReason empty_ { EmptyReason::NotEmpty }; - std::function on_select_entry { nullptr }; - std::function on_refresh_widgets { nullptr }; - - const std::filesystem::path parent_dir_path { u".." }; - std::filesystem::path current_path { u"" }; - std::filesystem::path extension_filter { u"" }; - - std::vector entry_list { }; - std::vector saved_index_stack { }; - - Labels labels { - { { 0, 0 }, "Path:", Color::light_grey() } - }; - - Text text_current { - { 6 * 8, 0 * 8, 24 * 8, 16 }, - "", - }; - - MenuView menu_view { - { 0, 2 * 8, 240, 26 * 8 }, - true - }; - - // HACK: for item count limit. - Text text_info { - { 1 * 8, 35 * 8, 15 * 8, 16 }, - "" - }; - - Button button_exit { - { 21 * 8, 34 * 8, 9 * 8, 32 }, - "Exit" - }; + public: + FileManBaseView( + NavigationView& nav, + std::string filter); + + virtual ~FileManBaseView() {} + + void focus() override; + std::string title() const override { return "Fileman"; }; + void push_dir(const std::filesystem::path& path); + + protected: + static constexpr size_t max_filename_length = 64; + static constexpr size_t max_items_shown = 100; + + struct file_assoc_t { + std::filesystem::path extension; + const Bitmap* icon; + ui::Color color; + }; + + const std::vector file_types = { + {u".TXT", &bitmap_icon_file_text, ui::Color::white()}, + {u".PNG", &bitmap_icon_file_image, ui::Color::green()}, + {u".BMP", &bitmap_icon_file_image, ui::Color::green()}, + {u".C8", &bitmap_icon_file_iq, ui::Color::dark_cyan()}, + {u".C16", &bitmap_icon_file_iq, ui::Color::dark_cyan()}, + {u".WAV", &bitmap_icon_file_wav, ui::Color::dark_magenta()}, + {u"", &bitmap_icon_file, ui::Color::light_grey()} // NB: Must be last. + }; + + std::filesystem::path get_selected_full_path() const; + const fileman_entry& get_selected_entry() const; + + void pop_dir(); + void refresh_list(); + void reload_current(); + void load_directory_contents(const std::filesystem::path& dir_path); + const file_assoc_t& get_assoc(const std::filesystem::path& ext) const; + + NavigationView& nav_; + + EmptyReason empty_{EmptyReason::NotEmpty}; + std::function on_select_entry{nullptr}; + std::function on_refresh_widgets{nullptr}; + + const std::filesystem::path parent_dir_path{u".."}; + std::filesystem::path current_path{u""}; + std::filesystem::path extension_filter{u""}; + + std::vector entry_list{}; + std::vector saved_index_stack{}; + + Labels labels{ + {{0, 0}, "Path:", Color::light_grey()}}; + + Text text_current{ + {6 * 8, 0 * 8, 24 * 8, 16}, + "", + }; + + MenuView menu_view{ + {0, 2 * 8, 240, 26 * 8}, + true}; + + // HACK: for item count limit. + Text text_info{ + {1 * 8, 35 * 8, 15 * 8, 16}, + ""}; + + Button button_exit{ + {21 * 8, 34 * 8, 9 * 8, 32}, + "Exit"}; }; class FileLoadView : public FileManBaseView { -public: - std::function on_changed { }; - - FileLoadView(NavigationView& nav, std::string filter); - virtual ~FileLoadView() { } + public: + std::function on_changed{}; -private: - void refresh_widgets(const bool v); + FileLoadView(NavigationView& nav, std::string filter); + virtual ~FileLoadView() {} + + private: + void refresh_widgets(const bool v); }; /* @@ -145,138 +140,129 @@ class FileLoadView : public FileManBaseView { // but it will OOM if launched from within FileManager. class FileSaveView : public View { public: - FileSaveView( - NavigationView& nav, - const std::filesystem::path& path, - const std::filesystem::path& file); + FileSaveView( + NavigationView& nav, + const std::filesystem::path& path, + const std::filesystem::path& file); - std::function on_save { }; + std::function on_save { }; private: - static constexpr size_t max_filename_length = 64; - - void refresh_widgets(); - - NavigationView& nav_; - std::filesystem::path path_; - std::filesystem::path file_; - std::string buffer_ { }; - - Labels labels { - { { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() }, - { { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() }, - }; - - Text text_path { - { 0 * 8, 2 * 16, 30 * 8, 16 }, - "", - }; - - Button button_edit_path { - { 18 * 8, 3 * 16, 11 * 8, 32 }, - "Edit Path" - }; - - Text text_name { - { 0 * 8, 7 * 16, 30 * 8, 16 }, - "", - }; - - Button button_edit_name { - { 18 * 8, 8 * 16, 11 * 8, 32 }, - "Edit Name" - }; - - Button button_save { - { 10 * 8, 16 * 16, 9 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 20 * 8, 16 * 16, 9 * 8, 32 }, - "Cancel" - }; + static constexpr size_t max_filename_length = 64; + + void refresh_widgets(); + + NavigationView& nav_; + std::filesystem::path path_; + std::filesystem::path file_; + std::string buffer_ { }; + + Labels labels { + { { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() }, + { { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() }, + }; + + Text text_path { + { 0 * 8, 2 * 16, 30 * 8, 16 }, + "", + }; + + Button button_edit_path { + { 18 * 8, 3 * 16, 11 * 8, 32 }, + "Edit Path" + }; + + Text text_name { + { 0 * 8, 7 * 16, 30 * 8, 16 }, + "", + }; + + Button button_edit_name { + { 18 * 8, 8 * 16, 11 * 8, 32 }, + "Edit Name" + }; + + Button button_save { + { 10 * 8, 16 * 16, 9 * 8, 32 }, + "Save" + }; + + Button button_cancel { + { 20 * 8, 16 * 16, 9 * 8, 32 }, + "Cancel" + }; }; */ class FileManagerView : public FileManBaseView { -public: - FileManagerView(NavigationView& nav); - virtual ~FileManagerView() { } - -private: - // Passed by ref to other views needing lifetime extension. - std::string name_buffer { }; - std::filesystem::path clipboard_path { }; - ClipboardMode clipboard_mode { ClipboardMode::None }; - - void refresh_widgets(const bool v); - void on_rename(); - void on_delete(); - void on_paste(); - void on_new_dir(); - void on_new_file(); - - // True if the selected entry is a real file item. - bool selected_is_valid() const; - - Labels labels { - { { 0, 26 * 8 }, "Created ", Color::light_grey() } - }; - - Text text_date { - { 8 * 8, 26 * 8 , 19 * 8, 16 }, - "" - }; - - NewButton button_rename { - { 0 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_rename, - Color::dark_blue() - }; - - NewButton button_delete { - { 4 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_trash, - Color::red() - }; - - NewButton button_cut { - { 9 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_cut, - Color::dark_grey() - }; - - NewButton button_copy { - { 13 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_copy, - Color::dark_grey() - }; - - NewButton button_paste { - { 17 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_paste, - Color::dark_grey() - }; - - NewButton button_new_dir { - { 22 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_new_dir, - Color::green() - }; - - NewButton button_new_file { - { 26 * 8, 29 * 8, 4 * 8, 32 }, - { }, - &bitmap_icon_new_file, - Color::green() - }; + public: + FileManagerView(NavigationView& nav); + virtual ~FileManagerView() {} + + private: + // Passed by ref to other views needing lifetime extension. + std::string name_buffer{}; + std::filesystem::path clipboard_path{}; + ClipboardMode clipboard_mode{ClipboardMode::None}; + + void refresh_widgets(const bool v); + void on_rename(); + void on_delete(); + void on_paste(); + void on_new_dir(); + void on_new_file(); + + // True if the selected entry is a real file item. + bool selected_is_valid() const; + + Labels labels{ + {{0, 26 * 8}, "Created ", Color::light_grey()}}; + + Text text_date{ + {8 * 8, 26 * 8, 19 * 8, 16}, + ""}; + + NewButton button_rename{ + {0 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_rename, + Color::dark_blue()}; + + NewButton button_delete{ + {4 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_trash, + Color::red()}; + + NewButton button_cut{ + {9 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_cut, + Color::dark_grey()}; + + NewButton button_copy{ + {13 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_copy, + Color::dark_grey()}; + + NewButton button_paste{ + {17 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_paste, + Color::dark_grey()}; + + NewButton button_new_dir{ + {22 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_new_dir, + Color::green()}; + + NewButton button_new_file{ + {26 * 8, 29 * 8, 4 * 8, 32}, + {}, + &bitmap_icon_new_file, + Color::green()}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_flash_utility.cpp b/firmware/application/apps/ui_flash_utility.cpp index 0f0eb8fb9..be9e0dce8 100644 --- a/firmware/application/apps/ui_flash_utility.cpp +++ b/firmware/application/apps/ui_flash_utility.cpp @@ -25,65 +25,60 @@ namespace ui { -Thread* FlashUtilityView::thread { nullptr }; +Thread* FlashUtilityView::thread{nullptr}; static constexpr size_t max_filename_length = 26; -FlashUtilityView::FlashUtilityView(NavigationView& nav) : nav_ (nav) { - add_children({ - &labels, - &menu_view - }); +FlashUtilityView::FlashUtilityView(NavigationView& nav) + : nav_(nav) { + add_children({&labels, + &menu_view}); - menu_view.set_parent_rect({ 0, 3 * 8, 240, 33 * 8 }); + menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8}); - for (const auto& entry : std::filesystem::directory_iterator(u"FIRMWARE", u"*.bin")) { - auto filename = entry.path().filename(); - auto path = entry.path().native(); + for (const auto& entry : std::filesystem::directory_iterator(u"FIRMWARE", u"*.bin")) { + auto filename = entry.path().filename(); + auto path = entry.path().native(); - menu_view.add_item({ - filename.string().substr(0, max_filename_length), - ui::Color::red(), - &bitmap_icon_temperature, - [this, path](KeyEvent) { - this->firmware_selected(path); - } - }); - } + menu_view.add_item({filename.string().substr(0, max_filename_length), + ui::Color::red(), + &bitmap_icon_temperature, + [this, path](KeyEvent) { + this->firmware_selected(path); + }}); + } } void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) { - nav_.push( - "Warning!", - "This will replace your\ncurrent firmware.\n\nIf things go wrong you are\nrequired to flash manually\nwith dfu.", - YESNO, - [this, path](bool choice) { - if (choice) { - std::u16string full_path = std::u16string(u"FIRMWARE/") + path; - this->flash_firmware(full_path); - } - } - ); + nav_.push( + "Warning!", + "This will replace your\ncurrent firmware.\n\nIf things go wrong you are\nrequired to flash manually\nwith dfu.", + YESNO, + [this, path](bool choice) { + if (choice) { + std::u16string full_path = std::u16string(u"FIRMWARE/") + path; + this->flash_firmware(full_path); + } + }); } void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) { - ui::Painter painter; - painter.fill_rectangle( - { 0, 0, portapack::display.width(), portapack::display.height() }, - ui::Color::black() - ); + ui::Painter painter; + painter.fill_rectangle( + {0, 0, portapack::display.width(), portapack::display.height()}, + ui::Color::black()); - painter.draw_string({ 12, 24 }, this->nav_.style(), "This will take 15 seconds."); - painter.draw_string({ 12, 64 }, this->nav_.style(), "Please wait while LEDs RX"); - painter.draw_string({ 12, 84 }, this->nav_.style(), "and TX are flashing."); - painter.draw_string({ 12, 124 }, this->nav_.style(), "Then restart the device."); + painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds."); + painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LEDs RX"); + painter.draw_string({12, 84}, this->nav_.style(), "and TX are flashing."); + painter.draw_string({12, 124}, this->nav_.style(), "Then restart the device."); - std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2); - m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false); - m0_halt(); + std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2); + m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false); + m0_halt(); } void FlashUtilityView::focus() { - menu_view.focus(); + menu_view.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_flash_utility.hpp b/firmware/application/apps/ui_flash_utility.hpp index 82acc22b9..9ea96c0b9 100644 --- a/firmware/application/apps/ui_flash_utility.hpp +++ b/firmware/application/apps/ui_flash_utility.hpp @@ -35,32 +35,30 @@ namespace ui { class FlashUtilityView : public View { -public: - FlashUtilityView(NavigationView& nav); + public: + FlashUtilityView(NavigationView& nav); - void focus() override; - - std::string title() const override { return "Flash Utility"; }; + void focus() override; -private: - NavigationView& nav_; - - bool confirmed = false; - static Thread* thread; + std::string title() const override { return "Flash Utility"; }; - Labels labels { - { { 4, 4 }, "Select firmware to flash:", Color::white() } - }; + private: + NavigationView& nav_; - MenuView menu_view { - { 0, 2 * 8, 240, 26 * 8 }, - true - }; + bool confirmed = false; + static Thread* thread; - void firmware_selected(std::filesystem::path::string_type path); - void flash_firmware(std::filesystem::path::string_type path); + Labels labels{ + {{4, 4}, "Select firmware to flash:", Color::white()}}; + + MenuView menu_view{ + {0, 2 * 8, 240, 26 * 8}, + true}; + + void firmware_selected(std::filesystem::path::string_type path); + void flash_firmware(std::filesystem::path::string_type path); }; } /* namespace ui */ -#endif/*__UI_FLSH_UITILTY_H__*/ +#endif /*__UI_FLSH_UITILTY_H__*/ diff --git a/firmware/application/apps/ui_freqman.cpp b/firmware/application/apps/ui_freqman.cpp index 3b52d308a..fdf3a1032 100644 --- a/firmware/application/apps/ui_freqman.cpp +++ b/firmware/application/apps/ui_freqman.cpp @@ -29,322 +29,305 @@ using namespace portapack; namespace ui { -static int32_t last_category_id { 0 }; +static int32_t last_category_id{0}; FreqManBaseView::FreqManBaseView( - NavigationView& nav -) : nav_ (nav) -{ - file_list = get_freqman_files(); - - add_children({ - &label_category, - &button_exit - }); - - if (file_list.size()) { - add_child(&options_category); - populate_categories(); - } else - error_ = ERROR_NOFILES; - - // initialize - change_category(last_category_id); - - // Default function - on_change_category = [this](int32_t category_id) { - change_category(category_id); - }; - - button_exit.on_select = [this, &nav](Button&) { - nav.pop(); - }; + NavigationView& nav) + : nav_(nav) { + file_list = get_freqman_files(); + + add_children({&label_category, + &button_exit}); + + if (file_list.size()) { + add_child(&options_category); + populate_categories(); + } else + error_ = ERROR_NOFILES; + + // initialize + change_category(last_category_id); + + // Default function + on_change_category = [this](int32_t category_id) { + change_category(category_id); + }; + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; }; void FreqManBaseView::focus() { - button_exit.focus(); - - if (error_ == ERROR_ACCESS) { - nav_.display_modal("Error", "File acces error", ABORT, nullptr); - } else if (error_ == ERROR_NOFILES) { - nav_.display_modal("Error", "No database files\nin /freqman", ABORT, nullptr); - } else { - options_category.focus(); - } + button_exit.focus(); + + if (error_ == ERROR_ACCESS) { + nav_.display_modal("Error", "File acces error", ABORT, nullptr); + } else if (error_ == ERROR_NOFILES) { + nav_.display_modal("Error", "No database files\nin /freqman", ABORT, nullptr); + } else { + options_category.focus(); + } } void FreqManBaseView::populate_categories() { - categories.clear(); - - for (size_t n = 0; n < file_list.size(); n++) - categories.emplace_back(std::make_pair(file_list[n].substr(0, 14), n)); - - // Alphabetical sort - std::sort(categories.begin(), categories.end(), [](auto &left, auto &right) { - return left.first < right.first; - }); - - options_category.set_options(categories); - options_category.set_selected_index(last_category_id); - - options_category.on_change = [this](size_t category_id, int32_t) { - if (on_change_category) - on_change_category(category_id); - }; + categories.clear(); + + for (size_t n = 0; n < file_list.size(); n++) + categories.emplace_back(std::make_pair(file_list[n].substr(0, 14), n)); + + // Alphabetical sort + std::sort(categories.begin(), categories.end(), [](auto& left, auto& right) { + return left.first < right.first; + }); + + options_category.set_options(categories); + options_category.set_selected_index(last_category_id); + + options_category.on_change = [this](size_t category_id, int32_t) { + if (on_change_category) + on_change_category(category_id); + }; } void FreqManBaseView::change_category(int32_t category_id) { - - if (!file_list.size()) return; - - last_category_id = current_category_id = category_id; - - if (!load_freqman_file(file_list[categories[current_category_id].second], database)) - error_ = ERROR_ACCESS; - else - refresh_list(); + if (!file_list.size()) return; + + last_category_id = current_category_id = category_id; + + if (!load_freqman_file(file_list[categories[current_category_id].second], database)) + error_ = ERROR_ACCESS; + else + refresh_list(); } void FreqManBaseView::refresh_list() { - if (!database.size()) { - if (on_refresh_widgets) - on_refresh_widgets(true); - } else { - if (on_refresh_widgets) - on_refresh_widgets(false); - - menu_view.clear(); - - for (size_t n = 0; n < database.size(); n++) { - menu_view.add_item({ - freqman_item_string(database[n], 30), - ui::Color::white(), - nullptr, - [this](KeyEvent){ - if (on_select_frequency) - on_select_frequency(); - } - }); - } - - menu_view.set_highlighted(0); // Refresh - } + if (!database.size()) { + if (on_refresh_widgets) + on_refresh_widgets(true); + } else { + if (on_refresh_widgets) + on_refresh_widgets(false); + + menu_view.clear(); + + for (size_t n = 0; n < database.size(); n++) { + menu_view.add_item({freqman_item_string(database[n], 30), + ui::Color::white(), + nullptr, + [this](KeyEvent) { + if (on_select_frequency) + on_select_frequency(); + }}); + } + + menu_view.set_highlighted(0); // Refresh + } } void FrequencySaveView::save_current_file() { - if (database.size() > FREQMAN_MAX_PER_FILE) { - nav_.display_modal( - "Error", "Too many entries, maximum is\n" FREQMAN_MAX_PER_FILE_STR ". Trim list ?", - YESNO, - [this](bool choice) { - if (choice) { - database.resize(FREQMAN_MAX_PER_FILE); - save_freqman_file(file_list[categories[current_category_id].second], database); - } - nav_.pop(); - } - ); - } else { - save_freqman_file(file_list[categories[current_category_id].second], database); - nav_.pop(); - } + if (database.size() > FREQMAN_MAX_PER_FILE) { + nav_.display_modal( + "Error", "Too many entries, maximum is\n" FREQMAN_MAX_PER_FILE_STR ". Trim list ?", + YESNO, + [this](bool choice) { + if (choice) { + database.resize(FREQMAN_MAX_PER_FILE); + save_freqman_file(file_list[categories[current_category_id].second], database); + } + nav_.pop(); + }); + } else { + save_freqman_file(file_list[categories[current_category_id].second], database); + nav_.pop(); + } } void FrequencySaveView::on_save_name() { - text_prompt(nav_, desc_buffer, 28, [this](std::string& buffer) { - database.push_back({ value_, 0, buffer, SINGLE }); - save_current_file(); - }); + text_prompt(nav_, desc_buffer, 28, [this](std::string& buffer) { + database.push_back({value_, 0, buffer, SINGLE}); + save_current_file(); + }); } void FrequencySaveView::on_save_timestamp() { - database.push_back({ value_, 0, live_timestamp.string(), SINGLE }); - save_current_file(); + database.push_back({value_, 0, live_timestamp.string(), SINGLE}); + save_current_file(); } FrequencySaveView::FrequencySaveView( - NavigationView& nav, - const rf::Frequency value -) : FreqManBaseView(nav), - value_ (value) -{ - desc_buffer.reserve(28); - - // Todo: add back ? - /*for (size_t n = 0; n < database.size(); n++) { - if (database[n].value == value_) { - error_ = ERROR_DUPLICATE; - break; - } - }*/ - - add_children({ - &labels, - &big_display, - &button_save_name, - &button_save_timestamp, - &live_timestamp - }); - - big_display.set(value); - - button_save_name.on_select = [this, &nav](Button&) { - on_save_name(); - }; - button_save_timestamp.on_select = [this, &nav](Button&) { - on_save_timestamp(); - }; + NavigationView& nav, + const rf::Frequency value) + : FreqManBaseView(nav), + value_(value) { + desc_buffer.reserve(28); + + // Todo: add back ? + /*for (size_t n = 0; n < database.size(); n++) { + if (database[n].value == value_) { + error_ = ERROR_DUPLICATE; + break; + } + }*/ + + add_children({&labels, + &big_display, + &button_save_name, + &button_save_timestamp, + &live_timestamp}); + + big_display.set(value); + + button_save_name.on_select = [this, &nav](Button&) { + on_save_name(); + }; + button_save_timestamp.on_select = [this, &nav](Button&) { + on_save_timestamp(); + }; } void FrequencyLoadView::refresh_widgets(const bool v) { - menu_view.hidden(v); - text_empty.hidden(!v); - //display.fill_rectangle(menu_view.screen_rect(), Color::black()); - set_dirty(); + menu_view.hidden(v); + text_empty.hidden(!v); + // display.fill_rectangle(menu_view.screen_rect(), Color::black()); + set_dirty(); } FrequencyLoadView::FrequencyLoadView( - NavigationView& nav -) : FreqManBaseView(nav) -{ - on_refresh_widgets = [this](bool v) { - refresh_widgets(v); - }; - - add_children({ - &menu_view, - &text_empty - }); - - // Resize menu view to fill screen - menu_view.set_parent_rect({ 0, 3 * 8, 240, 30 * 8 }); - - // Just to allow exit on left - menu_view.on_left = [&nav, this]() { - nav.pop(); - }; - - change_category(last_category_id); - refresh_list(); - - on_select_frequency = [&nav, this]() { - nav_.pop(); - - auto& entry = database[menu_view.highlighted_index()]; - - if (entry.type == RANGE) { - // User chose a frequency range entry - if (on_range_loaded) - on_range_loaded(entry.frequency_a, entry.frequency_b); - else if (on_frequency_loaded) - on_frequency_loaded(entry.frequency_a); - // TODO: Maybe return center of range if user choses a range when the app needs a unique frequency, instead of frequency_a ? - } else { - // User chose an unique frequency entry - if (on_frequency_loaded) - on_frequency_loaded(entry.frequency_a); - } - }; + NavigationView& nav) + : FreqManBaseView(nav) { + on_refresh_widgets = [this](bool v) { + refresh_widgets(v); + }; + + add_children({&menu_view, + &text_empty}); + + // Resize menu view to fill screen + menu_view.set_parent_rect({0, 3 * 8, 240, 30 * 8}); + + // Just to allow exit on left + menu_view.on_left = [&nav, this]() { + nav.pop(); + }; + + change_category(last_category_id); + refresh_list(); + + on_select_frequency = [&nav, this]() { + nav_.pop(); + + auto& entry = database[menu_view.highlighted_index()]; + + if (entry.type == RANGE) { + // User chose a frequency range entry + if (on_range_loaded) + on_range_loaded(entry.frequency_a, entry.frequency_b); + else if (on_frequency_loaded) + on_frequency_loaded(entry.frequency_a); + // TODO: Maybe return center of range if user choses a range when the app needs a unique frequency, instead of frequency_a ? + } else { + // User chose an unique frequency entry + if (on_frequency_loaded) + on_frequency_loaded(entry.frequency_a); + } + }; } void FrequencyManagerView::on_edit_freq(rf::Frequency f) { - database[menu_view.highlighted_index()].frequency_a = f; - save_freqman_file(file_list[categories[current_category_id].second], database); - refresh_list(); + database[menu_view.highlighted_index()].frequency_a = f; + save_freqman_file(file_list[categories[current_category_id].second], database); + refresh_list(); } void FrequencyManagerView::on_edit_desc(NavigationView& nav) { - text_prompt(nav, desc_buffer, 28, [this](std::string& buffer) { - database[menu_view.highlighted_index()].description = buffer; - refresh_list(); - save_freqman_file(file_list[categories[current_category_id].second], database); - }); + text_prompt(nav, desc_buffer, 28, [this](std::string& buffer) { + database[menu_view.highlighted_index()].description = buffer; + refresh_list(); + save_freqman_file(file_list[categories[current_category_id].second], database); + }); } void FrequencyManagerView::on_new_category(NavigationView& nav) { - text_prompt(nav, desc_buffer, 12, [this](std::string& buffer) { - File freqman_file; - create_freqman_file(buffer, freqman_file); - }); - populate_categories(); - refresh_list(); + text_prompt(nav, desc_buffer, 12, [this](std::string& buffer) { + File freqman_file; + create_freqman_file(buffer, freqman_file); + }); + populate_categories(); + refresh_list(); } void FrequencyManagerView::on_delete() { - database.erase(database.begin() + menu_view.highlighted_index()); - save_freqman_file(file_list[categories[current_category_id].second], database); - refresh_list(); + database.erase(database.begin() + menu_view.highlighted_index()); + save_freqman_file(file_list[categories[current_category_id].second], database); + refresh_list(); } void FrequencyManagerView::refresh_widgets(const bool v) { - button_edit_freq.hidden(v); - button_edit_desc.hidden(v); - button_delete.hidden(v); - menu_view.hidden(v); - text_empty.hidden(!v); - //display.fill_rectangle(menu_view.screen_rect(), Color::black()); - set_dirty(); + button_edit_freq.hidden(v); + button_edit_desc.hidden(v); + button_delete.hidden(v); + menu_view.hidden(v); + text_empty.hidden(!v); + // display.fill_rectangle(menu_view.screen_rect(), Color::black()); + set_dirty(); } FrequencyManagerView::~FrequencyManagerView() { - //save_freqman_file(file_list[categories[current_category_id].second], database); + // save_freqman_file(file_list[categories[current_category_id].second], database); } FrequencyManagerView::FrequencyManagerView( - NavigationView& nav -) : FreqManBaseView(nav) -{ - on_refresh_widgets = [this](bool v) { - refresh_widgets(v); - }; - - add_children({ - &labels, - &button_new_category, - &menu_view, - &text_empty, - &button_edit_freq, - &button_edit_desc, - &button_delete - }); - - // Just to allow exit on left - menu_view.on_left = [&nav, this]() { - nav.pop(); - }; - - change_category(last_category_id); - refresh_list(); - - on_select_frequency = [this]() { - button_edit_freq.focus(); - }; - - button_new_category.on_select = [this, &nav](Button&) { - desc_buffer = ""; - on_new_category(nav); - }; - - button_edit_freq.on_select = [this, &nav](Button&) { - auto new_view = nav.push(database[menu_view.highlighted_index()].frequency_a); - new_view->on_changed = [this](rf::Frequency f) { - on_edit_freq(f); - }; - }; - - button_edit_desc.on_select = [this, &nav](Button&) { - desc_buffer = database[menu_view.highlighted_index()].description; - on_edit_desc(nav); - }; - - button_delete.on_select = [this, &nav](Button&) { - nav.push("Confirm", "Are you sure ?", YESNO, - [this](bool choice) { - if (choice) - on_delete(); - } - ); - }; -} + NavigationView& nav) + : FreqManBaseView(nav) { + on_refresh_widgets = [this](bool v) { + refresh_widgets(v); + }; + add_children({&labels, + &button_new_category, + &menu_view, + &text_empty, + &button_edit_freq, + &button_edit_desc, + &button_delete}); + + // Just to allow exit on left + menu_view.on_left = [&nav, this]() { + nav.pop(); + }; + + change_category(last_category_id); + refresh_list(); + + on_select_frequency = [this]() { + button_edit_freq.focus(); + }; + + button_new_category.on_select = [this, &nav](Button&) { + desc_buffer = ""; + on_new_category(nav); + }; + + button_edit_freq.on_select = [this, &nav](Button&) { + auto new_view = nav.push(database[menu_view.highlighted_index()].frequency_a); + new_view->on_changed = [this](rf::Frequency f) { + on_edit_freq(f); + }; + }; + + button_edit_desc.on_select = [this, &nav](Button&) { + desc_buffer = database[menu_view.highlighted_index()].description; + on_edit_desc(nav); + }; + + button_delete.on_select = [this, &nav](Button&) { + nav.push("Confirm", "Are you sure ?", YESNO, + [this](bool choice) { + if (choice) + on_delete(); + }); + }; } + +} // namespace ui diff --git a/firmware/application/apps/ui_freqman.hpp b/firmware/application/apps/ui_freqman.hpp index 7ea6f1a47..2a727064b 100644 --- a/firmware/application/apps/ui_freqman.hpp +++ b/firmware/application/apps/ui_freqman.hpp @@ -30,146 +30,131 @@ #include "freqman.hpp" namespace ui { - + class FreqManBaseView : public View { -public: - FreqManBaseView( - NavigationView& nav - ); - - void focus() override; - -protected: - using option_t = std::pair; - using options_t = std::vector; - - NavigationView& nav_; - freqman_error error_ { NO_ERROR }; - options_t categories { }; - std::function on_change_category { nullptr }; - std::function on_select_frequency { nullptr }; - std::function on_refresh_widgets { nullptr }; - std::vector file_list { }; - int32_t current_category_id { 0 }; - - void populate_categories(); - void change_category(int32_t category_id); - void refresh_list(); - - freqman_db database { }; - - Labels label_category { - { { 0, 4 }, "Category:", Color::light_grey() } - }; - - OptionsField options_category { - { 9 * 8, 4 }, - 14, - { } - }; - - MenuView menu_view { - { 0, 3 * 8, 240, 23 * 8 }, - true - }; - Text text_empty { - { 7 * 8, 12 * 8, 16 * 8, 16 }, - "Empty category !", - }; - - Button button_exit { - { 20 * 8, 34 * 8, 10 * 8, 4 * 8 }, - "Exit" - }; + public: + FreqManBaseView( + NavigationView& nav); + + void focus() override; + + protected: + using option_t = std::pair; + using options_t = std::vector; + + NavigationView& nav_; + freqman_error error_{NO_ERROR}; + options_t categories{}; + std::function on_change_category{nullptr}; + std::function on_select_frequency{nullptr}; + std::function on_refresh_widgets{nullptr}; + std::vector file_list{}; + int32_t current_category_id{0}; + + void populate_categories(); + void change_category(int32_t category_id); + void refresh_list(); + + freqman_db database{}; + + Labels label_category{ + {{0, 4}, "Category:", Color::light_grey()}}; + + OptionsField options_category{ + {9 * 8, 4}, + 14, + {}}; + + MenuView menu_view{ + {0, 3 * 8, 240, 23 * 8}, + true}; + Text text_empty{ + {7 * 8, 12 * 8, 16 * 8, 16}, + "Empty category !", + }; + + Button button_exit{ + {20 * 8, 34 * 8, 10 * 8, 4 * 8}, + "Exit"}; }; class FrequencySaveView : public FreqManBaseView { -public: - FrequencySaveView(NavigationView& nav, const rf::Frequency value); - - std::string title() const override { return "Save freq."; }; - -private: - std::string desc_buffer { }; - rf::Frequency value_ { }; - - void on_save_name(); - void on_save_timestamp(); - void save_current_file(); - - BigFrequency big_display { - { 4, 2 * 16, 28 * 8, 32 }, - 0 - }; - - Labels labels { - { { 1 * 8, 12 * 8 }, "Save as:", Color::white() } - }; - - Button button_save_name { - { 1 * 8, 17 * 8, 12 * 8, 48 }, - "Name (set)" - }; - Button button_save_timestamp { - { 1 * 8, 25 * 8, 12 * 8, 48 }, - "Timestamp:" - }; - LiveDateTime live_timestamp { - { 14 * 8, 27 * 8, 16 * 8, 16 } - }; + public: + FrequencySaveView(NavigationView& nav, const rf::Frequency value); + + std::string title() const override { return "Save freq."; }; + + private: + std::string desc_buffer{}; + rf::Frequency value_{}; + + void on_save_name(); + void on_save_timestamp(); + void save_current_file(); + + BigFrequency big_display{ + {4, 2 * 16, 28 * 8, 32}, + 0}; + + Labels labels{ + {{1 * 8, 12 * 8}, "Save as:", Color::white()}}; + + Button button_save_name{ + {1 * 8, 17 * 8, 12 * 8, 48}, + "Name (set)"}; + Button button_save_timestamp{ + {1 * 8, 25 * 8, 12 * 8, 48}, + "Timestamp:"}; + LiveDateTime live_timestamp{ + {14 * 8, 27 * 8, 16 * 8, 16}}; }; class FrequencyLoadView : public FreqManBaseView { -public: - std::function on_frequency_loaded { }; - std::function on_range_loaded { }; - - FrequencyLoadView(NavigationView& nav); - - std::string title() const override { return "Load freq."; }; - -private: - void refresh_widgets(const bool v); + public: + std::function on_frequency_loaded{}; + std::function on_range_loaded{}; + + FrequencyLoadView(NavigationView& nav); + + std::string title() const override { return "Load freq."; }; + + private: + void refresh_widgets(const bool v); }; class FrequencyManagerView : public FreqManBaseView { -public: - FrequencyManagerView(NavigationView& nav); - ~FrequencyManagerView(); - - std::string title() const override { return "Freqman"; }; - -private: - std::string desc_buffer { }; - - void refresh_widgets(const bool v); - void on_edit_freq(rf::Frequency f); - void on_edit_desc(NavigationView& nav); - void on_new_category(NavigationView& nav); - void on_delete(); - - Labels labels { - { { 4 * 8 + 4, 26 * 8 }, "Edit:", Color::light_grey() } - }; - - Button button_new_category { - { 23 * 8, 2, 7 * 8, 20 }, - "New" - }; - - Button button_edit_freq { - { 0 * 8, 29 * 8, 14 * 8, 32 }, - "Frequency" - }; - Button button_edit_desc { - { 0 * 8, 34 * 8, 14 * 8, 32 }, - "Description" - }; - - Button button_delete { - { 18 * 8, 27 * 8, 12 * 8, 32 }, - "Delete" - }; + public: + FrequencyManagerView(NavigationView& nav); + ~FrequencyManagerView(); + + std::string title() const override { return "Freqman"; }; + + private: + std::string desc_buffer{}; + + void refresh_widgets(const bool v); + void on_edit_freq(rf::Frequency f); + void on_edit_desc(NavigationView& nav); + void on_new_category(NavigationView& nav); + void on_delete(); + + Labels labels{ + {{4 * 8 + 4, 26 * 8}, "Edit:", Color::light_grey()}}; + + Button button_new_category{ + {23 * 8, 2, 7 * 8, 20}, + "New"}; + + Button button_edit_freq{ + {0 * 8, 29 * 8, 14 * 8, 32}, + "Frequency"}; + Button button_edit_desc{ + {0 * 8, 34 * 8, 14 * 8, 32}, + "Description"}; + + Button button_delete{ + {18 * 8, 27 * 8, 12 * 8, 32}, + "Delete"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_jammer.cpp b/firmware/application/apps/ui_jammer.cpp index 3adbe2ffd..3e8740468 100644 --- a/firmware/application/apps/ui_jammer.cpp +++ b/firmware/application/apps/ui_jammer.cpp @@ -33,366 +33,351 @@ using namespace portapack; namespace ui { void RangeView::focus() { - check_enabled.focus(); + check_enabled.focus(); } extern constexpr Style RangeView::style_info; void RangeView::update_start(rf::Frequency f) { - // Change everything except max - frequency_range.min = f; - button_start.set_text(to_string_short_freq(f)); - - center = (frequency_range.min + frequency_range.max) / 2; - width = abs(frequency_range.max - frequency_range.min); - - button_center.set_text(to_string_short_freq(center)); - button_width.set_text(to_string_short_freq(width)); + // Change everything except max + frequency_range.min = f; + button_start.set_text(to_string_short_freq(f)); + + center = (frequency_range.min + frequency_range.max) / 2; + width = abs(frequency_range.max - frequency_range.min); + + button_center.set_text(to_string_short_freq(center)); + button_width.set_text(to_string_short_freq(width)); } void RangeView::update_stop(rf::Frequency f) { - // Change everything except min - frequency_range.max = f; - button_stop.set_text(to_string_short_freq(f)); - - center = (frequency_range.min + frequency_range.max) / 2; - width = abs(frequency_range.max - frequency_range.min); - - button_center.set_text(to_string_short_freq(center)); - button_width.set_text(to_string_short_freq(width)); + // Change everything except min + frequency_range.max = f; + button_stop.set_text(to_string_short_freq(f)); + + center = (frequency_range.min + frequency_range.max) / 2; + width = abs(frequency_range.max - frequency_range.min); + + button_center.set_text(to_string_short_freq(center)); + button_width.set_text(to_string_short_freq(width)); } void RangeView::update_center(rf::Frequency f) { - // Change min/max/center, keep width - center = f; - button_center.set_text(to_string_short_freq(center)); - - rf::Frequency min = center - (width / 2); - rf::Frequency max = min + width; - - frequency_range.min = min; - button_start.set_text(to_string_short_freq(min)); - - frequency_range.max = max; - button_stop.set_text(to_string_short_freq(max)); + // Change min/max/center, keep width + center = f; + button_center.set_text(to_string_short_freq(center)); + + rf::Frequency min = center - (width / 2); + rf::Frequency max = min + width; + + frequency_range.min = min; + button_start.set_text(to_string_short_freq(min)); + + frequency_range.max = max; + button_stop.set_text(to_string_short_freq(max)); } void RangeView::update_width(uint32_t w) { - // Change min/max/width, keep center - width = w; - - button_width.set_text(to_string_short_freq(width)); - - rf::Frequency min = center - (width / 2); - rf::Frequency max = min + width; - - frequency_range.min = min; - button_start.set_text(to_string_short_freq(min)); - - frequency_range.max = max; - button_stop.set_text(to_string_short_freq(max)); + // Change min/max/width, keep center + width = w; + + button_width.set_text(to_string_short_freq(width)); + + rf::Frequency min = center - (width / 2); + rf::Frequency max = min + width; + + frequency_range.min = min; + button_start.set_text(to_string_short_freq(min)); + + frequency_range.max = max; + button_stop.set_text(to_string_short_freq(max)); } void RangeView::paint(Painter&) { - // Draw lines and arrows - Rect r; - Point p; - Coord c; - - r = button_center.screen_rect(); - p = r.center() + Point(0, r.height() / 2); - - display.draw_line(p, p + Point(0, 10), Color::grey()); - - r = button_width.screen_rect(); - c = r.top() + (r.height() / 2); - - p = {r.left() - 64, c}; - display.draw_line({r.left(), c}, p, Color::grey()); - display.draw_line(p, p + Point(10, -10), Color::grey()); - display.draw_line(p, p + Point(10, 10), Color::grey()); - - p = {r.right() + 64, c}; - display.draw_line({r.right(), c}, p, Color::grey()); - display.draw_line(p, p + Point(-10, -10), Color::grey()); - display.draw_line(p, p + Point(-10, 10), Color::grey()); + // Draw lines and arrows + Rect r; + Point p; + Coord c; + + r = button_center.screen_rect(); + p = r.center() + Point(0, r.height() / 2); + + display.draw_line(p, p + Point(0, 10), Color::grey()); + + r = button_width.screen_rect(); + c = r.top() + (r.height() / 2); + + p = {r.left() - 64, c}; + display.draw_line({r.left(), c}, p, Color::grey()); + display.draw_line(p, p + Point(10, -10), Color::grey()); + display.draw_line(p, p + Point(10, 10), Color::grey()); + + p = {r.right() + 64, c}; + display.draw_line({r.right(), c}, p, Color::grey()); + display.draw_line(p, p + Point(-10, -10), Color::grey()); + display.draw_line(p, p + Point(-10, 10), Color::grey()); } RangeView::RangeView(NavigationView& nav) { - hidden(true); - - add_children({ - &labels, - &check_enabled, - &button_load_range, - &button_start, - &button_stop, - &button_center, - &button_width - }); - - check_enabled.on_select = [this](Checkbox&, bool v) { - frequency_range.enabled = v; - }; - - button_start.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(frequency_range.min); - new_view->on_changed = [this, &button](rf::Frequency f) { - update_start(f); - }; - }; - - button_stop.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(frequency_range.max); - new_view->on_changed = [this, &button](rf::Frequency f) { - update_stop(f); - }; - }; - - button_center.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(center); - new_view->on_changed = [this, &button](rf::Frequency f) { - update_center(f); - }; - }; - - button_width.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(width); - new_view->on_changed = [this, &button](rf::Frequency f) { - update_width(f); - }; - }; - - button_load_range.on_select = [this, &nav](Button&) { - auto load_view = nav.push(); - load_view->on_frequency_loaded = [this](rf::Frequency value) { - update_center(value); - update_width(100000); // 100kHz default jamming bandwidth when loading unique frequency - }; - load_view->on_range_loaded = [this](rf::Frequency start, rf::Frequency stop) { - update_start(start); - update_stop(stop); - }; - }; - - check_enabled.set_value(false); + hidden(true); + + add_children({&labels, + &check_enabled, + &button_load_range, + &button_start, + &button_stop, + &button_center, + &button_width}); + + check_enabled.on_select = [this](Checkbox&, bool v) { + frequency_range.enabled = v; + }; + + button_start.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + update_start(f); + }; + }; + + button_stop.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + update_stop(f); + }; + }; + + button_center.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(center); + new_view->on_changed = [this, &button](rf::Frequency f) { + update_center(f); + }; + }; + + button_width.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(width); + new_view->on_changed = [this, &button](rf::Frequency f) { + update_width(f); + }; + }; + + button_load_range.on_select = [this, &nav](Button&) { + auto load_view = nav.push(); + load_view->on_frequency_loaded = [this](rf::Frequency value) { + update_center(value); + update_width(100000); // 100kHz default jamming bandwidth when loading unique frequency + }; + load_view->on_range_loaded = [this](rf::Frequency start, rf::Frequency stop) { + update_start(start); + update_stop(stop); + }; + }; + + check_enabled.set_value(false); } void JammerView::focus() { - tab_view.focus(); + tab_view.focus(); } JammerView::~JammerView() { - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) { - if (freq) { - transmitter_model.set_tuning_frequency(freq); - text_range_number.set(to_string_dec_uint(range, 2)); - } + if (freq) { + transmitter_model.set_tuning_frequency(freq); + text_range_number.set(to_string_dec_uint(range, 2)); + } } void JammerView::set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration) { - jammer_channels[i].enabled = true; - jammer_channels[i].width = (width * 0xFFFFFFULL) / 1536000; - jammer_channels[i].center = center; - jammer_channels[i].duration = 30720 * duration; + jammer_channels[i].enabled = true; + jammer_channels[i].width = (width * 0xFFFFFFULL) / 1536000; + jammer_channels[i].center = center; + jammer_channels[i].duration = 30720 * duration; } extern constexpr Style JammerView::style_val; extern constexpr Style JammerView::style_cancel; void JammerView::start_tx() { - uint32_t c, i = 0; - size_t num_channels; - rf::Frequency start_freq, range_bw, range_bw_sub, ch_width; - bool out_of_ranges = false; - - size_t hop_value = options_hop.selected_index_value(); - - // Disable all channels by default - for (c = 0; c < JAMMER_MAX_CH; c++) - jammer_channels[c].enabled = false; - - // Generate jamming channels with JAMMER_MAX_CH maximum width - // Convert ranges min/max to center/bw - for (size_t r = 0; r < 3; r++) { - - if (range_views[r]->frequency_range.enabled) { - range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min); - - // Get lower bound - if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max) - start_freq = range_views[r]->frequency_range.min; - else - start_freq = range_views[r]->frequency_range.max; - - if (range_bw >= JAMMER_CH_WIDTH) { - // Split range in multiple channels - num_channels = 0; - range_bw_sub = range_bw; - - do { - range_bw_sub -= JAMMER_CH_WIDTH; - num_channels++; - } while (range_bw_sub >= JAMMER_CH_WIDTH); - - ch_width = range_bw / num_channels; - - for (c = 0; c < num_channels; c++) { - if (i >= JAMMER_MAX_CH) { - out_of_ranges = true; - break; - } - set_jammer_channel(i, ch_width, start_freq + (ch_width / 2) + (ch_width * c), hop_value); - i++; - } - } else { - // Range fits in a single channel - if (i >= JAMMER_MAX_CH) { - out_of_ranges = true; - } else { - set_jammer_channel(i, range_bw, start_freq + (range_bw / 2), hop_value); - i++; - } - } - } - } - - if (!out_of_ranges && i) { - text_range_total.set("/" + to_string_dec_uint(i, 2)); - - jamming = true; - button_transmit.set_style(&style_cancel); - button_transmit.set_text("STOP"); - - transmitter_model.set_sampling_rate(3072000U); - transmitter_model.set_rf_amp(field_amp.value()); - transmitter_model.set_baseband_bandwidth(3500000U); - transmitter_model.set_tx_gain(field_gain.value()); - transmitter_model.enable(); - - baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value()); - mscounter = 0; //euquiq: Reset internal ms counter for do_timer() - } else { - if (out_of_ranges) - nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz."); - else - nav_.display_modal("Error", "No range enabled."); - } + uint32_t c, i = 0; + size_t num_channels; + rf::Frequency start_freq, range_bw, range_bw_sub, ch_width; + bool out_of_ranges = false; + + size_t hop_value = options_hop.selected_index_value(); + + // Disable all channels by default + for (c = 0; c < JAMMER_MAX_CH; c++) + jammer_channels[c].enabled = false; + + // Generate jamming channels with JAMMER_MAX_CH maximum width + // Convert ranges min/max to center/bw + for (size_t r = 0; r < 3; r++) { + if (range_views[r]->frequency_range.enabled) { + range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min); + + // Get lower bound + if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max) + start_freq = range_views[r]->frequency_range.min; + else + start_freq = range_views[r]->frequency_range.max; + + if (range_bw >= JAMMER_CH_WIDTH) { + // Split range in multiple channels + num_channels = 0; + range_bw_sub = range_bw; + + do { + range_bw_sub -= JAMMER_CH_WIDTH; + num_channels++; + } while (range_bw_sub >= JAMMER_CH_WIDTH); + + ch_width = range_bw / num_channels; + + for (c = 0; c < num_channels; c++) { + if (i >= JAMMER_MAX_CH) { + out_of_ranges = true; + break; + } + set_jammer_channel(i, ch_width, start_freq + (ch_width / 2) + (ch_width * c), hop_value); + i++; + } + } else { + // Range fits in a single channel + if (i >= JAMMER_MAX_CH) { + out_of_ranges = true; + } else { + set_jammer_channel(i, range_bw, start_freq + (range_bw / 2), hop_value); + i++; + } + } + } + } + + if (!out_of_ranges && i) { + text_range_total.set("/" + to_string_dec_uint(i, 2)); + + jamming = true; + button_transmit.set_style(&style_cancel); + button_transmit.set_text("STOP"); + + transmitter_model.set_sampling_rate(3072000U); + transmitter_model.set_rf_amp(field_amp.value()); + transmitter_model.set_baseband_bandwidth(3500000U); + transmitter_model.set_tx_gain(field_gain.value()); + transmitter_model.enable(); + + baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value()); + mscounter = 0; // euquiq: Reset internal ms counter for do_timer() + } else { + if (out_of_ranges) + nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz."); + else + nav_.display_modal("Error", "No range enabled."); + } } void JammerView::stop_tx() { - button_transmit.set_style(&style_val); - button_transmit.set_text("START"); - transmitter_model.disable(); - radio::disable(); - baseband::set_jammer(false, JammerType::TYPE_FSK, 0); - jamming = false; - cooling = false; + button_transmit.set_style(&style_val); + button_transmit.set_text("START"); + transmitter_model.disable(); + radio::disable(); + baseband::set_jammer(false, JammerType::TYPE_FSK, 0); + jamming = false; + cooling = false; } -//called each 1/60th of second +// called each 1/60th of second void JammerView::on_timer() { - if (++mscounter == 60) { - mscounter = 0; - if (jamming) - { - if (cooling) - { - if (++seconds >= field_timepause.value()) - { //Re-start TX - transmitter_model.enable(); - button_transmit.set_text("STOP"); - baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value()); - - int32_t jitter_amount = field_jitter.value(); - if (jitter_amount) - { - lfsr_v = lfsr_iterate(lfsr_v); - jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount); - mscounter += jitter_amount; - } - - cooling = false; - seconds = 0; - } - } - else - { - if (++seconds >= field_timetx.value()) //Start cooling period: - { - transmitter_model.disable(); - button_transmit.set_text("PAUSED"); - baseband::set_jammer(false, JammerType::TYPE_FSK, 0); - - int32_t jitter_amount = field_jitter.value(); - if (jitter_amount) - { - lfsr_v = lfsr_iterate(lfsr_v); - jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount); - mscounter += jitter_amount; - } - - cooling = true; - seconds = 0; - } - } - - } - - } + if (++mscounter == 60) { + mscounter = 0; + if (jamming) { + if (cooling) { + if (++seconds >= field_timepause.value()) { // Re-start TX + transmitter_model.enable(); + button_transmit.set_text("STOP"); + baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value()); + + int32_t jitter_amount = field_jitter.value(); + if (jitter_amount) { + lfsr_v = lfsr_iterate(lfsr_v); + jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount); + mscounter += jitter_amount; + } + + cooling = false; + seconds = 0; + } + } else { + if (++seconds >= field_timetx.value()) // Start cooling period: + { + transmitter_model.disable(); + button_transmit.set_text("PAUSED"); + baseband::set_jammer(false, JammerType::TYPE_FSK, 0); + + int32_t jitter_amount = field_jitter.value(); + if (jitter_amount) { + lfsr_v = lfsr_iterate(lfsr_v); + jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount); + mscounter += jitter_amount; + } + + cooling = true; + seconds = 0; + } + } + } + } } - + JammerView::JammerView( - NavigationView& nav -) : nav_ { nav } -{ - Rect view_rect = { 0, 3 * 8, 240, 80 }; - baseband::run_image(portapack::spi_flash::image_tag_jammer); - - add_children({ - &tab_view, - &view_range_a, - &view_range_b, - &view_range_c, - &labels, - &options_type, - &text_range_number, - &text_range_total, - &options_speed, - &options_hop, - &field_timetx, - &field_timepause, - &field_jitter, - &field_gain, - &field_amp, - &button_transmit - }); - - view_range_a.set_parent_rect(view_rect); - view_range_b.set_parent_rect(view_rect); - view_range_c.set_parent_rect(view_rect); - - options_type.set_selected_index(3); // Rand CW - options_speed.set_selected_index(3); // 10kHz - options_hop.set_selected_index(1); // 50ms - button_transmit.set_style(&style_val); - - field_timetx.set_value(30); - field_timepause.set_value(1); - field_gain.set_value(transmitter_model.tx_gain()); - field_amp.set_value(transmitter_model.rf_amp()); - - button_transmit.on_select = [this](Button&) { - if (jamming || cooling) - stop_tx(); - else - start_tx(); - }; + NavigationView& nav) + : nav_{nav} { + Rect view_rect = {0, 3 * 8, 240, 80}; + baseband::run_image(portapack::spi_flash::image_tag_jammer); + + add_children({&tab_view, + &view_range_a, + &view_range_b, + &view_range_c, + &labels, + &options_type, + &text_range_number, + &text_range_total, + &options_speed, + &options_hop, + &field_timetx, + &field_timepause, + &field_jitter, + &field_gain, + &field_amp, + &button_transmit}); + + view_range_a.set_parent_rect(view_rect); + view_range_b.set_parent_rect(view_rect); + view_range_c.set_parent_rect(view_rect); + + options_type.set_selected_index(3); // Rand CW + options_speed.set_selected_index(3); // 10kHz + options_hop.set_selected_index(1); // 50ms + button_transmit.set_style(&style_val); + + field_timetx.set_value(30); + field_timepause.set_value(1); + field_gain.set_value(transmitter_model.tx_gain()); + field_amp.set_value(transmitter_model.rf_amp()); + + button_transmit.on_select = [this](Button&) { + if (jamming || cooling) + stop_tx(); + else + start_tx(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_jammer.hpp b/firmware/application/apps/ui_jammer.hpp index 9f2b20791..97a7270f9 100644 --- a/firmware/application/apps/ui_jammer.hpp +++ b/firmware/application/apps/ui_jammer.hpp @@ -35,238 +35,217 @@ using namespace jammer; namespace ui { class RangeView : public View { -public: - RangeView(NavigationView& nav); - - void focus() override; - void paint(Painter&) override; - - jammer_range_t frequency_range { false, 0, 0 }; - -private: - void update_start(rf::Frequency f); - void update_stop(rf::Frequency f); - void update_center(rf::Frequency f); - void update_width(uint32_t w); - - uint32_t width { }; - rf::Frequency center { }; - - static constexpr Style style_info { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - - Labels labels { - { { 2 * 8, 8 * 8 + 4 }, "Start", Color::light_grey() }, - { { 23 * 8, 8 * 8 + 4 }, "Stop", Color::light_grey() }, - { { 12 * 8, 5 * 8 - 4}, "Center", Color::light_grey() }, - { { 12 * 8 + 4, 13 * 8 }, "Width", Color::light_grey() } - }; - - Checkbox check_enabled { - { 1 * 8, 4 }, - 12, - "Enable range" - }; - - Button button_load_range { - { 18 * 8, 4, 12 * 8, 24 }, - "Load range" - }; - - Button button_start { - { 0 * 8, 11 * 8, 11 * 8, 28 }, - "" - }; - Button button_stop { - { 19 * 8, 11 * 8, 11 * 8, 28 }, - "" - }; - Button button_center { - { 76, 4 * 15 - 4, 11 * 8, 28 }, - "" - }; - Button button_width { - { 76, 8 * 15, 11 * 8, 28 }, - "" - }; + public: + RangeView(NavigationView& nav); + + void focus() override; + void paint(Painter&) override; + + jammer_range_t frequency_range{false, 0, 0}; + + private: + void update_start(rf::Frequency f); + void update_stop(rf::Frequency f); + void update_center(rf::Frequency f); + void update_width(uint32_t w); + + uint32_t width{}; + rf::Frequency center{}; + + static constexpr Style style_info{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + Labels labels{ + {{2 * 8, 8 * 8 + 4}, "Start", Color::light_grey()}, + {{23 * 8, 8 * 8 + 4}, "Stop", Color::light_grey()}, + {{12 * 8, 5 * 8 - 4}, "Center", Color::light_grey()}, + {{12 * 8 + 4, 13 * 8}, "Width", Color::light_grey()}}; + + Checkbox check_enabled{ + {1 * 8, 4}, + 12, + "Enable range"}; + + Button button_load_range{ + {18 * 8, 4, 12 * 8, 24}, + "Load range"}; + + Button button_start{ + {0 * 8, 11 * 8, 11 * 8, 28}, + ""}; + Button button_stop{ + {19 * 8, 11 * 8, 11 * 8, 28}, + ""}; + Button button_center{ + {76, 4 * 15 - 4, 11 * 8, 28}, + ""}; + Button button_width{ + {76, 8 * 15, 11 * 8, 28}, + ""}; }; class JammerView : public View { -public: - JammerView(NavigationView& nav); - ~JammerView(); - - JammerView(const JammerView&) = delete; - JammerView(JammerView&&) = delete; - JammerView& operator=(const JammerView&) = delete; - JammerView& operator=(JammerView&&) = delete; - - void focus() override; - - std::string title() const override { return "Jammer TX"; }; - -private: - NavigationView& nav_; - - void start_tx(); - void on_timer(); - void stop_tx(); - void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration); - void on_retune(const rf::Frequency freq, const uint32_t range); - - JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data; - bool jamming { false }; - bool cooling { false }; //euquiq: Indicates jammer in cooldown - uint16_t seconds = 0; //euquiq: seconds counter for toggling tx / cooldown - int16_t mscounter = 0; //euquiq: Internal ms counter for do_timer() - lfsr_word_t lfsr_v = 1; //euquiq: Used to generate "random" Jitter - - static constexpr Style style_val { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - static constexpr Style style_cancel { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - RangeView view_range_a { nav_ }; - RangeView view_range_b { nav_ }; - RangeView view_range_c { nav_ }; - - std::array range_views { { &view_range_a, &view_range_b, &view_range_c } }; - - TabView tab_view { - { "Range 1", Color::white(), range_views[0] }, - { "Range 2", Color::white(), range_views[1] }, - { "Range 3", Color::white(), range_views[2] }, - }; - - Labels labels { - { { 2 * 8, 23 * 8 }, "Type:", Color::light_grey() }, - { { 1 * 8, 25 * 8 }, "Speed:", Color::light_grey() }, - { { 3 * 8, 27 * 8 }, "Hop:", Color::light_grey() }, - { { 4 * 8, 29 * 8 }, "TX:", Color::light_grey() }, - { { 1 * 8, 31 * 8 }, "Sle3p:", Color::light_grey() }, //euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty. - { { 0 * 8, 33 * 8 }, "Jitter:", Color::light_grey() }, //Maybe the repository curator can keep the "mystype" for some versions. - { { 11 * 8, 29 * 8 }, "Secs.", Color::light_grey() }, - { { 11 * 8, 31 * 8 }, "Secs.", Color::light_grey() }, - { { 11 * 8, 33 * 8 }, "/60", Color::light_grey() }, - { { 2 * 8, 35 * 8 }, "Gain:", Color::light_grey() }, - { { 11 * 8, 35 * 8 }, "A:", Color::light_grey() } - }; - - OptionsField options_type { - { 7 * 8, 23 * 8 }, - 8, - { - { "Rand FSK", 0 }, - { "FM tone", 1 }, - { "CW sweep", 2 }, - { "Rand CW", 3 }, - } - }; - - Text text_range_number { - { 16 * 8, 23 * 8, 2 * 8, 16 }, - "--" - }; - Text text_range_total { - { 18 * 8, 23 * 8, 3 * 8, 16 }, - "/--" - }; - - OptionsField options_speed { - { 7 * 8, 25 * 8 }, - 6, - { - { "10Hz ", 10 }, - { "100Hz ", 100 }, - { "1kHz ", 1000 }, - { "10kHz ", 10000 }, - { "100kHz", 100000 } - } - }; - - OptionsField options_hop { - { 7 * 8, 27 * 8 }, - 5, - { - { "10ms ", 1 }, - { "50ms ", 5 }, - { "100ms", 10 }, - { "1s ", 100 }, - { "2s ", 200 }, - { "5s ", 500 }, - { "10s ", 1000 } - } - }; - - NumberField field_timetx { - { 7 * 8, 29 * 8 }, - 3, - { 1, 180 }, - 1, - ' ', - }; - - NumberField field_timepause { - { 8 * 8, 31 * 8 }, - 2, - { 1, 60 }, - 1, - ' ', - }; - - NumberField field_jitter { - { 8 * 8, 33 * 8 }, - 2, - { 1, 60 }, - 1, - ' ', - }; - - NumberField field_gain { - { 8 * 8, 35 * 8 }, - 2, - { 0, 47 }, - 1, - ' ', - }; - - NumberField field_amp { - { 13 * 8, 35 * 8 }, - 1, - { 0, 1 }, - 1, - ' ', - }; - - Button button_transmit { - { 148, 216, 80, 80}, - "START" - }; - - MessageHandlerRegistration message_handler_retune { - Message::ID::Retune, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_retune(message->freq, message->range); - } - }; - - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->on_timer(); - } - }; + public: + JammerView(NavigationView& nav); + ~JammerView(); + + JammerView(const JammerView&) = delete; + JammerView(JammerView&&) = delete; + JammerView& operator=(const JammerView&) = delete; + JammerView& operator=(JammerView&&) = delete; + + void focus() override; + + std::string title() const override { return "Jammer TX"; }; + + private: + NavigationView& nav_; + + void start_tx(); + void on_timer(); + void stop_tx(); + void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration); + void on_retune(const rf::Frequency freq, const uint32_t range); + + JammerChannel* jammer_channels = (JammerChannel*)shared_memory.bb_data.data; + bool jamming{false}; + bool cooling{false}; // euquiq: Indicates jammer in cooldown + uint16_t seconds = 0; // euquiq: seconds counter for toggling tx / cooldown + int16_t mscounter = 0; // euquiq: Internal ms counter for do_timer() + lfsr_word_t lfsr_v = 1; // euquiq: Used to generate "random" Jitter + + static constexpr Style style_val{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + static constexpr Style style_cancel{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + RangeView view_range_a{nav_}; + RangeView view_range_b{nav_}; + RangeView view_range_c{nav_}; + + std::array range_views{{&view_range_a, &view_range_b, &view_range_c}}; + + TabView tab_view{ + {"Range 1", Color::white(), range_views[0]}, + {"Range 2", Color::white(), range_views[1]}, + {"Range 3", Color::white(), range_views[2]}, + }; + + Labels labels{ + {{2 * 8, 23 * 8}, "Type:", Color::light_grey()}, + {{1 * 8, 25 * 8}, "Speed:", Color::light_grey()}, + {{3 * 8, 27 * 8}, "Hop:", Color::light_grey()}, + {{4 * 8, 29 * 8}, "TX:", Color::light_grey()}, + {{1 * 8, 31 * 8}, "Sle3p:", Color::light_grey()}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty. + {{0 * 8, 33 * 8}, "Jitter:", Color::light_grey()}, // Maybe the repository curator can keep the "mystype" for some versions. + {{11 * 8, 29 * 8}, "Secs.", Color::light_grey()}, + {{11 * 8, 31 * 8}, "Secs.", Color::light_grey()}, + {{11 * 8, 33 * 8}, "/60", Color::light_grey()}, + {{2 * 8, 35 * 8}, "Gain:", Color::light_grey()}, + {{11 * 8, 35 * 8}, "A:", Color::light_grey()}}; + + OptionsField options_type{ + {7 * 8, 23 * 8}, + 8, + { + {"Rand FSK", 0}, + {"FM tone", 1}, + {"CW sweep", 2}, + {"Rand CW", 3}, + }}; + + Text text_range_number{ + {16 * 8, 23 * 8, 2 * 8, 16}, + "--"}; + Text text_range_total{ + {18 * 8, 23 * 8, 3 * 8, 16}, + "/--"}; + + OptionsField options_speed{ + {7 * 8, 25 * 8}, + 6, + {{"10Hz ", 10}, + {"100Hz ", 100}, + {"1kHz ", 1000}, + {"10kHz ", 10000}, + {"100kHz", 100000}}}; + + OptionsField options_hop{ + {7 * 8, 27 * 8}, + 5, + {{"10ms ", 1}, + {"50ms ", 5}, + {"100ms", 10}, + {"1s ", 100}, + {"2s ", 200}, + {"5s ", 500}, + {"10s ", 1000}}}; + + NumberField field_timetx{ + {7 * 8, 29 * 8}, + 3, + {1, 180}, + 1, + ' ', + }; + + NumberField field_timepause{ + {8 * 8, 31 * 8}, + 2, + {1, 60}, + 1, + ' ', + }; + + NumberField field_jitter{ + {8 * 8, 33 * 8}, + 2, + {1, 60}, + 1, + ' ', + }; + + NumberField field_gain{ + {8 * 8, 35 * 8}, + 2, + {0, 47}, + 1, + ' ', + }; + + NumberField field_amp{ + {13 * 8, 35 * 8}, + 1, + {0, 1}, + 1, + ' ', + }; + + Button button_transmit{ + {148, 216, 80, 80}, + "START"}; + + MessageHandlerRegistration message_handler_retune{ + Message::ID::Retune, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_retune(message->freq, message->range); + }}; + + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->on_timer(); + }}; }; } /* namespace ui */ - diff --git a/firmware/application/apps/ui_keyfob.cpp b/firmware/application/apps/ui_keyfob.cpp index b85e2d682..47c223bb7 100644 --- a/firmware/application/apps/ui_keyfob.cpp +++ b/firmware/application/apps/ui_keyfob.cpp @@ -31,231 +31,228 @@ using namespace portapack; namespace ui { uint8_t KeyfobView::subaru_get_checksum() { - uint8_t checksum = 0; - - for (size_t i = 0; i < 9; i++) { - checksum ^= (frame[i] & 0x0F); // 00 11 22 33 44 55 66 77 88 9- - checksum ^= ((frame[i] >> 4) & 0x0F); - } - - checksum ^= ((frame[9] >> 4) & 0x0F); - checksum++; - checksum &= 0x0F; - - return checksum; + uint8_t checksum = 0; + + for (size_t i = 0; i < 9; i++) { + checksum ^= (frame[i] & 0x0F); // 00 11 22 33 44 55 66 77 88 9- + checksum ^= ((frame[i] >> 4) & 0x0F); + } + + checksum ^= ((frame[9] >> 4) & 0x0F); + checksum++; + checksum &= 0x0F; + + return checksum; } bool KeyfobView::subaru_is_valid() { - if (frame[0] != 0x55) - return false; - - if (subaru_get_checksum() != (frame[9] & 0x0F)) - return false; - - return true; + if (frame[0] != 0x55) + return false; + + if (subaru_get_checksum() != (frame[9] & 0x0F)) + return false; + + return true; } uint16_t KeyfobView::subaru_get_code() { - // 77777777 88888888 9999 - return (frame[7] << 12) | (frame[8] << 4) | (frame[9] >> 4); + // 77777777 88888888 9999 + return (frame[7] << 12) | (frame[8] << 4) | (frame[9] >> 4); } void KeyfobView::subaru_set_code(const uint16_t code) { - frame[7] = (code >> 12) & 0xFF; - frame[8] = (code >> 4) & 0xFF; - frame[9] &= 0x0F; - frame[9] |= (code & 0x0F) << 4; + frame[7] = (code >> 12) & 0xFF; + frame[8] = (code >> 4) & 0xFF; + frame[9] &= 0x0F; + frame[9] |= (code & 0x0F) << 4; } int32_t KeyfobView::subaru_get_command() { - uint32_t command_a = frame[5] & 0x0F; - uint32_t command_b = frame[6] & 0x0F; - - if (command_a != command_b) - return -1; - - return command_a; + uint32_t command_a = frame[5] & 0x0F; + uint32_t command_b = frame[6] & 0x0F; + + if (command_a != command_b) + return -1; + + return command_a; } void KeyfobView::subaru_set_command(const uint32_t command) { - frame[5] &= 0xF0; - frame[5] |= command; - frame[6] &= 0xF0; - frame[6] |= command; + frame[5] &= 0xF0; + frame[5] |= command; + frame[6] &= 0xF0; + frame[6] |= command; } void KeyfobView::generate_payload(size_t& bitstream_length) { - for (size_t i = 0; i < (10 * 8); i++) { - if (frame[i >> 3] & (1 << (7 - (i & 7)))) - bitstream_append(bitstream_length, 2, 0b10); - else - bitstream_append(bitstream_length, 2, 0b01); - } + for (size_t i = 0; i < (10 * 8); i++) { + if (frame[i >> 3] & (1 << (7 - (i & 7)))) + bitstream_append(bitstream_length, 2, 0b10); + else + bitstream_append(bitstream_length, 2, 0b01); + } } size_t KeyfobView::generate_frame() { - size_t bitstream_length = 0; - uint64_t payload; - - // Symfield word to frame - payload = field_payload_a.value_hex_u64(); - for (size_t i = 0; i < 5; i++) { - frame[4 - i] = payload & 0xFF; - payload >>= 8; - } - payload = field_payload_b.value_hex_u64(); - for (size_t i = 0; i < 5; i++) { - frame[9 - i] = payload & 0xFF; - payload >>= 8; - } - - // Recompute checksum - frame[9] = (frame[9] & 0xF0) | subaru_get_checksum(); - update_symfields(); - - // Preamble: 128x 01 - for (size_t i = 0; i < 128; i++) - bitstream_append(bitstream_length, 2, 0b01); - - // Space: 4x 0 - bitstream_append(bitstream_length, 4, 0b0000); - - // Payload - generate_payload(bitstream_length); - - // Space: 8x 0 - bitstream_append(bitstream_length, 8, 0b00000000); - - // Payload again - generate_payload(bitstream_length); - - return bitstream_length; + size_t bitstream_length = 0; + uint64_t payload; + + // Symfield word to frame + payload = field_payload_a.value_hex_u64(); + for (size_t i = 0; i < 5; i++) { + frame[4 - i] = payload & 0xFF; + payload >>= 8; + } + payload = field_payload_b.value_hex_u64(); + for (size_t i = 0; i < 5; i++) { + frame[9 - i] = payload & 0xFF; + payload >>= 8; + } + + // Recompute checksum + frame[9] = (frame[9] & 0xF0) | subaru_get_checksum(); + update_symfields(); + + // Preamble: 128x 01 + for (size_t i = 0; i < 128; i++) + bitstream_append(bitstream_length, 2, 0b01); + + // Space: 4x 0 + bitstream_append(bitstream_length, 4, 0b0000); + + // Payload + generate_payload(bitstream_length); + + // Space: 8x 0 + bitstream_append(bitstream_length, 8, 0b00000000); + + // Payload again + generate_payload(bitstream_length); + + return bitstream_length; } void KeyfobView::focus() { - options_make.focus(); + options_make.focus(); } KeyfobView::~KeyfobView() { - // save app settings - settings.save("tx_keyfob", &app_settings); + // save app settings + settings.save("tx_keyfob", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void KeyfobView::update_progress(const uint32_t progress) { - text_status.set("Repeat #" + to_string_dec_uint(progress)); + text_status.set("Repeat #" + to_string_dec_uint(progress)); } void KeyfobView::on_tx_progress(const uint32_t progress, const bool done) { - if (!done) { - // Repeating... - update_progress(progress + 1); - progressbar.set_value(progress); - } else { - transmitter_model.disable(); - text_status.set("Done"); - progressbar.set_value(0); - tx_view.set_transmitting(false); - } + if (!done) { + // Repeating... + update_progress(progress + 1); + progressbar.set_value(progress); + } else { + transmitter_model.disable(); + text_status.set("Done"); + progressbar.set_value(0); + tx_view.set_transmitting(false); + } } void KeyfobView::on_make_change(size_t index) { - (void)index; + (void)index; } // DEBUG void KeyfobView::update_symfields() { - for (size_t i = 0; i < 5; i++) { - field_payload_a.set_sym(i << 1, frame[i] >> 4); - field_payload_a.set_sym((i << 1) + 1, frame[i] & 0x0F); - } - for (size_t i = 0; i < 5; i++) { - field_payload_b.set_sym(i << 1, frame[5 + i] >> 4); - field_payload_b.set_sym((i << 1) + 1, frame[5 + i] & 0x0F); - } + for (size_t i = 0; i < 5; i++) { + field_payload_a.set_sym(i << 1, frame[i] >> 4); + field_payload_a.set_sym((i << 1) + 1, frame[i] & 0x0F); + } + for (size_t i = 0; i < 5; i++) { + field_payload_b.set_sym(i << 1, frame[5 + i] >> 4); + field_payload_b.set_sym((i << 1) + 1, frame[5 + i] & 0x0F); + } } void KeyfobView::on_command_change(uint32_t value) { - subaru_set_command(value); - update_symfields(); + subaru_set_command(value); + update_symfields(); } void KeyfobView::start_tx() { - progressbar.set_max(repeats - 1); - - update_progress(1); - - size_t bitstream_length = generate_frame(); - - transmitter_model.set_sampling_rate(OOK_SAMPLERATE); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_ook_data( - bitstream_length, - subaru_samples_per_bit, - repeats, - 200 // Pause symbols - ); + progressbar.set_max(repeats - 1); + + update_progress(1); + + size_t bitstream_length = generate_frame(); + + transmitter_model.set_sampling_rate(OOK_SAMPLERATE); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_ook_data( + bitstream_length, + subaru_samples_per_bit, + repeats, + 200 // Pause symbols + ); } KeyfobView::KeyfobView( - NavigationView& nav -) : nav_ { nav } -{ - baseband::run_image(portapack::spi_flash::image_tag_ook); - - add_children({ - &labels, - &options_make, - &options_command, - &field_payload_a, - &field_payload_b, - &text_status, - &progressbar, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_keyfob", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - frame[0] = 0x55; - update_symfields(); - - options_make.on_change = [this](size_t index, int32_t) { - on_make_change(index); - }; - - options_command.on_change = [this](size_t, int32_t value) { - on_command_change(value); - }; - - options_make.set_selected_index(0); - - transmitter_model.set_tuning_frequency(433920000); // Fixed 433.92MHz - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(transmitter_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - }; + NavigationView& nav) + : nav_{nav} { + baseband::run_image(portapack::spi_flash::image_tag_ook); + + add_children({&labels, + &options_make, + &options_command, + &field_payload_a, + &field_payload_b, + &text_status, + &progressbar, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_keyfob", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + frame[0] = 0x55; + update_symfields(); + + options_make.on_change = [this](size_t index, int32_t) { + on_make_change(index); + }; + + options_command.on_change = [this](size_t, int32_t value) { + on_command_change(value); + }; + + options_make.set_selected_index(0); + + transmitter_model.set_tuning_frequency(433920000); // Fixed 433.92MHz + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_keyfob.hpp b/firmware/application/apps/ui_keyfob.hpp index 3cc8419ab..e571339ca 100644 --- a/firmware/application/apps/ui_keyfob.hpp +++ b/firmware/application/apps/ui_keyfob.hpp @@ -31,106 +31,94 @@ using namespace encoders; namespace ui { class KeyfobView : public View { -public: - KeyfobView(NavigationView& nav); - ~KeyfobView(); - - void focus() override; - - std::string title() const override { return "Key fob TX"; }; - -private: - NavigationView& nav_; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - // 1013210ns / bit - static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321); - static constexpr uint32_t repeats = 4; - - uint8_t frame[10] { }; - - void generate_payload(size_t& bitstream_length); - size_t generate_frame(); - void start_tx(); - void on_tx_progress(const uint32_t progress, const bool done); - void update_progress(const uint32_t progress); - void on_make_change(size_t index); - void on_command_change(uint32_t value); - - // DEBUG - void update_symfields(); - - uint8_t subaru_get_checksum(); - bool subaru_is_valid(); - uint16_t subaru_get_code(); - void subaru_set_code(const uint16_t code); - int32_t subaru_get_command(); - void subaru_set_command(const uint32_t command); - - Labels labels { - { { 5 * 8, 1 * 16 }, "Make:", Color::light_grey() }, - { { 2 * 8, 2 * 16 }, "Command:", Color::light_grey() }, - { { 2 * 8, 4 * 16 }, "Payload: #####", Color::light_grey() }, - { { 2 * 8, 7 * 16 }, "Checksum is fixed just", Color::light_grey() }, - { { 2 * 8, 8 * 16 }, "before transmission.", Color::light_grey() }, - }; - - OptionsField options_make { - { 10 * 8, 1 * 16 }, - 8, - { - { "Subaru", 0 } - } - }; - - OptionsField options_command { - { 10 * 8, 2 * 16 }, - 6, - { - { "Lock", 1 }, - { "Unlock", 2 }, - { "Trunk", 11 }, - { "Panic", 10 } - } - }; - - SymField field_payload_a { - { 2 * 8, 5 * 16 }, - 10, - SymField::SYMFIELD_HEX - }; - SymField field_payload_b { - { 13 * 8, 5 * 16 }, - 10, - SymField::SYMFIELD_HEX - }; - - Text text_status { - { 2 * 8, 13 * 16, 128, 16 }, - "Ready" - }; - - ProgressBar progressbar { - { 2 * 8, 13 * 16 + 20, 208, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 0, - 15, - true - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + KeyfobView(NavigationView& nav); + ~KeyfobView(); + + void focus() override; + + std::string title() const override { return "Key fob TX"; }; + + private: + NavigationView& nav_; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + // 1013210ns / bit + static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321); + static constexpr uint32_t repeats = 4; + + uint8_t frame[10]{}; + + void generate_payload(size_t& bitstream_length); + size_t generate_frame(); + void start_tx(); + void on_tx_progress(const uint32_t progress, const bool done); + void update_progress(const uint32_t progress); + void on_make_change(size_t index); + void on_command_change(uint32_t value); + + // DEBUG + void update_symfields(); + + uint8_t subaru_get_checksum(); + bool subaru_is_valid(); + uint16_t subaru_get_code(); + void subaru_set_code(const uint16_t code); + int32_t subaru_get_command(); + void subaru_set_command(const uint32_t command); + + Labels labels{ + {{5 * 8, 1 * 16}, "Make:", Color::light_grey()}, + {{2 * 8, 2 * 16}, "Command:", Color::light_grey()}, + {{2 * 8, 4 * 16}, "Payload: #####", Color::light_grey()}, + {{2 * 8, 7 * 16}, "Checksum is fixed just", Color::light_grey()}, + {{2 * 8, 8 * 16}, "before transmission.", Color::light_grey()}, + }; + + OptionsField options_make{ + {10 * 8, 1 * 16}, + 8, + {{"Subaru", 0}}}; + + OptionsField options_command{ + {10 * 8, 2 * 16}, + 6, + {{"Lock", 1}, + {"Unlock", 2}, + {"Trunk", 11}, + {"Panic", 10}}}; + + SymField field_payload_a{ + {2 * 8, 5 * 16}, + 10, + SymField::SYMFIELD_HEX}; + SymField field_payload_b{ + {13 * 8, 5 * 16}, + 10, + SymField::SYMFIELD_HEX}; + + Text text_status{ + {2 * 8, 13 * 16, 128, 16}, + "Ready"}; + + ProgressBar progressbar{ + {2 * 8, 13 * 16 + 20, 208, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 0, + 15, + true}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_lcr.cpp b/firmware/application/apps/ui_lcr.cpp index 400903732..0141ad2ef 100644 --- a/firmware/application/apps/ui_lcr.cpp +++ b/firmware/application/apps/ui_lcr.cpp @@ -36,17 +36,17 @@ using namespace portapack; namespace ui { void LCRView::focus() { - button_set_rgsb.focus(); + button_set_rgsb.focus(); } LCRView::~LCRView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_lcr", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_lcr", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } /* @@ -59,242 +59,231 @@ final_str += "bps "; text_recap.set(final_str);*/ void LCRView::update_progress() { - if (tx_mode == IDLE) { - text_status.set("Ready"); - progress.set_value(0); - } else { - std::string progress_str = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(persistent_memory::modem_repeat()) + - " " + to_string_dec_uint(scan_index + 1) + "/" + to_string_dec_uint(scan_count); - - text_status.set(progress_str); - - if (tx_mode == SINGLE) - progress.set_value(repeat_index); - else if (tx_mode == SCAN) - progress.set_value(scan_progress); - } + if (tx_mode == IDLE) { + text_status.set("Ready"); + progress.set_value(0); + } else { + std::string progress_str = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(persistent_memory::modem_repeat()) + + " " + to_string_dec_uint(scan_index + 1) + "/" + to_string_dec_uint(scan_count); + + text_status.set(progress_str); + + if (tx_mode == SINGLE) + progress.set_value(repeat_index); + else if (tx_mode == SCAN) + progress.set_value(scan_progress); + } } void LCRView::on_tx_progress(const uint32_t progress, const bool done) { - if (!done) { - // Repeating... - repeat_index = progress + 1; - - if (tx_mode == SCAN) - scan_progress++; - } else { - // Done transmitting - tx_view.set_transmitting(false); - transmitter_model.disable(); - - if ((tx_mode == SCAN) && (scan_index < (scan_count - 1))) { - // Next address - scan_index++; - scan_progress++; - repeat_index = 1; - start_tx(true); - } else { - tx_mode = IDLE; - } - } - - update_progress(); + if (!done) { + // Repeating... + repeat_index = progress + 1; + + if (tx_mode == SCAN) + scan_progress++; + } else { + // Done transmitting + tx_view.set_transmitting(false); + transmitter_model.disable(); + + if ((tx_mode == SCAN) && (scan_index < (scan_count - 1))) { + // Next address + scan_index++; + scan_progress++; + repeat_index = 1; + start_tx(true); + } else { + tx_mode = IDLE; + } + } + + update_progress(); } void LCRView::start_tx(const bool scan) { - uint32_t repeats = persistent_memory::modem_repeat(); - - if (scan) { - if (tx_mode != SCAN) { - scan_index = 0; - scan_count = scan_list[options_scanlist.selected_index()].count; - scan_progress = 1; - repeat_index = 1; - tx_mode = SCAN; - progress.set_max(scan_count * repeats); - update_progress(); - } - rgsb = scan_list[options_scanlist.selected_index()].addresses[scan_index]; - button_set_rgsb.set_text(rgsb); - } else { - tx_mode = SINGLE; - repeat_index = 1; - scan_count = 1; - scan_index = 0; - progress.set_max(repeats); - update_progress(); - } - - std::vector litterals_list; - - for (size_t i = 0; i < LCR_MAX_AM; i++) { - if (checkboxes[i].value()) - litterals_list.push_back(litteral[i]); - } - - modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data); - - transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency()); - transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data)); - - baseband::set_afsk_data( - AFSK_TX_SAMPLERATE / persistent_memory::modem_baudrate(), - persistent_memory::afsk_mark_freq(), - persistent_memory::afsk_space_freq(), - repeats, - transmitter_model.channel_bandwidth(), - serializer::symbol_count(persistent_memory::serial_format()) - ); + uint32_t repeats = persistent_memory::modem_repeat(); + + if (scan) { + if (tx_mode != SCAN) { + scan_index = 0; + scan_count = scan_list[options_scanlist.selected_index()].count; + scan_progress = 1; + repeat_index = 1; + tx_mode = SCAN; + progress.set_max(scan_count * repeats); + update_progress(); + } + rgsb = scan_list[options_scanlist.selected_index()].addresses[scan_index]; + button_set_rgsb.set_text(rgsb); + } else { + tx_mode = SINGLE; + repeat_index = 1; + scan_count = 1; + scan_index = 0; + progress.set_max(repeats); + update_progress(); + } + + std::vector litterals_list; + + for (size_t i = 0; i < LCR_MAX_AM; i++) { + if (checkboxes[i].value()) + litterals_list.push_back(litteral[i]); + } + + modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data); + + transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency()); + transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data)); + + baseband::set_afsk_data( + AFSK_TX_SAMPLERATE / persistent_memory::modem_baudrate(), + persistent_memory::afsk_mark_freq(), + persistent_memory::afsk_space_freq(), + repeats, + transmitter_model.channel_bandwidth(), + serializer::symbol_count(persistent_memory::serial_format())); } void LCRView::on_button_set_am(NavigationView& nav, int16_t button_id) { - text_prompt( - nav, - litteral[button_id], - 7, - [this, button_id](std::string& buffer) { - texts[button_id].set(buffer); - }); + text_prompt( + nav, + litteral[button_id], + 7, + [this, button_id](std::string& buffer) { + texts[button_id].set(buffer); + }); } LCRView::LCRView(NavigationView& nav) { - std::string label; - - baseband::run_image(portapack::spi_flash::image_tag_afsk); - - add_children({ - &labels, - &options_ec, - &button_set_rgsb, - &button_modem_setup, - &text_status, - &progress, - &options_scanlist, - &check_scan, - &button_clear, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_lcr", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - options_scanlist.set_selected_index(0); - - const auto button_set_am_fn = [this, &nav](Button& button) { - on_button_set_am(nav, button.id); - }; - - for (size_t n = 0; n < LCR_MAX_AM; n++) { - Button* button = &buttons[n]; - button->on_select = button_set_am_fn; - button->id = n; - button->set_text("AM " + to_string_dec_uint(n + 1)); - button->set_parent_rect({ - static_cast(40), - static_cast(n * 32 + 64), - 48, 24 - }); - add_child(button); - - Checkbox* checkbox = &checkboxes[n]; - checkbox->set_parent_rect({ - static_cast(8), - static_cast(n * 32 + 64), - 48, 24 - }); - checkbox->set_value(false); - add_child(checkbox); - - Rectangle* rectangle = &rectangles[n]; - rectangle->set_parent_rect({ - static_cast(98), - static_cast(n * 32 + 66), - 68, 20 - }); - rectangle->set_color(ui::Color::grey()); - rectangle->set_outline(true); - add_child(rectangle); - - Text* text = &texts[n]; - text->set_parent_rect({ - static_cast(104), - static_cast(n * 32 + 68), - 7 * 8, 16 - }); - add_child(text); - } - - button_set_rgsb.set_text(rgsb); - options_ec.set_selected_index(0); // Auto - checkboxes[0].set_value(true); - - button_set_rgsb.on_select = [this, &nav](Button&) { - text_prompt( - nav, - rgsb, - 4, - [this](std::string& buffer) { - button_set_rgsb.set_text(buffer); - }); - }; - - button_modem_setup.on_select = [this, &nav](Button&) { - if (tx_mode == IDLE) - nav.push(); - }; - - button_clear.on_select = [this, &nav](Button&) { - options_ec.set_selected_index(0); // Auto - for (size_t n = 0; n < LCR_MAX_AM; n++) { - litteral[n] = " "; - checkboxes[n].set_value(true); - } - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(transmitter_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (check_scan.value()) { - if (tx_mode == IDLE) { - start_tx(true); - tx_view.set_transmitting(true); - } else { - // Kill scan process - baseband::kill_afsk(); - tx_view.set_transmitting(false); - transmitter_model.disable(); - text_status.set("Abort @" + rgsb); - progress.set_value(0); - tx_mode = IDLE; - } - } else { - if (tx_mode == IDLE) { - start_tx(false); - tx_view.set_transmitting(true); - } - } - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - transmitter_model.disable(); - tx_mode = IDLE; - }; + std::string label; + + baseband::run_image(portapack::spi_flash::image_tag_afsk); + + add_children({&labels, + &options_ec, + &button_set_rgsb, + &button_modem_setup, + &text_status, + &progress, + &options_scanlist, + &check_scan, + &button_clear, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_lcr", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + options_scanlist.set_selected_index(0); + + const auto button_set_am_fn = [this, &nav](Button& button) { + on_button_set_am(nav, button.id); + }; + + for (size_t n = 0; n < LCR_MAX_AM; n++) { + Button* button = &buttons[n]; + button->on_select = button_set_am_fn; + button->id = n; + button->set_text("AM " + to_string_dec_uint(n + 1)); + button->set_parent_rect({static_cast(40), + static_cast(n * 32 + 64), + 48, 24}); + add_child(button); + + Checkbox* checkbox = &checkboxes[n]; + checkbox->set_parent_rect({static_cast(8), + static_cast(n * 32 + 64), + 48, 24}); + checkbox->set_value(false); + add_child(checkbox); + + Rectangle* rectangle = &rectangles[n]; + rectangle->set_parent_rect({static_cast(98), + static_cast(n * 32 + 66), + 68, 20}); + rectangle->set_color(ui::Color::grey()); + rectangle->set_outline(true); + add_child(rectangle); + + Text* text = &texts[n]; + text->set_parent_rect({static_cast(104), + static_cast(n * 32 + 68), + 7 * 8, 16}); + add_child(text); + } + + button_set_rgsb.set_text(rgsb); + options_ec.set_selected_index(0); // Auto + checkboxes[0].set_value(true); + + button_set_rgsb.on_select = [this, &nav](Button&) { + text_prompt( + nav, + rgsb, + 4, + [this](std::string& buffer) { + button_set_rgsb.set_text(buffer); + }); + }; + + button_modem_setup.on_select = [this, &nav](Button&) { + if (tx_mode == IDLE) + nav.push(); + }; + + button_clear.on_select = [this, &nav](Button&) { + options_ec.set_selected_index(0); // Auto + for (size_t n = 0; n < LCR_MAX_AM; n++) { + litteral[n] = " "; + checkboxes[n].set_value(true); + } + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (check_scan.value()) { + if (tx_mode == IDLE) { + start_tx(true); + tx_view.set_transmitting(true); + } else { + // Kill scan process + baseband::kill_afsk(); + tx_view.set_transmitting(false); + transmitter_model.disable(); + text_status.set("Abort @" + rgsb); + progress.set_value(0); + tx_mode = IDLE; + } + } else { + if (tx_mode == IDLE) { + start_tx(false); + tx_view.set_transmitting(true); + } + } + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + transmitter_model.disable(); + tx_mode = IDLE; + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_lcr.hpp b/firmware/application/apps/ui_lcr.hpp index bd5b4aacc..7ae63c55b 100644 --- a/firmware/application/apps/ui_lcr.hpp +++ b/firmware/application/apps/ui_lcr.hpp @@ -30,144 +30,126 @@ #include "app_settings.hpp" namespace ui { - + #define LCR_MAX_AM 5 class LCRView : public View { -public: - LCRView(NavigationView& nav); - ~LCRView(); - - void focus() override; - - std::string title() const override { return "TEDI/LCR TX"; }; - -private: - struct scan_list_t { - uint8_t count; - const std::string * addresses; - }; - - const scan_list_t scan_list[2] = { - { 36, &RGSB_list_Lille[0] }, - { 20, &RGSB_list_Reims[0] } - }; - - const std::string RGSB_list_Lille[36] = { - "AI10", "AI20", "AI30", "AI40", - "AI50", "AI60", "AI70", "AJ10", - "AJ20", "AJ30", "AJ40", "AJ50", - "AJ60", "AJ70", "AK10", - "EAA0", "EAB0", "EAC0", "EAD0", - "EbA0", "EbB0", "EbC0", "EbD0", - "EbE0", "EbF0", "EbG0", "EbH0", - "EbI0", "EbJ0", "EbK0", "EbL0", - "EbM0", "EbN0", "EbO0", "EbP0", - "EbS0" - }; - - const std::string RGSB_list_Reims[20] = { - "AI10", "AI20", "AI30", "AI40", - "AI50", "AI60", "AI70", - "AJ10", "AJ20", "AJ30", "AJ40", - "AJ50", "AJ60", "AJ70", - "AK10", "AK20", "AK50", "AK60", - "AK70", - "AP10" - }; - - enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - tx_modes tx_mode = IDLE; - uint8_t scan_count { 0 }, scan_index { 0 }; - uint32_t scan_progress { 0 }; - std::array litteral { { " " } }; - std::string rgsb { "AI10" }; - uint16_t lcr_message_data[256]; - uint8_t repeat_index { 0 }; - - void update_progress(); - void start_tx(const bool scan); - void on_tx_progress(const uint32_t progress, const bool done); - void on_button_set_am(NavigationView& nav, int16_t button_id); - - Labels labels { - { { 0, 8 }, "EC: RGSB:", Color::light_grey() }, - { { 17 * 8, 4 * 8 }, "List:", Color::light_grey() } - }; - - std::array buttons { }; - std::array checkboxes { }; - std::array rectangles { }; - std::array texts { }; - - OptionsField options_ec { - { 3 * 8, 8 }, - 4, - { - { "Auto", 0 }, - { "Jour", 1 }, - { "Nuit", 2 }, - { "S ? ", 3 } - } - }; - - Button button_set_rgsb { - { 13 * 8, 4, 8 * 8, 24 }, - "RGSB" - }; - Checkbox check_scan { - { 22 * 8, 4 }, - 4, - "Scan" - }; - - Button button_modem_setup { - { 1 * 8, 4 * 8 + 2, 14 * 8, 24 }, - "Modem setup" - }; - OptionsField options_scanlist { - { 22 * 8, 4 * 8 }, - 6, - { - { "Reims ", 1 } - } - }; - - Button button_clear { - { 22 * 8, 8 * 8, 7 * 8, 19 * 8 }, - "CLEAR" - }; - - Text text_status { - { 2 * 8, 27 * 8 + 4, 26 * 8, 16 }, - "Ready" - }; - ProgressBar progress { - { 2 * 8, 29 * 8 + 4, 26 * 8, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + LCRView(NavigationView& nav); + ~LCRView(); + + void focus() override; + + std::string title() const override { return "TEDI/LCR TX"; }; + + private: + struct scan_list_t { + uint8_t count; + const std::string* addresses; + }; + + const scan_list_t scan_list[2] = { + {36, &RGSB_list_Lille[0]}, + {20, &RGSB_list_Reims[0]}}; + + const std::string RGSB_list_Lille[36] = { + "AI10", "AI20", "AI30", "AI40", + "AI50", "AI60", "AI70", "AJ10", + "AJ20", "AJ30", "AJ40", "AJ50", + "AJ60", "AJ70", "AK10", + "EAA0", "EAB0", "EAC0", "EAD0", + "EbA0", "EbB0", "EbC0", "EbD0", + "EbE0", "EbF0", "EbG0", "EbH0", + "EbI0", "EbJ0", "EbK0", "EbL0", + "EbM0", "EbN0", "EbO0", "EbP0", + "EbS0"}; + + const std::string RGSB_list_Reims[20] = { + "AI10", "AI20", "AI30", "AI40", + "AI50", "AI60", "AI70", + "AJ10", "AJ20", "AJ30", "AJ40", + "AJ50", "AJ60", "AJ70", + "AK10", "AK20", "AK50", "AK60", + "AK70", + "AP10"}; + + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + tx_modes tx_mode = IDLE; + uint8_t scan_count{0}, scan_index{0}; + uint32_t scan_progress{0}; + std::array litteral{{" "}}; + std::string rgsb{"AI10"}; + uint16_t lcr_message_data[256]; + uint8_t repeat_index{0}; + + void update_progress(); + void start_tx(const bool scan); + void on_tx_progress(const uint32_t progress, const bool done); + void on_button_set_am(NavigationView& nav, int16_t button_id); + + Labels labels{ + {{0, 8}, "EC: RGSB:", Color::light_grey()}, + {{17 * 8, 4 * 8}, "List:", Color::light_grey()}}; + + std::array buttons{}; + std::array checkboxes{}; + std::array rectangles{}; + std::array texts{}; + + OptionsField options_ec{ + {3 * 8, 8}, + 4, + {{"Auto", 0}, + {"Jour", 1}, + {"Nuit", 2}, + {"S ? ", 3}}}; + + Button button_set_rgsb{ + {13 * 8, 4, 8 * 8, 24}, + "RGSB"}; + Checkbox check_scan{ + {22 * 8, 4}, + 4, + "Scan"}; + + Button button_modem_setup{ + {1 * 8, 4 * 8 + 2, 14 * 8, 24}, + "Modem setup"}; + OptionsField options_scanlist{ + {22 * 8, 4 * 8}, + 6, + {{"Reims ", 1}}}; + + Button button_clear{ + {22 * 8, 8 * 8, 7 * 8, 19 * 8}, + "CLEAR"}; + + Text text_status{ + {2 * 8, 27 * 8 + 4, 26 * 8, 16}, + "Ready"}; + ProgressBar progress{ + {2 * 8, 29 * 8 + 4, 26 * 8, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_level.cpp b/firmware/application/apps/ui_level.cpp index d69da9d16..2f7da777f 100644 --- a/firmware/application/apps/ui_level.cpp +++ b/firmware/application/apps/ui_level.cpp @@ -29,280 +29,254 @@ using portapack::memory::map::backup_ram; namespace ui { - bool LevelView::check_sd_card() - { - return (sd_card::status() == sd_card::Status::Mounted) ? true : false; +bool LevelView::check_sd_card() { + return (sd_card::status() == sd_card::Status::Mounted) ? true : false; +} + +void LevelView::focus() { + button_frequency.focus(); +} + +LevelView::~LevelView() { + // save app settings + app_settings.lna = field_lna.value(); + app_settings.vga = field_vga.value(); + app_settings.rx_amp = field_rf_amp.value(); + + settings.save("level", &app_settings); + + receiver_model.disable(); + baseband::shutdown(); +} + +LevelView::LevelView(NavigationView& nav) + : nav_{nav} { + add_children({&labels, + &field_lna, + &field_vga, + &field_rf_amp, + &field_volume, + &field_bw, + &field_mode, + &step_mode, + &rssi_resolution, + &button_frequency, + &text_ctcss, + &freq_stats_rssi, + &freq_stats_db, + &audio_mode, + &peak_mode, + &rssi, + &rssi_graph}); + + rssi.set_vertical_rssi(true); + + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + // Level directory + if (check_sd_card()) { // Check to see if SD Card is mounted + make_new_directory(u"/LEVEL"); + sd_card_mounted = true; } - void LevelView::focus() { - button_frequency.focus(); + change_mode(NFM_MODULATION); // Start on AM + field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector + + // HELPER: Pre-setting a manual range, based on stored frequency + freq = persistent_memory::tuned_frequency(); + receiver_model.set_tuning_frequency(freq); + button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>"); + + // load auto common app settings + if (sd_card_mounted) { + auto rc = settings.load("level", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + receiver_model.set_rf_amp(app_settings.rx_amp); + } } - LevelView::~LevelView() { - - // save app settings - app_settings.lna = field_lna.value(); - app_settings.vga = field_vga.value(); - app_settings.rx_amp = field_rf_amp.value(); - - settings.save("level", &app_settings); - - receiver_model.disable(); - baseband::shutdown(); - } + button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) { + auto new_view = nav_.push(freq); + new_view->on_changed = [this, &button](rf::Frequency f) { + freq = f; + receiver_model.set_tuning_frequency(f); // Retune to actual freq + button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>"); + }; + }; - LevelView::LevelView( NavigationView& nav) : nav_ { nav } { - - add_children( { - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &field_volume, - &field_bw, - &field_mode, - &step_mode, - &rssi_resolution, - &button_frequency, - &text_ctcss, - &freq_stats_rssi, - &freq_stats_db, - &audio_mode, - &peak_mode, - &rssi, - &rssi_graph - } ); - - rssi.set_vertical_rssi( true ); - - field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - - // Level directory - if( check_sd_card() ) { // Check to see if SD Card is mounted - make_new_directory( u"/LEVEL" ); - sd_card_mounted = true ; + button_frequency.on_change = [this]() { + int64_t def_step = freqman_entry_get_step_value(step_mode.selected_index()); + freq = freq + (button_frequency.get_encoder_delta() * def_step); + if (freq < 1) { + freq = 1; } - - change_mode(NFM_MODULATION); //Start on AM - field_mode.set_by_value(NFM_MODULATION); //Reflect the mode into the manual selector - - //HELPER: Pre-setting a manual range, based on stored frequency - freq = persistent_memory::tuned_frequency(); - receiver_model.set_tuning_frequency( freq ); - button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" ); - - // load auto common app settings - if( sd_card_mounted ) - { - auto rc = settings.load("level", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - receiver_model.set_rf_amp(app_settings.rx_amp); - } + if (freq > (MAX_UFREQ - def_step)) { + freq = MAX_UFREQ; } - - button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) { - auto new_view = nav_.push(freq); - new_view->on_changed = [this, &button](rf::Frequency f) { - freq = f ; - receiver_model.set_tuning_frequency( f ); // Retune to actual freq - button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" ); - }; - }; - - button_frequency.on_change = [this]() { - int64_t def_step = freqman_entry_get_step_value( step_mode.selected_index() ); - freq = freq + ( button_frequency.get_encoder_delta() * def_step ); - if( freq < 1 ) - { - freq = 1 ; - } - if( freq > ( MAX_UFREQ - def_step ) ) - { - freq = MAX_UFREQ ; - } - button_frequency.set_encoder_delta( 0 ); - - receiver_model.set_tuning_frequency( freq ); // Retune to actual freq - button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" ); - }; - - field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v != -1 ) - { - receiver_model.disable(); - baseband::shutdown(); - change_mode(v); - if( audio_mode.selected_index() != 0 ) - { - audio::output::start(); - } - receiver_model.enable(); - } - }; - - rssi_resolution.on_change = [this](size_t, OptionsField::value_t v) { - if( v != -1 ) - { - rssi_graph.set_nb_columns( v ); - } - }; - - audio_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v == 0 ) - { - audio::output::stop(); - } - else if( v == 1 ) - { + button_frequency.set_encoder_delta(0); + + receiver_model.set_tuning_frequency(freq); // Retune to actual freq + button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>"); + }; + + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (v != -1) { + receiver_model.disable(); + baseband::shutdown(); + change_mode(v); + if (audio_mode.selected_index() != 0) { audio::output::start(); - this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); - } - else - { - } - }; - + receiver_model.enable(); + } + }; - peak_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v == 0 ) - { - rssi.set_peak( false , 0 ); - } - else - { - rssi.set_peak( true , v ); - } - }; + rssi_resolution.on_change = [this](size_t, OptionsField::value_t v) { + if (v != -1) { + rssi_graph.set_nb_columns(v); + } + }; + + audio_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (v == 0) { + audio::output::stop(); + } else if (v == 1) { + audio::output::start(); + this->on_headphone_volume_changed((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + } else { + } + }; - // default peak value - peak_mode.set_selected_index(2); - rssi_resolution.set_selected_index(1); - //FILL STEP OPTIONS - freqman_set_modulation_option( field_mode ); - freqman_set_step_option_short( step_mode ); - freq_stats_rssi.set_style(&style_white); - freq_stats_db.set_style(&style_white); + peak_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (v == 0) { + rssi.set_peak(false, 0); + } else { + rssi.set_peak(true, v); + } + }; + + // default peak value + peak_mode.set_selected_index(2); + rssi_resolution.set_selected_index(1); + // FILL STEP OPTIONS + freqman_set_modulation_option(field_mode); + freqman_set_step_option_short(step_mode); + freq_stats_rssi.set_style(&style_white); + freq_stats_db.set_style(&style_white); +} + +void LevelView::on_headphone_volume_changed(int32_t v) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); +} + +void LevelView::on_statistics_update(const ChannelStatistics& statistics) { + static int last_max_db = -1000; + static int last_min_rssi = -1000; + static int last_avg_rssi = -1000; + static int last_max_rssi = -1000; + + rssi_graph.add_values(rssi.get_min(), rssi.get_avg(), rssi.get_max(), statistics.max_db); + + bool refresh_db = false; + bool refresh_rssi = false; + + if (last_max_db != statistics.max_db) { + refresh_db = true; } - - void LevelView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); + if (last_min_rssi != rssi.get_min() || last_avg_rssi != rssi.get_avg() || last_max_rssi != rssi.get_max()) { + refresh_rssi = true; } + if (refresh_db) { + last_max_db = statistics.max_db; + freq_stats_db.set("Power: " + to_string_dec_int(statistics.max_db) + " db"); + } + if (refresh_rssi) { + last_min_rssi = rssi.get_min(); + last_avg_rssi = rssi.get_avg(); + last_max_rssi = rssi.get_max(); + freq_stats_rssi.set("RSSI: " + to_string_dec_int(rssi.get_min()) + "/" + to_string_dec_int(rssi.get_avg()) + "/" + to_string_dec_int(rssi.get_max()) + ",dt: " + to_string_dec_int(rssi.get_delta())); + } +} /* on_statistic_updates */ + +size_t LevelView::change_mode(freqman_index_t new_mod) { + field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; }; + + switch (new_mod) { + case AM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw DSB (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + case NFM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw 16k (2) default + field_bw.set_selected_index(2); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + break; + case WFM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw 200k (0) only/default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + default: + break; + } + return step_mode.selected_index(); +} +void LevelView::handle_coded_squelch(const uint32_t value) { + static int32_t last_idx = -1; - void LevelView::on_statistics_update(const ChannelStatistics& statistics) { - static int last_max_db = -1000 ; - static int last_min_rssi = -1000 ; - static int last_avg_rssi = -1000 ; - static int last_max_rssi = -1000 ; - - rssi_graph.add_values( rssi.get_min() , rssi.get_avg() , rssi.get_max() , statistics.max_db ); + float diff, min_diff = value; + size_t min_idx{0}; + size_t c; - bool refresh_db = false ; - bool refresh_rssi = false ; + if (field_mode.selected_index() != NFM_MODULATION) { + text_ctcss.set(" "); + return; + } - if( last_max_db != statistics.max_db ) - { - refresh_db = true ; + // Find nearest match + for (c = 0; c < tone_keys.size(); c++) { + diff = abs(((float)value / 100.0) - tone_keys[c].second); + if (diff < min_diff) { + min_idx = c; + min_diff = diff; } - if( last_min_rssi != rssi.get_min() || last_avg_rssi != rssi.get_avg() || last_max_rssi != rssi.get_max() ) - { - refresh_rssi = true ; - } - if( refresh_db ) - { - last_max_db = statistics.max_db ; - freq_stats_db.set( "Power: "+to_string_dec_int( statistics.max_db )+" db" ); - } - if( refresh_rssi ) - { - last_min_rssi = rssi.get_min(); - last_avg_rssi = rssi.get_avg(); - last_max_rssi = rssi.get_max(); - freq_stats_rssi.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+",dt: "+to_string_dec_int( rssi.get_delta() ) ); - } - } /* on_statistic_updates */ - - size_t LevelView::change_mode( freqman_index_t new_mod ) { - - field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; }; - - switch( new_mod ) { - case AM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw DSB (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - receiver_model.set_am_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - case NFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 16k (2) default - field_bw.set_selected_index(2); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - break; - case WFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 200k (0) only/default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - receiver_model.set_wfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - default: - break; - } - return step_mode.selected_index(); } - - void LevelView::handle_coded_squelch(const uint32_t value) { - static int32_t last_idx = -1 ; - - float diff, min_diff = value; - size_t min_idx { 0 }; - size_t c; - - if( field_mode.selected_index() != NFM_MODULATION ) - { + // Arbitrary confidence threshold + if (last_idx < 0 || (unsigned)last_idx != min_idx) { + last_idx = min_idx; + if (min_diff < 40) + text_ctcss.set("T: " + tone_keys[min_idx].first); + else text_ctcss.set(" "); - return ; - } - - // Find nearest match - for (c = 0; c < tone_keys.size(); c++) { - diff = abs(((float)value / 100.0) - tone_keys[c].second); - if (diff < min_diff) { - min_idx = c; - min_diff = diff; - } - } - - // Arbitrary confidence threshold - if( last_idx < 0 || (unsigned)last_idx != min_idx ) - { - last_idx = min_idx ; - if (min_diff < 40) - text_ctcss.set("T: "+tone_keys[min_idx].first); - else - text_ctcss.set(" "); - } } +} } /* namespace ui */ diff --git a/firmware/application/apps/ui_level.hpp b/firmware/application/apps/ui_level.hpp index b289ab9f1..331efccc4 100644 --- a/firmware/application/apps/ui_level.hpp +++ b/firmware/application/apps/ui_level.hpp @@ -38,198 +38,189 @@ #include "file.hpp" #include "app_settings.hpp" - namespace ui { - class LevelView : public View { - public: - LevelView(NavigationView& nav); - ~LevelView(); - - void focus() override; - - const Style style_grey { // level - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - - const Style style_white { // level - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::white(), - }; - - const Style style_yellow { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::yellow(), - }; - - const Style style_green { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - - const Style style_red { //erasing freq - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - const Style style_blue { // quick level, wait == 0 - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::blue(), - }; - - std::string title() const override { return "Level"; }; - - private: - NavigationView& nav_; - - size_t change_mode( freqman_index_t mod_type); - void on_statistics_update(const ChannelStatistics& statistics); - void set_display_freq( int64_t freq ); - bool check_sd_card(); - - int32_t db { 0 }; - long long int MAX_UFREQ = { 7200000000 }; // maximum usable freq - bool sd_card_mounted = false ; - rf::Frequency freq = { 0 } ; - - Labels labels - { - { { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() }, - { { 0 * 8 , 1 * 16 }, "BW: MODE: S: ", Color::light_grey() }, - }; - - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; - - NumberField field_volume { - { 24 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - OptionsField field_bw { - { 3 * 8, 1 * 16 }, - 6, - { } - }; - - OptionsField field_mode { - { 15 * 8, 1 * 16 }, - 3, - { - } - }; - - OptionsField step_mode { - { 16 * 8, 2 * 16 + 4 }, - 12, - { - } - }; - - ButtonWithEncoder button_frequency { - { 0 * 8 , 2 * 16 + 8 , 15 * 8 , 1 * 8 }, - "" - }; - - OptionsField audio_mode { - { 21 * 8, 1 * 16 }, - 9, - { - {"audio off", 0}, - {"audio on",1} - //{"tone on", 2}, - //{"tone off", 2}, - } - }; - - Text text_ctcss { - { 22 * 8, 3 * 16 + 4 , 14 * 8, 1 * 8 }, - "" - }; - - // RSSI: XX/XX/XXX,dt: XX - Text freq_stats_rssi { - { 0 * 8 , 3 * 16 + 4 , 22 * 8, 14 }, - }; - - // Power: -XXX db - Text freq_stats_db { - { 0 * 8 , 4 * 16 + 4 , 14 * 8, 14 }, - }; - - OptionsField peak_mode { - { 40 + 10 * 8, 4 * 16 + 4 }, - 10, - { - {"peak:none", 0}, - {"peak:0.25s",250}, - {"peak:0.5s",500}, - {"peak:1s",1000}, - {"peak:3s",3000}, - {"peak:5s",5000}, - {"peak:10s",10000}, - } - }; - OptionsField rssi_resolution { - { 44 + 20 * 8, 4 * 16 + 4}, - 4, - { - {"16x", 16}, - {"32x", 32}, - {"64x", 64}, - {"128x", 128}, - {"240x", 240}, - } - }; - - RSSIGraph rssi_graph { // 240x320 => - { 0 , 5 * 16 + 4 , 240 - 5 * 8 , 320 - ( 5 * 16 + 4 ) }, - }; - - RSSI rssi { // 240x320 => - { 240 - 5 * 8 , 5 * 16 + 4 , 5 * 8 , 320 - ( 5 * 16 + 4 ) }, - }; - - void handle_coded_squelch(const uint32_t value); - void on_headphone_volume_changed(int32_t v); - - MessageHandlerRegistration message_handler_coded_squelch { - Message::ID::CodedSquelch, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_coded_squelch(message.value); - } - }; - - MessageHandlerRegistration message_handler_stats { - Message::ID::ChannelStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - }; +class LevelView : public View { + public: + LevelView(NavigationView& nav); + ~LevelView(); + + void focus() override; + + const Style style_grey{ + // level + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + const Style style_white{ + // level + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::white(), + }; + + const Style style_yellow{ + // Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::yellow(), + }; + + const Style style_green{ + // Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + const Style style_red{ + // erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + const Style style_blue{ + // quick level, wait == 0 + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::blue(), + }; + + std::string title() const override { return "Level"; }; + + private: + NavigationView& nav_; + + size_t change_mode(freqman_index_t mod_type); + void on_statistics_update(const ChannelStatistics& statistics); + void set_display_freq(int64_t freq); + bool check_sd_card(); + + int32_t db{0}; + long long int MAX_UFREQ = {7200000000}; // maximum usable freq + bool sd_card_mounted = false; + rf::Frequency freq = {0}; + + Labels labels{ + {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()}, + {{0 * 8, 1 * 16}, "BW: MODE: S: ", Color::light_grey()}, + }; + + LNAGainField field_lna{ + {4 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {11 * 8, 0 * 16}}; + + RFAmpField field_rf_amp{ + {18 * 8, 0 * 16}}; + + NumberField field_volume{ + {24 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + OptionsField field_bw{ + {3 * 8, 1 * 16}, + 6, + {}}; + + OptionsField field_mode{ + {15 * 8, 1 * 16}, + 3, + {}}; + + OptionsField step_mode{ + {16 * 8, 2 * 16 + 4}, + 12, + {}}; + + ButtonWithEncoder button_frequency{ + {0 * 8, 2 * 16 + 8, 15 * 8, 1 * 8}, + ""}; + + OptionsField audio_mode{ + {21 * 8, 1 * 16}, + 9, + { + {"audio off", 0}, + {"audio on", 1} + //{"tone on", 2}, + //{"tone off", 2}, + }}; + + Text text_ctcss{ + {22 * 8, 3 * 16 + 4, 14 * 8, 1 * 8}, + ""}; + + // RSSI: XX/XX/XXX,dt: XX + Text freq_stats_rssi{ + {0 * 8, 3 * 16 + 4, 22 * 8, 14}, + }; + + // Power: -XXX db + Text freq_stats_db{ + {0 * 8, 4 * 16 + 4, 14 * 8, 14}, + }; + + OptionsField peak_mode{ + {40 + 10 * 8, 4 * 16 + 4}, + 10, + { + {"peak:none", 0}, + {"peak:0.25s", 250}, + {"peak:0.5s", 500}, + {"peak:1s", 1000}, + {"peak:3s", 3000}, + {"peak:5s", 5000}, + {"peak:10s", 10000}, + }}; + OptionsField rssi_resolution{ + {44 + 20 * 8, 4 * 16 + 4}, + 4, + { + {"16x", 16}, + {"32x", 32}, + {"64x", 64}, + {"128x", 128}, + {"240x", 240}, + }}; + + RSSIGraph rssi_graph{ + // 240x320 => + {0, 5 * 16 + 4, 240 - 5 * 8, 320 - (5 * 16 + 4)}, + }; + + RSSI rssi{ + // 240x320 => + {240 - 5 * 8, 5 * 16 + 4, 5 * 8, 320 - (5 * 16 + 4)}, + }; + + void handle_coded_squelch(const uint32_t value); + void on_headphone_volume_changed(int32_t v); + + MessageHandlerRegistration message_handler_coded_squelch{ + Message::ID::CodedSquelch, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_coded_squelch(message.value); + }}; + + MessageHandlerRegistration message_handler_stats{ + Message::ID::ChannelStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; +}; } /* namespace ui */ diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index 6e03eda2c..d28ec990e 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -24,670 +24,590 @@ using namespace portapack; -namespace ui -{ - void GlassView::focus() - { - button_marker.focus(); - } +namespace ui { +void GlassView::focus() { + button_marker.focus(); +} - GlassView::~GlassView() - { - receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps - receiver_model.disable(); - baseband::shutdown(); - } +GlassView::~GlassView() { + receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps + receiver_model.disable(); + baseband::shutdown(); +} - // Returns the next multiple of num that is a multiple of multiplier - int64_t GlassView::next_mult_of(int64_t num, int64_t multiplier) { - return ((num / multiplier) + 1) * multiplier; - } +// Returns the next multiple of num that is a multiple of multiplier +int64_t GlassView::next_mult_of(int64_t num, int64_t multiplier) { + return ((num / multiplier) + 1) * multiplier; +} - void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) { - int64_t span = *f_max - *f_min; - int64_t num_intervals = span / width; - if( span % width != 0 ) - { - num_intervals++; - } - int64_t new_span = num_intervals * width; - int64_t delta_span = (new_span - span) / 2; - *f_min -= delta_span; - *f_max += delta_span; +void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) { + int64_t span = *f_max - *f_min; + int64_t num_intervals = span / width; + if (span % width != 0) { + num_intervals++; } + int64_t new_span = num_intervals * width; + int64_t delta_span = (new_span - span) / 2; + *f_min -= delta_span; + *f_max += delta_span; +} - void GlassView::on_lna_changed(int32_t v_db) - { - receiver_model.set_lna(v_db); - } +void GlassView::on_lna_changed(int32_t v_db) { + receiver_model.set_lna(v_db); +} - void GlassView::on_vga_changed(int32_t v_db) - { - receiver_model.set_vga(v_db); - } +void GlassView::on_vga_changed(int32_t v_db) { + receiver_model.set_vga(v_db); +} - void GlassView::reset_live_view( bool clear_screen ) - { - max_freq_hold = 0 ; - max_freq_power = -1000 ; - if( clear_screen ) - { - // only clear screen in peak mode - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 + 16 } , { 240 , 320 - (108 + 16) } } , { 0 , 0 , 0 } ); - } +void GlassView::reset_live_view(bool clear_screen) { + max_freq_hold = 0; + max_freq_power = -1000; + if (clear_screen) { + // only clear screen in peak mode + if (live_frequency_view == 2) { + display.fill_rectangle({{0, 108 + 16}, {240, 320 - (108 + 16)}}, {0, 0, 0}); } } +} - void GlassView::add_spectrum_pixel( uint8_t power ) - { - static int64_t last_max_freq = 0 ; +void GlassView::add_spectrum_pixel(uint8_t power) { + static int64_t last_max_freq = 0; - spectrum_row[pixel_index] = spectrum_rgb3_lut[power] ; // row of colors - spectrum_data[pixel_index] = ( live_frequency_integrate * spectrum_data[pixel_index] + power ) / (live_frequency_integrate + 1); // smoothing - pixel_index ++ ; + spectrum_row[pixel_index] = spectrum_rgb3_lut[power]; // row of colors + spectrum_data[pixel_index] = (live_frequency_integrate * spectrum_data[pixel_index] + power) / (live_frequency_integrate + 1); // smoothing + pixel_index++; - if (pixel_index == 240) // got an entire waterfall line - { - if( live_frequency_view > 0 ) - { - constexpr int rssi_sample_range = 256; - constexpr float rssi_voltage_min = 0.4; - constexpr float rssi_voltage_max = 2.2; - constexpr float adc_voltage_max = 3.3; - constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; - constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; - constexpr int raw_delta = raw_max - raw_min; - const range_t y_max_range { 0 , 320 - ( 108 + 16 ) }; - - //drawing and keeping track of max freq - for( uint16_t xpos = 0 ; xpos < 240 ; xpos ++ ) - { - // save max powerwull freq - if( spectrum_data[ xpos ] > max_freq_power ) - { - max_freq_power = spectrum_data[ xpos ]; - max_freq_hold = f_min + ( (f_max - f_min) * xpos) / 240 ; - } - - int16_t point = y_max_range.clip( ( ( spectrum_data[ xpos ] - raw_min ) * ( 320 - ( 108 + 16 ) ) ) / raw_delta ); - uint8_t color_gradient = (point * 255) / 212 ; - // clear if not in peak view - if( live_frequency_view != 2 ) - { - display.fill_rectangle( { { xpos , 108 + 16 } , { 1 , 320 - point } } , { 0 , 0 , 0 } ); - } - display.fill_rectangle( { { xpos , 320 - point } , { 1 , point } } , { color_gradient , 0 , uint8_t( 255 - color_gradient ) } ); - } - if( last_max_freq != max_freq_hold ) - { - last_max_freq = max_freq_hold ; - freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) ); + if (pixel_index == 240) // got an entire waterfall line + { + if (live_frequency_view > 0) { + constexpr int rssi_sample_range = 256; + constexpr float rssi_voltage_min = 0.4; + constexpr float rssi_voltage_max = 2.2; + constexpr float adc_voltage_max = 3.3; + constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; + constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; + constexpr int raw_delta = raw_max - raw_min; + const range_t y_max_range{0, 320 - (108 + 16)}; + + // drawing and keeping track of max freq + for (uint16_t xpos = 0; xpos < 240; xpos++) { + // save max powerwull freq + if (spectrum_data[xpos] > max_freq_power) { + max_freq_power = spectrum_data[xpos]; + max_freq_hold = f_min + ((f_max - f_min) * xpos) / 240; + } + + int16_t point = y_max_range.clip(((spectrum_data[xpos] - raw_min) * (320 - (108 + 16))) / raw_delta); + uint8_t color_gradient = (point * 255) / 212; + // clear if not in peak view + if (live_frequency_view != 2) { + display.fill_rectangle({{xpos, 108 + 16}, {1, 320 - point}}, {0, 0, 0}); } - PlotMarker( marker ); + display.fill_rectangle({{xpos, 320 - point}, {1, point}}, {color_gradient, 0, uint8_t(255 - color_gradient)}); } - else - { - display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier + if (last_max_freq != max_freq_hold) { + last_max_freq = max_freq_hold; + freq_stats.set("MAX HOLD: " + to_string_short_freq(max_freq_hold)); } - pixel_index = 0; // Start New cascade line + PlotMarker(marker); + } else { + display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier } + pixel_index = 0; // Start New cascade line } +} - // Apparently, the spectrum object returns an array of 256 bins - // Each having the radio signal power for it's corresponding frequency slot - void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) - { - // default fast scan offset - uint8_t offset = 2 ; - baseband::spectrum_streaming_stop(); - if( fast_scan || ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) ) - { - // Convert bins of this spectrum slice into a representative max_power and when enough, into pixels - // Spectrum.db has 256 bins. - // All things said and done, we actually need 240 of those bins - for (uint8_t bin = 0; bin < 240; bin++) - { - // if the view is done in one pass, show it like in analog_audio_app - if( ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) ) - { - // Center 16 bins are ignored (DC spike is blanked) - if (bin < 120) - { - if (spectrum.db[256 - 120 + bin] > max_power) // 134 - max_power = spectrum.db[256 - 120 + bin]; - } - else - { - if (spectrum.db[ bin - 120] > max_power) // 118 - max_power = spectrum.db[bin - 120]; - } - } - else // view is made in multiple pass, use original bin picking - { - // Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored - if (bin < 120) - { - if (spectrum.db[134 + bin] > max_power) // 134 - max_power = spectrum.db[134 + bin]; - } - else - { - if (spectrum.db[bin - 118] > max_power) // 118 - max_power = spectrum.db[bin - 118]; - } +// Apparently, the spectrum object returns an array of 256 bins +// Each having the radio signal power for it's corresponding frequency slot +void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) { + // default fast scan offset + uint8_t offset = 2; + baseband::spectrum_streaming_stop(); + if (fast_scan || (LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX)) { + // Convert bins of this spectrum slice into a representative max_power and when enough, into pixels + // Spectrum.db has 256 bins. + // All things said and done, we actually need 240 of those bins + for (uint8_t bin = 0; bin < 240; bin++) { + // if the view is done in one pass, show it like in analog_audio_app + if ((LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX)) { + // Center 16 bins are ignored (DC spike is blanked) + if (bin < 120) { + if (spectrum.db[256 - 120 + bin] > max_power) // 134 + max_power = spectrum.db[256 - 120 + bin]; + } else { + if (spectrum.db[bin - 120] > max_power) // 118 + max_power = spectrum.db[bin - 120]; } - - if( bin == 120 ) - { - bins_Hz_size += 12 * each_bin_size; // add DC bin Hz count into the "pixel fulfilled bag of Hz" + } else // view is made in multiple pass, use original bin picking + { + // Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored + if (bin < 120) { + if (spectrum.db[134 + bin] > max_power) // 134 + max_power = spectrum.db[134 + bin]; + } else { + if (spectrum.db[bin - 118] > max_power) // 118 + max_power = spectrum.db[bin - 118]; } + } + + if (bin == 120) { + bins_Hz_size += 12 * each_bin_size; // add DC bin Hz count into the "pixel fulfilled bag of Hz" + } else { + bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz" + } + + if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled + { + if (min_color_power < max_power) + add_spectrum_pixel(max_power); // Pixel will represent max_power else - { - bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz" - } + add_spectrum_pixel(0); // Filtered out, show black + + max_power = 0; - if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled + if (!pixel_index) // Received indication that a waterfall line has been completed { - if (min_color_power < max_power) - add_spectrum_pixel(max_power); // Pixel will represent max_power - else - add_spectrum_pixel(0); // Filtered out, show black - - max_power = 0; - - if (!pixel_index) // Received indication that a waterfall line has been completed - { - bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin" - f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep - radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving - chThdSleepMilliseconds(10); - baseband::spectrum_streaming_start(); // Do the RX - return; - } - bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel + bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin" + f_center = f_center_ini - offset * each_bin_size; // Start a new sweep + radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving + chThdSleepMilliseconds(10); + baseband::spectrum_streaming_start(); // Do the RX + return; } + bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel } - f_center += ( 256 - ( 2 * offset ) ) * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH - // lost bins are taken in account so next slice first ignored bins overlap previous kept ones } - else //slow scan - { - offset = 32 ; - uint8_t bin_length = 80 ; - for (uint8_t bin = offset ; bin < bin_length + offset ; bin++) + f_center += (256 - (2 * offset)) * each_bin_size; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH + // lost bins are taken in account so next slice first ignored bins overlap previous kept ones + } else // slow scan + { + offset = 32; + uint8_t bin_length = 80; + for (uint8_t bin = offset; bin < bin_length + offset; bin++) { + if (bin < 120) { + if (spectrum.db[134 + bin] > max_power) // 134 + max_power = spectrum.db[134 + bin]; + } else { + if (spectrum.db[bin - 118] > max_power) // 118 + max_power = spectrum.db[bin - 118]; + } + + bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz" + + if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled { - if (bin < 120) - { - if (spectrum.db[134 + bin] > max_power) // 134 - max_power = spectrum.db[134 + bin]; - } + if (min_color_power < max_power) + add_spectrum_pixel(max_power); // Pixel will represent max_power else - { - if (spectrum.db[bin - 118] > max_power) // 118 - max_power = spectrum.db[bin - 118]; - } + add_spectrum_pixel(0); // Filtered out, show black - bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz" + max_power = 0; - if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled + if (!pixel_index) // Received indication that a waterfall line has been completed { - if (min_color_power < max_power) - add_spectrum_pixel(max_power); // Pixel will represent max_power - else - add_spectrum_pixel(0); // Filtered out, show black - - max_power = 0; - - if (!pixel_index) // Received indication that a waterfall line has been completed - { - bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin" - f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep - radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving - chThdSleepMilliseconds(10); - baseband::spectrum_streaming_start(); // Do the RX - return; - } - bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel + bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin" + f_center = f_center_ini - offset * each_bin_size; // Start a new sweep + radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving + chThdSleepMilliseconds(10); + baseband::spectrum_streaming_start(); // Do the RX + return; } + bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel } - f_center += bin_length * each_bin_size ; } - radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving - chThdSleepMilliseconds(5); - baseband::spectrum_streaming_start(); // Do the RX + f_center += bin_length * each_bin_size; } + radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving + chThdSleepMilliseconds(5); + baseband::spectrum_streaming_start(); // Do the RX +} - void GlassView::on_hide() - { - baseband::spectrum_streaming_stop(); - display.scroll_disable(); - } +void GlassView::on_hide() { + baseband::spectrum_streaming_stop(); + display.scroll_disable(); +} - void GlassView::on_show() - { - display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates - baseband::spectrum_streaming_start(); - } +void GlassView::on_show() { + display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates + baseband::spectrum_streaming_start(); +} - void GlassView::on_range_changed() - { - reset_live_view( false ); - f_min = field_frequency_min.value(); - f_max = field_frequency_max.value(); - search_span = f_max - f_min; - - if( locked_range ) - { - button_range.set_text(">"+to_string_dec_uint(search_span)+"<"); - } - else - { - button_range.set_text(" "+to_string_dec_uint(search_span)+" "); - } +void GlassView::on_range_changed() { + reset_live_view(false); + f_min = field_frequency_min.value(); + f_max = field_frequency_max.value(); + search_span = f_max - f_min; - f_min = (f_min)*MHZ_DIV; // Transpose into full frequency realm - f_max = (f_max)*MHZ_DIV; - adjust_range( &f_min , &f_max , 240 ); - - marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz - marker = f_min + (f_max - f_min) / 2 ; - button_marker.set_text( to_string_short_freq( marker ) ); - PlotMarker( marker ); // Refresh marker on screen - - pixel_index = 0; // reset pixel counter - max_power = 0; - bins_Hz_size = 0; // reset amount of Hz filled up by pixels - if( (f_max - f_min) <= LOOKING_GLASS_SLICE_WIDTH_MAX ) - { - LOOKING_GLASS_SLICE_WIDTH = (f_max - f_min) ; - receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); - receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH/2); - } - else if( LOOKING_GLASS_SLICE_WIDTH != LOOKING_GLASS_SLICE_WIDTH_MAX ) - { - LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX ; - receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); - receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); - } - if( next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 ) > LOOKING_GLASS_SLICE_WIDTH_MAX ) - LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX ; - else - LOOKING_GLASS_SLICE_WIDTH = next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 ); - - receiver_model.set_squelch_level(0); - each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 256 ; - f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2) ; // Initial center frequency for sweep - f_center = f_center_ini ; // Reset sweep into first slice - baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); - receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice + if (locked_range) { + button_range.set_text(">" + to_string_dec_uint(search_span) + "<"); + } else { + button_range.set_text(" " + to_string_dec_uint(search_span) + " "); } - void GlassView::PlotMarker(rf::Frequency pos) - { - pos -= f_min; - pos = pos / marker_pixel_step; // Real pixel - - uint8_t shift_y = 0 ; - if( live_frequency_view > 0 ) // plot one line down when in live view - { - shift_y = 16 ; - } - portapack::display.fill_rectangle({0, 100 + shift_y, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw - portapack::display.fill_rectangle({(int)pos - 2, 100 + shift_y, 5, 3}, Color::red()); // Red marker top - portapack::display.fill_rectangle({(int)pos - 1, 103 + shift_y, 3, 3}, Color::red()); // Red marker middle - portapack::display.fill_rectangle({(int)pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom + f_min = (f_min)*MHZ_DIV; // Transpose into full frequency realm + f_max = (f_max)*MHZ_DIV; + adjust_range(&f_min, &f_max, 240); + + marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz + marker = f_min + (f_max - f_min) / 2; + button_marker.set_text(to_string_short_freq(marker)); + PlotMarker(marker); // Refresh marker on screen + + pixel_index = 0; // reset pixel counter + max_power = 0; + bins_Hz_size = 0; // reset amount of Hz filled up by pixels + if ((f_max - f_min) <= LOOKING_GLASS_SLICE_WIDTH_MAX) { + LOOKING_GLASS_SLICE_WIDTH = (f_max - f_min); + receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); + receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH / 2); + } else if (LOOKING_GLASS_SLICE_WIDTH != LOOKING_GLASS_SLICE_WIDTH_MAX) { + LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX; + receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); + receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); } + if (next_mult_of(LOOKING_GLASS_SLICE_WIDTH, 256) > LOOKING_GLASS_SLICE_WIDTH_MAX) + LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX; + else + LOOKING_GLASS_SLICE_WIDTH = next_mult_of(LOOKING_GLASS_SLICE_WIDTH, 256); + + receiver_model.set_squelch_level(0); + each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 256; + f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); // Initial center frequency for sweep + f_center = f_center_ini; // Reset sweep into first slice + baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); + receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice +} + +void GlassView::PlotMarker(rf::Frequency pos) { + pos -= f_min; + pos = pos / marker_pixel_step; // Real pixel - GlassView::GlassView( - NavigationView &nav) : nav_(nav) + uint8_t shift_y = 0; + if (live_frequency_view > 0) // plot one line down when in live view { - baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); - - add_children({&labels, - &field_frequency_min, - &field_frequency_max, - &field_lna, - &field_vga, - &button_range, - &steps_config, - &scan_type, - &view_config, - &level_integration, - &filter_config, - &field_rf_amp, - &range_presets, - &button_marker, - &field_trigger, - &button_jump, - &button_rst, - &freq_stats}); - - load_Presets(); // Load available presets from TXT files (or default) - - field_frequency_min.on_change = [this](int32_t v) - { - reset_live_view( true ); - int32_t min_size = steps ; - if( locked_range ) - min_size = search_span ; - if( min_size < 2 ) - min_size = 2 ; - if( v > 7200 - min_size ) - { - v = 7200 - min_size ; - field_frequency_min.set_value( v ); - } - if (v > (field_frequency_max.value() - min_size ) ) - field_frequency_max.set_value( v + min_size ); - if( locked_range ) - field_frequency_max.set_value( v + min_size ); - this->on_range_changed(); - }; - field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset - field_frequency_min.set_step( steps ); - - field_frequency_min.on_select = [this, &nav](NumberField& field) { - auto new_view = nav_.push(field_frequency_min.value()*1000000); - new_view->on_changed = [this, &field](rf::Frequency f) { - int32_t freq = f / 1000000 ; - int32_t min_size = steps ; - if( locked_range ) - min_size = search_span ; - if( min_size < 2 ) - min_size = 2 ; - if( freq > (7200 - min_size ) ) - freq = 7200 - min_size ; - field_frequency_min.set_value( freq ); - if( field_frequency_max.value() < ( freq + min_size ) ) - field_frequency_max.set_value( freq + min_size ); - this->on_range_changed(); - }; - }; + shift_y = 16; + } + portapack::display.fill_rectangle({0, 100 + shift_y, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw + portapack::display.fill_rectangle({(int)pos - 2, 100 + shift_y, 5, 3}, Color::red()); // Red marker top + portapack::display.fill_rectangle({(int)pos - 1, 103 + shift_y, 3, 3}, Color::red()); // Red marker middle + portapack::display.fill_rectangle({(int)pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom +} - field_frequency_max.on_change = [this](int32_t v) - { - reset_live_view( true ); - int32_t min_size = steps ; - if( locked_range ) - min_size = search_span ; - if( min_size < 2 ) - min_size = 2 ; - if( v < min_size ) - { - v = min_size ; - field_frequency_max.set_value( v ); - } - if (v < (field_frequency_min.value() + min_size) ) - field_frequency_min.set_value(v - min_size); - if( locked_range ) - field_frequency_min.set_value( v - min_size ); +GlassView::GlassView( + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); + + add_children({&labels, + &field_frequency_min, + &field_frequency_max, + &field_lna, + &field_vga, + &button_range, + &steps_config, + &scan_type, + &view_config, + &level_integration, + &filter_config, + &field_rf_amp, + &range_presets, + &button_marker, + &field_trigger, + &button_jump, + &button_rst, + &freq_stats}); + + load_Presets(); // Load available presets from TXT files (or default) + + field_frequency_min.on_change = [this](int32_t v) { + reset_live_view(true); + int32_t min_size = steps; + if (locked_range) + min_size = search_span; + if (min_size < 2) + min_size = 2; + if (v > 7200 - min_size) { + v = 7200 - min_size; + field_frequency_min.set_value(v); + } + if (v > (field_frequency_max.value() - min_size)) + field_frequency_max.set_value(v + min_size); + if (locked_range) + field_frequency_max.set_value(v + min_size); + this->on_range_changed(); + }; + field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset + field_frequency_min.set_step(steps); + + field_frequency_min.on_select = [this, &nav](NumberField& field) { + auto new_view = nav_.push(field_frequency_min.value() * 1000000); + new_view->on_changed = [this, &field](rf::Frequency f) { + int32_t freq = f / 1000000; + int32_t min_size = steps; + if (locked_range) + min_size = search_span; + if (min_size < 2) + min_size = 2; + if (freq > (7200 - min_size)) + freq = 7200 - min_size; + field_frequency_min.set_value(freq); + if (field_frequency_max.value() < (freq + min_size)) + field_frequency_max.set_value(freq + min_size); this->on_range_changed(); }; - field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset - field_frequency_max.set_step( steps ); - - field_frequency_max.on_select = [this, &nav](NumberField& field) { - auto new_view = nav_.push(field_frequency_max.value()*1000000); - new_view->on_changed = [this, &field](rf::Frequency f) { - int32_t min_size = steps ; - if( locked_range ) - min_size = search_span ; - if( min_size < 2 ) - min_size = 2 ; - int32_t freq = f / 1000000 ; - if( freq < min_size ) - freq = min_size ; - field_frequency_max.set_value( freq ); - if( field_frequency_min.value() > ( freq - min_size) ) - field_frequency_min.set_value( freq - min_size ); - this->on_range_changed(); - }; - }; - - field_lna.on_change = [this](int32_t v) - { - reset_live_view( true ); - this->on_lna_changed(v); - }; - field_lna.set_value(receiver_model.lna()); - - field_vga.on_change = [this](int32_t v_db) - { - reset_live_view( true ); - this->on_vga_changed(v_db); - }; - field_vga.set_value(receiver_model.vga()); - - steps_config.on_change = [this](size_t n, OptionsField::value_t v) - { - (void)n; - field_frequency_min.set_step( v ); - field_frequency_max.set_step( v ); - steps = v ; - }; - steps_config.set_selected_index(0); //default of 1 Mhz steps - - scan_type.on_change = [this](size_t n, OptionsField::value_t v) - { - (void)n; - fast_scan = v ; - }; - scan_type.set_selected_index(0); // default legacy fast scan - - view_config.on_change = [this](size_t n, OptionsField::value_t v) - { - (void)n; - // clear between changes - reset_live_view( true ); - if( v == 0 ) - { - live_frequency_view = 0 ; - level_integration.hidden( true ); - freq_stats.hidden( true ); - button_jump.hidden( true ); - button_rst.hidden( true ); - display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates - } - else if( v == 1 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } ); - live_frequency_view = 1 ; - display.scroll_disable(); - level_integration.hidden( false ); - freq_stats.hidden( false ); - button_jump.hidden( false ); - button_rst.hidden( false ); - } - else if( v == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } ); - live_frequency_view = 2 ; - display.scroll_disable(); - level_integration.hidden( false ); - freq_stats.hidden( false ); - button_jump.hidden( false ); - button_rst.hidden( false ); - } - set_dirty(); - }; - view_config.set_selected_index(0); //default spectrum - - level_integration.on_change = [this](size_t n, OptionsField::value_t v) - { - (void)n; - reset_live_view( true ); - live_frequency_integrate = v ; - }; - level_integration.set_selected_index(3); //default integration of ( 3 * old value + new_value ) / 4 - - filter_config.on_change = [this](size_t n, OptionsField::value_t v) { - (void)n; - reset_live_view( true ); - min_color_power = v; - }; - filter_config.set_selected_index(0); - - range_presets.on_change = [this](size_t n, OptionsField::value_t v) - { - (void)n; - field_frequency_min.set_value(presets_db[v].min, false); - field_frequency_max.set_value(presets_db[v].max, false); + }; + + field_frequency_max.on_change = [this](int32_t v) { + reset_live_view(true); + int32_t min_size = steps; + if (locked_range) + min_size = search_span; + if (min_size < 2) + min_size = 2; + if (v < min_size) { + v = min_size; + field_frequency_max.set_value(v); + } + if (v < (field_frequency_min.value() + min_size)) + field_frequency_min.set_value(v - min_size); + if (locked_range) + field_frequency_min.set_value(v - min_size); + this->on_range_changed(); + }; + field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset + field_frequency_max.set_step(steps); + + field_frequency_max.on_select = [this, &nav](NumberField& field) { + auto new_view = nav_.push(field_frequency_max.value() * 1000000); + new_view->on_changed = [this, &field](rf::Frequency f) { + int32_t min_size = steps; + if (locked_range) + min_size = search_span; + if (min_size < 2) + min_size = 2; + int32_t freq = f / 1000000; + if (freq < min_size) + freq = min_size; + field_frequency_max.set_value(freq); + if (field_frequency_min.value() > (freq - min_size)) + field_frequency_min.set_value(freq - min_size); this->on_range_changed(); }; - - button_marker.on_change = [this]() - { - marker = marker + button_marker.get_encoder_delta() * marker_pixel_step ; - if( marker < f_min ) - marker = f_min ; - if( marker > f_max ) - marker = f_max ; - button_marker.set_text( to_string_short_freq( marker ) ); - button_marker.set_encoder_delta( 0 ); - PlotMarker( marker ); // Refresh marker on screen - }; - - button_marker.on_select = [this](ButtonWithEncoder &) - { - receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq. - receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO - nav_.pop(); - nav_.push(); // Jump into audio view - }; - - field_trigger.on_change = [this](int32_t v) - { - baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v); - }; - field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution - - button_range.on_select = [this](Button&) { - if( locked_range ) - { - locked_range = false ; - button_range.set_style(&style_white); - button_range.set_text(" "+to_string_dec_uint(search_span)+" "); - } - else - { - locked_range = true ; - button_range.set_style(&style_red); - button_range.set_text(">"+to_string_dec_uint(search_span)+"<"); - } - }; - - button_jump.on_select = [this](Button&) { - receiver_model.set_tuning_frequency(max_freq_hold); // Center tune rx in marker freq. - receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO - nav_.pop(); - nav_.push(); // Jump into audio view - }; - - button_rst.on_select = [this](Button&) { - reset_live_view( true ); - }; - - display.scroll_set_area(109, 319); - baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger: - // Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute , - // it keeps adding the output of the fft to the buffer until "trigger" number of calls are made, - // at which time it pushes the buffer up with channel_spectrum.feed - - on_range_changed(); - - receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); - receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); // 20mhz - receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz - receiver_model.set_squelch_level(0); - receiver_model.enable(); - } - - void GlassView::load_Presets() - { - File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD - auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT"); - presets_db.clear(); // Start with fresh db - if (result.is_valid()) - { - presets_Default(); // There is no txt, store a default range + }; + + field_lna.on_change = [this](int32_t v) { + reset_live_view(true); + this->on_lna_changed(v); + }; + field_lna.set_value(receiver_model.lna()); + + field_vga.on_change = [this](int32_t v_db) { + reset_live_view(true); + this->on_vga_changed(v_db); + }; + field_vga.set_value(receiver_model.vga()); + + steps_config.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + field_frequency_min.set_step(v); + field_frequency_max.set_step(v); + steps = v; + }; + steps_config.set_selected_index(0); // default of 1 Mhz steps + + scan_type.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + fast_scan = v; + }; + scan_type.set_selected_index(0); // default legacy fast scan + + view_config.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + // clear between changes + reset_live_view(true); + if (v == 0) { + live_frequency_view = 0; + level_integration.hidden(true); + freq_stats.hidden(true); + button_jump.hidden(true); + button_rst.hidden(true); + display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates + } else if (v == 1) { + display.fill_rectangle({{0, 108}, {240, 24}}, {0, 0, 0}); + live_frequency_view = 1; + display.scroll_disable(); + level_integration.hidden(false); + freq_stats.hidden(false); + button_jump.hidden(false); + button_rst.hidden(false); + } else if (v == 2) { + display.fill_rectangle({{0, 108}, {240, 24}}, {0, 0, 0}); + live_frequency_view = 2; + display.scroll_disable(); + level_integration.hidden(false); + freq_stats.hidden(false); + button_jump.hidden(false); + button_rst.hidden(false); } - else - { - std::string line; // There is a txt file - char one_char[1]; // Read it char by char - for (size_t pointer = 0; pointer < presets_file.size(); pointer++) - { - presets_file.seek(pointer); - presets_file.read(one_char, 1); - if ((int)one_char[0] > 31) - { // ascii space upwards - line += one_char[0]; // Add it to the textline - } - else if (one_char[0] == '\n') - { // New Line - txtline_process(line); // make sense of this textline - line.clear(); // Ready for next textline - } + set_dirty(); + }; + view_config.set_selected_index(0); // default spectrum + + level_integration.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + reset_live_view(true); + live_frequency_integrate = v; + }; + level_integration.set_selected_index(3); // default integration of ( 3 * old value + new_value ) / 4 + + filter_config.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + reset_live_view(true); + min_color_power = v; + }; + filter_config.set_selected_index(0); + + range_presets.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + field_frequency_min.set_value(presets_db[v].min, false); + field_frequency_max.set_value(presets_db[v].max, false); + this->on_range_changed(); + }; + + button_marker.on_change = [this]() { + marker = marker + button_marker.get_encoder_delta() * marker_pixel_step; + if (marker < f_min) + marker = f_min; + if (marker > f_max) + marker = f_max; + button_marker.set_text(to_string_short_freq(marker)); + button_marker.set_encoder_delta(0); + PlotMarker(marker); // Refresh marker on screen + }; + + button_marker.on_select = [this](ButtonWithEncoder&) { + receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq. + receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO + nav_.pop(); + nav_.push(); // Jump into audio view + }; + + field_trigger.on_change = [this](int32_t v) { + baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v); + }; + field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution + + button_range.on_select = [this](Button&) { + if (locked_range) { + locked_range = false; + button_range.set_style(&style_white); + button_range.set_text(" " + to_string_dec_uint(search_span) + " "); + } else { + locked_range = true; + button_range.set_style(&style_red); + button_range.set_text(">" + to_string_dec_uint(search_span) + "<"); + } + }; + + button_jump.on_select = [this](Button&) { + receiver_model.set_tuning_frequency(max_freq_hold); // Center tune rx in marker freq. + receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO + nav_.pop(); + nav_.push(); // Jump into audio view + }; + + button_rst.on_select = [this](Button&) { + reset_live_view(true); + }; + + display.scroll_set_area(109, 319); + baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger: + // Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute , + // it keeps adding the output of the fft to the buffer until "trigger" number of calls are made, + // at which time it pushes the buffer up with channel_spectrum.feed + + on_range_changed(); + + receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); + receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); // 20mhz + receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz + receiver_model.set_squelch_level(0); + receiver_model.enable(); +} + +void GlassView::load_Presets() { + File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD + auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT"); + presets_db.clear(); // Start with fresh db + if (result.is_valid()) { + presets_Default(); // There is no txt, store a default range + } else { + std::string line; // There is a txt file + char one_char[1]; // Read it char by char + for (size_t pointer = 0; pointer < presets_file.size(); pointer++) { + presets_file.seek(pointer); + presets_file.read(one_char, 1); + if ((int)one_char[0] > 31) { // ascii space upwards + line += one_char[0]; // Add it to the textline + } else if (one_char[0] == '\n') { // New Line + txtline_process(line); // make sense of this textline + line.clear(); // Ready for next textline } - if (line.length() > 0) - txtline_process(line); // Last line had no newline at end ? - if (!presets_db.size()) - presets_Default(); // no antenna on txt, use default } - populate_Presets(); + if (line.length() > 0) + txtline_process(line); // Last line had no newline at end ? + if (!presets_db.size()) + presets_Default(); // no antenna on txt, use default } + populate_Presets(); +} - void GlassView::txtline_process(std::string &line) - { - if (line.find("#") != std::string::npos) - return; // Line is just a comment +void GlassView::txtline_process(std::string& line) { + if (line.find("#") != std::string::npos) + return; // Line is just a comment - size_t comma = line.find(","); // Get first comma position - if (comma == std::string::npos) - return; // No comma at all + size_t comma = line.find(","); // Get first comma position + if (comma == std::string::npos) + return; // No comma at all - size_t previous = 0; - preset_entry new_preset; + size_t previous = 0; + preset_entry new_preset; - new_preset.min = std::stoi(line.substr(0, comma)); - if (!new_preset.min) - return; // No frequency! + new_preset.min = std::stoi(line.substr(0, comma)); + if (!new_preset.min) + return; // No frequency! - previous = comma + 1; - comma = line.find(",", previous); // Search for next delimiter - if (comma == std::string::npos) - return; // No comma at all + previous = comma + 1; + comma = line.find(",", previous); // Search for next delimiter + if (comma == std::string::npos) + return; // No comma at all - new_preset.max = std::stoi(line.substr(previous, comma - previous)); - if (!new_preset.max) - return; // No frequency! + new_preset.max = std::stoi(line.substr(previous, comma - previous)); + if (!new_preset.max) + return; // No frequency! - new_preset.label = line.substr(comma + 1); - if (new_preset.label.size() == 0) - return; // No label ? + new_preset.label = line.substr(comma + 1); + if (new_preset.label.size() == 0) + return; // No label ? - presets_db.push_back(new_preset); // Add this preset. - } - - void GlassView::populate_Presets() - { - using option_t = std::pair; - using options_t = std::vector; - options_t entries; + presets_db.push_back(new_preset); // Add this preset. +} - for (preset_entry preset : presets_db) - { // go thru all available presets - entries.emplace_back(preset.label, entries.size()); - } - range_presets.set_options(entries); - } +void GlassView::populate_Presets() { + using option_t = std::pair; + using options_t = std::vector; + options_t entries; - void GlassView::presets_Default() - { - presets_db.clear(); - presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"}); + for (preset_entry preset : presets_db) { // go thru all available presets + entries.emplace_back(preset.label, entries.size()); } + range_presets.set_options(entries); +} +void GlassView::presets_Default() { + presets_db.clear(); + presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"}); } + +} // namespace ui diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index 3b05edb76..74c3fed4b 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -35,244 +35,228 @@ #include "analog_audio_app.hpp" #include "spectrum_color_lut.hpp" -namespace ui -{ +namespace ui { #define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000 -#define MHZ_DIV 1000000 -#define X2_MHZ_DIV 2000000 - - class GlassView : public View - { - public: - - GlassView(NavigationView &nav); - - GlassView( const GlassView &); - GlassView& operator=(const GlassView &nav); - - ~GlassView(); - std::string title() const override { return "LookingGlass"; }; - - void on_show() override; - void on_hide() override; - void focus() override; - - private: - NavigationView& nav_; - - struct preset_entry - { - rf::Frequency min{}; - rf::Frequency max{}; - std::string label{}; - }; - - const Style style_white { // free range - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::white(), - }; - - const Style style_red { // locked range - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - std::vector presets_db{}; - - // Each slice bandwidth 20 MHz and a multiple of 256 - // since we are using LOOKING_GLASS_SLICE_WIDTH/256 as the each_bin_size - // it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq - int64_t LOOKING_GLASS_SLICE_WIDTH = 20000000; - - // frequency rounding helpers - int64_t next_mult_of(int64_t num, int64_t multiplier); - void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width); - - void on_channel_spectrum(const ChannelSpectrum& spectrum); - void do_timers(); - void on_range_changed(); - void on_lna_changed(int32_t v_db); - void on_vga_changed(int32_t v_db); - void reset_live_view( bool clear_screen ); - void add_spectrum_pixel(uint8_t power); - void PlotMarker(rf::Frequency pos); - void load_Presets(); - void txtline_process(std::string& line); - void populate_Presets(); - void presets_Default(); - - rf::Frequency f_min { 0 }, f_max { 0 }; - rf::Frequency search_span { 0 }; - rf::Frequency f_center { 0 }; - rf::Frequency f_center_ini { 0 }; - rf::Frequency marker { 0 }; - rf::Frequency marker_pixel_step { 0 }; - rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 256 }; - rf::Frequency bins_Hz_size { 0 }; - uint8_t min_color_power { 0 }; - uint32_t pixel_index { 0 }; - std::array spectrum_row = { 0 }; - std::array spectrum_data = { 0 }; - ChannelSpectrumFIFO* fifo { nullptr }; - uint8_t max_power = 0; - int32_t steps = 0 ; - uint8_t live_frequency_view = 0 ; - int16_t live_frequency_integrate = 3 ; - int64_t max_freq_hold = 0 ; - int16_t max_freq_power = -1000 ; - bool fast_scan = true ; // default to legacy fast scan - bool locked_range = false ; - - Labels labels{ - {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, - {{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()}, - {{0, 2 * 16}, "PRESET:", Color::light_grey()}, - {{0, 3 * 16}, "MARKER: MHz", Color::light_grey()}, - {{0, 4 * 16}, "RES: STEP:", Color::light_grey()} - }; - - NumberField field_frequency_min { - { 4 * 8, 0 * 16 }, - 4, - { 0, 7199 }, - 1, // number of steps by encoder delta - ' ' - }; - - NumberField field_frequency_max { - { 13 * 8, 0 * 16 }, - 4, - { 1, 7200 }, - 1, // number of steps by encoder delta - ' ' - }; - - LNAGainField field_lna { - { 21 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 27 * 8, 0 * 16 } - }; - - Button button_range{ - {7 * 8, 1 * 16, 4 * 8, 16}, - ""}; - - OptionsField filter_config{ - {20 * 8, 1 * 16}, - 4, - { - {"OFF ", 0}, - {"MID ", 118}, //85 +25 (110) + a bit more to kill all blue - {"HIGH", 202}, //168 + 25 (193) - }}; - - RFAmpField field_rf_amp{ - {29 * 8, 1 * 16}}; - - OptionsField range_presets{ - {7 * 8, 2 * 16}, - 20, - { - {" NONE (WIFI 2.4GHz)", 0}, - }}; - - ButtonWithEncoder button_marker{ - {7 * 8, 3 * 16 , 10 * 8 , 16}, - " " - }; - - NumberField field_trigger{ - {4 * 8, 4 * 16}, - 3, - {2, 128}, - 2, - ' '}; - - OptionsField steps_config{ - { 13 * 8, 4 * 16}, - 3, - { - {"1", 1}, - {"25", 25}, - {"50", 50}, - {"100", 100}, - {"250", 250}, - {"500", 500}, - } - }; - - OptionsField scan_type{ - { 17 * 8, 4 * 16}, - 2, - { - {"F-", true }, - {"S-", false }, - } - }; - - OptionsField view_config{ - { 19 * 8, 4 * 16}, - 7, - { - {"SPCTR-V", 0 }, - {"LEVEL-V", 1 }, - {"PEAK-V" , 2 }, - } - }; - - OptionsField level_integration{ - { 27 * 8, 4 * 16}, - 2, - { - {"x0", 0 }, - {"x1", 1 }, - {"x2", 2 }, - {"x3", 3 }, - {"x4", 4 }, - {"x5", 5 }, - {"x6", 6 }, - {"x7", 7 }, - {"x8", 8 }, - {"x9", 9 }, - }}; - - Button button_jump { - { 240 - 4 * 8 , 5 * 16 , 4 * 8, 16 }, - "JMP" - }; - - Button button_rst { - { 240 - 9 * 8 , 5 * 16 , 4 * 8, 16 }, - "RST" - }; - - Text freq_stats{ - {0 * 8, 5 * 16 , 240 - 10 * 8 , 8 }, - "" - }; - - MessageHandlerRegistration message_handler_spectrum_config { - Message::ID::ChannelSpectrumConfig, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->fifo = message.fifo; - } - }; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->fifo ) { - ChannelSpectrum channel_spectrum; - while( fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - } - }; +#define MHZ_DIV 1000000 +#define X2_MHZ_DIV 2000000 + +class GlassView : public View { + public: + GlassView(NavigationView& nav); + + GlassView(const GlassView&); + GlassView& operator=(const GlassView& nav); + + ~GlassView(); + std::string title() const override { return "LookingGlass"; }; + + void on_show() override; + void on_hide() override; + void focus() override; + + private: + NavigationView& nav_; + + struct preset_entry { + rf::Frequency min{}; + rf::Frequency max{}; + std::string label{}; + }; + + const Style style_white{ + // free range + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::white(), }; -} + + const Style style_red{ + // locked range + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + std::vector presets_db{}; + + // Each slice bandwidth 20 MHz and a multiple of 256 + // since we are using LOOKING_GLASS_SLICE_WIDTH/256 as the each_bin_size + // it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq + int64_t LOOKING_GLASS_SLICE_WIDTH = 20000000; + + // frequency rounding helpers + int64_t next_mult_of(int64_t num, int64_t multiplier); + void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width); + + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void do_timers(); + void on_range_changed(); + void on_lna_changed(int32_t v_db); + void on_vga_changed(int32_t v_db); + void reset_live_view(bool clear_screen); + void add_spectrum_pixel(uint8_t power); + void PlotMarker(rf::Frequency pos); + void load_Presets(); + void txtline_process(std::string& line); + void populate_Presets(); + void presets_Default(); + + rf::Frequency f_min{0}, f_max{0}; + rf::Frequency search_span{0}; + rf::Frequency f_center{0}; + rf::Frequency f_center_ini{0}; + rf::Frequency marker{0}; + rf::Frequency marker_pixel_step{0}; + rf::Frequency each_bin_size{LOOKING_GLASS_SLICE_WIDTH / 256}; + rf::Frequency bins_Hz_size{0}; + uint8_t min_color_power{0}; + uint32_t pixel_index{0}; + std::array spectrum_row = {0}; + std::array spectrum_data = {0}; + ChannelSpectrumFIFO* fifo{nullptr}; + uint8_t max_power = 0; + int32_t steps = 0; + uint8_t live_frequency_view = 0; + int16_t live_frequency_integrate = 3; + int64_t max_freq_hold = 0; + int16_t max_freq_power = -1000; + bool fast_scan = true; // default to legacy fast scan + bool locked_range = false; + + Labels labels{ + {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, + {{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()}, + {{0, 2 * 16}, "PRESET:", Color::light_grey()}, + {{0, 3 * 16}, "MARKER: MHz", Color::light_grey()}, + {{0, 4 * 16}, "RES: STEP:", Color::light_grey()}}; + + NumberField field_frequency_min{ + {4 * 8, 0 * 16}, + 4, + {0, 7199}, + 1, // number of steps by encoder delta + ' '}; + + NumberField field_frequency_max{ + {13 * 8, 0 * 16}, + 4, + {1, 7200}, + 1, // number of steps by encoder delta + ' '}; + + LNAGainField field_lna{ + {21 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {27 * 8, 0 * 16}}; + + Button button_range{ + {7 * 8, 1 * 16, 4 * 8, 16}, + ""}; + + OptionsField filter_config{ + {20 * 8, 1 * 16}, + 4, + { + {"OFF ", 0}, + {"MID ", 118}, // 85 +25 (110) + a bit more to kill all blue + {"HIGH", 202}, // 168 + 25 (193) + }}; + + RFAmpField field_rf_amp{ + {29 * 8, 1 * 16}}; + + OptionsField range_presets{ + {7 * 8, 2 * 16}, + 20, + { + {" NONE (WIFI 2.4GHz)", 0}, + }}; + + ButtonWithEncoder button_marker{ + {7 * 8, 3 * 16, 10 * 8, 16}, + " "}; + + NumberField field_trigger{ + {4 * 8, 4 * 16}, + 3, + {2, 128}, + 2, + ' '}; + + OptionsField steps_config{ + {13 * 8, 4 * 16}, + 3, + { + {"1", 1}, + {"25", 25}, + {"50", 50}, + {"100", 100}, + {"250", 250}, + {"500", 500}, + }}; + + OptionsField scan_type{ + {17 * 8, 4 * 16}, + 2, + { + {"F-", true}, + {"S-", false}, + }}; + + OptionsField view_config{ + {19 * 8, 4 * 16}, + 7, + { + {"SPCTR-V", 0}, + {"LEVEL-V", 1}, + {"PEAK-V", 2}, + }}; + + OptionsField level_integration{ + {27 * 8, 4 * 16}, + 2, + { + {"x0", 0}, + {"x1", 1}, + {"x2", 2}, + {"x3", 3}, + {"x4", 4}, + {"x5", 5}, + {"x6", 6}, + {"x7", 7}, + {"x8", 8}, + {"x9", 9}, + }}; + + Button button_jump{ + {240 - 4 * 8, 5 * 16, 4 * 8, 16}, + "JMP"}; + + Button button_rst{ + {240 - 9 * 8, 5 * 16, 4 * 8, 16}, + "RST"}; + + Text freq_stats{ + {0 * 8, 5 * 16, 240 - 10 * 8, 8}, + ""}; + + MessageHandlerRegistration message_handler_spectrum_config{ + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->fifo = message.fifo; + }}; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if (this->fifo) { + ChannelSpectrum channel_spectrum; + while (fifo->out(channel_spectrum)) { + this->on_channel_spectrum(channel_spectrum); + } + } + }}; +}; +} // namespace ui #endif diff --git a/firmware/application/apps/ui_mictx.cpp b/firmware/application/apps/ui_mictx.cpp index 30505e2c2..972ddb134 100644 --- a/firmware/application/apps/ui_mictx.cpp +++ b/firmware/application/apps/ui_mictx.cpp @@ -39,617 +39,600 @@ using wolfson::wm8731::WM8731; using namespace tonekey; using namespace portapack; - -WM8731 audio_codec_wm8731 { i2c0, 0x1a }; - +WM8731 audio_codec_wm8731{i2c0, 0x1a}; namespace ui { void MicTXView::focus() { - switch(focused_ui) { - case 0: - field_frequency.focus(); - break; - case 1: - field_rxfrequency.focus(); - break; - default: - field_va.focus(); - break; - } + switch (focused_ui) { + case 0: + field_frequency.focus(); + break; + case 1: + field_rxfrequency.focus(); + break; + default: + field_va.focus(); + break; + } } void MicTXView::update_vumeter() { - vumeter.set_value(audio_level); + vumeter.set_value(audio_level); } void MicTXView::on_tx_progress(const bool done) { - // Roger beep played, stop transmitting - if (done) - set_tx(false); + // Roger beep played, stop transmitting + if (done) + set_tx(false); } void MicTXView::configure_baseband() { - baseband::set_audiotx_config( - sampling_rate / 20, // Update vu-meter at 20Hz - transmitting ? transmitter_model.channel_bandwidth() : 0, - mic_gain, - shift_bits_s16, // to be used in dsp_modulate - TONES_F2D(tone_key_frequency(tone_key_index), sampling_rate), - enable_am, - enable_dsb, - enable_usb, - enable_lsb - ); - + baseband::set_audiotx_config( + sampling_rate / 20, // Update vu-meter at 20Hz + transmitting ? transmitter_model.channel_bandwidth() : 0, + mic_gain, + shift_bits_s16, // to be used in dsp_modulate + TONES_F2D(tone_key_frequency(tone_key_index), sampling_rate), + enable_am, + enable_dsb, + enable_usb, + enable_lsb); } void MicTXView::set_tx(bool enable) { - if (enable) { - if (rx_enabled) //If audio RX is enabled - rxaudio(false); //Then turn off audio RX - transmitting = true; - configure_baseband(); - transmitter_model.set_tuning_frequency(tx_frequency); // Now,no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp); - transmitter_model.enable(); - portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming - } else { - if (transmitting && rogerbeep_enabled) { - baseband::request_beep(); //Transmit the roger beep - transmitting = false; //And flag the end of the transmission so ... - } else { // (if roger beep was enabled, this will be executed after the beep ends transmitting. - transmitting = false; - configure_baseband(); - transmitter_model.disable(); - if (rx_enabled) //If audio RX is enabled and we've been transmitting - rxaudio(true); //Turn back on audio RX - } - } + if (enable) { + if (rx_enabled) // If audio RX is enabled + rxaudio(false); // Then turn off audio RX + transmitting = true; + configure_baseband(); + transmitter_model.set_tuning_frequency(tx_frequency); // Now,no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp); + transmitter_model.enable(); + portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming + } else { + if (transmitting && rogerbeep_enabled) { + baseband::request_beep(); // Transmit the roger beep + transmitting = false; // And flag the end of the transmission so ... + } else { // (if roger beep was enabled, this will be executed after the beep ends transmitting. + transmitting = false; + configure_baseband(); + transmitter_model.disable(); + if (rx_enabled) // If audio RX is enabled and we've been transmitting + rxaudio(true); // Turn back on audio RX + } + } } void MicTXView::do_timing() { - if (va_enabled) { - if (!transmitting) { - // Attack - if (audio_level >= va_level) { - if ((attack_timer >> 8) >= attack_ms) { - decay_timer = 0; - attack_timer = 0; - set_tx(true); - } else { - attack_timer += lcd_frame_duration; - } - } else { - attack_timer = 0; - } - } else { - // Decay - if (audio_level < va_level) { - if ((decay_timer >> 8) >= decay_ms) { - decay_timer = 0; - attack_timer = 0; - set_tx(false); - } else { - decay_timer += lcd_frame_duration; - } - } else { - decay_timer = 0; - } - } - } else { - // Check for PTT release - const auto switches_state = get_switches_state(); - if (!switches_state[4] && transmitting && !button_touch) // Select button - set_tx(false); - } + if (va_enabled) { + if (!transmitting) { + // Attack + if (audio_level >= va_level) { + if ((attack_timer >> 8) >= attack_ms) { + decay_timer = 0; + attack_timer = 0; + set_tx(true); + } else { + attack_timer += lcd_frame_duration; + } + } else { + attack_timer = 0; + } + } else { + // Decay + if (audio_level < va_level) { + if ((decay_timer >> 8) >= decay_ms) { + decay_timer = 0; + attack_timer = 0; + set_tx(false); + } else { + decay_timer += lcd_frame_duration; + } + } else { + decay_timer = 0; + } + } + } else { + // Check for PTT release + const auto switches_state = get_switches_state(); + if (!switches_state[4] && transmitting && !button_touch) // Select button + set_tx(false); + } } /* Hmmmm. Maybe useless now. void MicTXView::on_tuning_frequency_changed(rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - //if ( rx_enabled ) - receiver_model.set_tuning_frequency(f); //Update freq also for RX + transmitter_model.set_tuning_frequency(f); + //if ( rx_enabled ) + receiver_model.set_tuning_frequency(f); //Update freq also for RX } */ void MicTXView::rxaudio(bool is_on) { - if (is_on) { - audio::input::stop(); - baseband::shutdown(); - - if (enable_am || enable_usb || enable_lsb || enable_dsb) { // "NFM/FM",0 ," WFM ",1 , " AM ",2, " USB ", 3, " LSB ",4, " DSB-SC", 5 - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); // that AM demodulation engine is common to all Amplitude mod : AM/USB/LSB/DSB (2,3,4,5) - if (options_mode.selected_index() < 5) // We will be called here with 2,3,4,5 . We treat here demod. filter 2,3,4; (excluding DSB-C case (5) it is treated more down). - receiver_model.set_am_configuration(options_mode.selected_index() - 1); // selecting proper filter(2,3,4). 2-1=1=>6k-AM(1) , 3-1=2=>+3k-USB(2), 4-1=3=>-3K-LSB(3), - } - else { // We are in NFM/FM or WFM (NFM BW:8k5 or 11k / FM BW 16k / WFM BW:200k) - - if (enable_wfm) { // WFM , BW 200Khz aprox , or the two new addional BW filters (180k, 40k) - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - // receiver_model.set_wfm_configuration(n); // it is called above , depending user's selection (200k, 180k,40k). - } else { // NFM BW:8k5 or 11k / FM BW 16k - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // - // receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (8k5, 11k, 16k). - } - } - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); -// receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out. - if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return - receiver_model.set_tuning_frequency(tx_frequency); // Update freq also for RX = TX - else - receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate freq controls! - receiver_model.set_lna(rx_lna); - receiver_model.set_vga(rx_vga); - receiver_model.set_rf_amp(rx_amp); - receiver_model.enable(); - hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App - audio::output::start(); - } else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX. - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); //This fixes something with AM RX... - receiver_model.disable(); - baseband::shutdown(); - - baseband::run_image(portapack::spi_flash::image_tag_mic_tx); - audio::output::stop(); - audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF. - portapack::pin_i2s0_rx_sda.mode(3); - configure_baseband(); - } + if (is_on) { + audio::input::stop(); + baseband::shutdown(); + + if (enable_am || enable_usb || enable_lsb || enable_dsb) { // "NFM/FM",0 ," WFM ",1 , " AM ",2, " USB ", 3, " LSB ",4, " DSB-SC", 5 + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); // that AM demodulation engine is common to all Amplitude mod : AM/USB/LSB/DSB (2,3,4,5) + if (options_mode.selected_index() < 5) // We will be called here with 2,3,4,5 . We treat here demod. filter 2,3,4; (excluding DSB-C case (5) it is treated more down). + receiver_model.set_am_configuration(options_mode.selected_index() - 1); // selecting proper filter(2,3,4). 2-1=1=>6k-AM(1) , 3-1=2=>+3k-USB(2), 4-1=3=>-3K-LSB(3), + } else { // We are in NFM/FM or WFM (NFM BW:8k5 or 11k / FM BW 16k / WFM BW:200k) + + if (enable_wfm) { // WFM , BW 200Khz aprox , or the two new addional BW filters (180k, 40k) + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + // receiver_model.set_wfm_configuration(n); // it is called above , depending user's selection (200k, 180k,40k). + } else { // NFM BW:8k5 or 11k / FM BW 16k + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // + // receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (8k5, 11k, 16k). + } + } + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + // receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out. + if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return + receiver_model.set_tuning_frequency(tx_frequency); // Update freq also for RX = TX + else + receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate freq controls! + receiver_model.set_lna(rx_lna); + receiver_model.set_vga(rx_vga); + receiver_model.set_rf_amp(rx_amp); + receiver_model.enable(); + hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App + audio::output::start(); + } else { // These incredibly convoluted steps are required for the vumeter to reappear when stopping RX. + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // This fixes something with AM RX... + receiver_model.disable(); + baseband::shutdown(); + + baseband::run_image(portapack::spi_flash::image_tag_mic_tx); + audio::output::stop(); + audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF. + portapack::pin_i2s0_rx_sda.mode(3); + configure_baseband(); + } } void MicTXView::on_headphone_volume_changed(int32_t v) { - //if (rx_enabled) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); - //} + // if (rx_enabled) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); + //} } void MicTXView::set_ptt_visibility(bool v) { - tx_button.hidden(!v); + tx_button.hidden(!v); } MicTXView::MicTXView( - NavigationView& nav -) -{ - portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming - - baseband::run_image(portapack::spi_flash::image_tag_mic_tx); - - if (audio::debug::codec_name() =="WM8731" ) { - add_children({ - &labels_WM8731, // we have audio codec WM8731, same MIC menu as original. - &vumeter, - &options_gain, // MIC GAIN float factor on the GUI. - &options_wm8731_boost_mode, -// &check_va, - &field_va, - &field_va_level, - &field_va_attack, - &field_va_decay, - &field_bw, - &tx_view, // it already integrates previous rfgain, rfamp. - &options_mode, - &field_frequency, - &options_tone_key, - &check_rogerbeep, - &check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX - &check_rxactive, - &field_volume, - &field_rxbw, - &field_squelch, - &field_rxfrequency, - &field_rxlna, - &field_rxvga, - &field_rxamp, - &tx_button - }); - - } else { - add_children({ - &labels_AK4951, // we have audio codec AK4951, enable Automatic Level Control - &vumeter, - &options_gain, - &options_ak4951_alc_mode, -// &check_va, - &field_va, - &field_va_level, - &field_va_attack, - &field_va_decay, - &field_bw, - &tx_view, // it already integrates previous rfgain, rfamp. - &options_mode, - &field_frequency, - &options_tone_key, - &check_rogerbeep, - &check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX - &check_rxactive, - &field_volume, - &field_rxbw, - &field_squelch, - &field_rxfrequency, - &field_rxlna, - &field_rxvga, - &field_rxamp, - &tx_button - }); - - } - - tone_keys_populate(options_tone_key); - options_tone_key.on_change = [this](size_t i, int32_t) { - tone_key_index = i; - }; - options_tone_key.set_selected_index(0); - - options_gain.on_change = [this](size_t, int32_t v) { - mic_gain = v / 10.0; - configure_baseband(); - }; - options_gain.set_selected_index(1); // x1.0 preselected default. - - if (audio::debug::codec_name() =="WM8731") { - options_wm8731_boost_mode.on_change = [this](size_t, int8_t v) { - - switch(v) { - case 0: // +12 dB’s respect reference level orig fw 1.5.x fw FM : when +20dB's boost ON) and shift bits (>>8), - shift_bits_s16 = 6; // now mic-boost on (+20dBs) and shift bits (>>6), +20+12=32 dB’s (orig fw +20 dBs+ 0dBs)=> +12dB's respect ref. - break; - case 1: // +06 dB’s reference level , (when +20dB's boost ON) - shift_bits_s16 = 7; // now mic-boost on (+20dBs) and shift bits (>>7), +20+06=26 dB’s (orig fw +20 dBs+ 0dBs) => +06dB's respect ref. - break; - case 2: - shift_bits_s16 = 4; // +04 dB’s respect ref level , (when +20dB's boost OFF) - break; // now mic-boost off (+00dBs) shift bits (4) (+0+24dB's)=24 dBs => +04dB's respect ref. - case 3: - shift_bits_s16 = 5; // -02 dB’s respect ref level , (when +20dB's boost OFF) - break; // now mic-boost off (+00dBs) shift bits (5) (+0+18dB's)=18 dBs => -02dB's respect ref. - case 4: - shift_bits_s16 = 6; // -08 dB’s respect ref level , (when +20dB's boost OFF) - break; // now mic-boost off (+00dBs) shift bits (6) (+0+12dB's)=12 dBs => -08dB's respect ref. - } - ak4951_alc_and_wm8731_boost_GUI = v; // 0,..4 WM8731_boost dB's options, (combination boost on/off , and effective gain in captured data >>x) - audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (WM8731) , set up the proper wm_boost on/off , 0..4 (0,1) boost_on , (2,3,4) boost_0ff - configure_baseband(); // to update in real timme,sending msg , var-parameters >>shift_bits FM msg ,to audio_tx from M0 to M4 Proc - - }; - options_wm8731_boost_mode.set_selected_index(3); // preset GUI index 3 as default WM -> -02 dB's . - } else { - shift_bits_s16 = 8; // Initialized default fixed >>8_FM for FM tx mod , shift audio data for AK4951 ,using top 8 bits s16 data (>>8) - options_ak4951_alc_mode.on_change = [this](size_t, int8_t v) { - ak4951_alc_and_wm8731_boost_GUI = v; // 0,..11, AK4951 Mic -Automatic volume Level Control options, - audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (AK4951) ==> Set up proper ALC mode from 0..11 options - configure_baseband(); // sending fixed >>8_FM , var-parameters msg , to audiotx from this M0 to M4 process. - }; - } - - // options_ak4951_alc_mode.set_selected_index(0); - - tx_frequency = transmitter_model.tuning_frequency(); - field_frequency.set_value(transmitter_model.tuning_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - tx_frequency = f; - if(!rx_enabled) { // not activated receiver. just update freq TX - transmitter_model.set_tuning_frequency(f); - } else { // activated receiver. - if (bool_same_F_tx_rx_enabled) // user selected common freq- TX = RX - receiver_model.set_tuning_frequency(f); //Update common freq also for RX - } - }; - field_frequency.on_edit = [this, &nav]() { - focused_ui = 0; - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(tx_frequency); - new_view->on_changed = [this](rf::Frequency f) { - tx_frequency = f; - if(!rx_enabled) { - transmitter_model.set_tuning_frequency(f); - } else { - if (bool_same_F_tx_rx_enabled) - receiver_model.set_tuning_frequency(f); //Update freq also for RX - } - this->field_frequency.set_value(f); - set_dirty(); - }; - }; - - field_bw.on_change = [this](uint32_t v) { - transmitter_model.set_channel_bandwidth(v * 1000); - }; - field_bw.set_value(10); // pre-default first time, TX deviation FM for NFM / FM - - // now , no need direct update , field_rfgain , field_rfamp (it is done in ui_transmitter.cpp) - - options_mode.on_change = [this](size_t, int32_t v) { //{ "NFM/FM", 0 }, { " WFM ", 1 },{ "AM", 2 },{ "USB", 3 },{ "LSB", 4 },{ "DSB", 5 } - enable_am = false; - enable_usb = false; - enable_lsb = false; - enable_dsb = false; - enable_wfm = false; - - using option_t = std::pair; - using options_t = std::vector; - options_t rxbw; // Aux structure to change dynamically field_rxbw contents, - - switch(v) { - case 0: //{ "FM", 0 } - enable_am = false; - enable_usb = false; - enable_lsb = false; - enable_dsb = false; - field_bw.set_value(10); // pre-default deviation FM for WFM - // field_bw.set_value(transmitter_model.channel_bandwidth() / 1000); - //if (rx_enabled) - rxaudio(rx_enabled); //Update now if we have RX audio on - options_tone_key.hidden(0); // we are in FM mode , we should have active the Key-tones & CTCSS option. - - rxbw.emplace_back(" NFM1:8k5 ", 0); // restore the original dynamic field_rxbw value. - rxbw.emplace_back(" NFM2:11k ", 1); - rxbw.emplace_back(" FM :16k ", 2); - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - field_rxbw.hidden(0); // we are in FM mode, we need to allow the user set up of the RX NFM BW selection (8K5, 11K, 16K) - field_bw.hidden(0); // we are in FM mode, we need to allow FM deviation parameter , in non FM mode. - break; - case 1: //{ "WFM", 1 } - enable_am = false; - enable_usb = false; - enable_lsb = false; - enable_dsb = false; - enable_wfm = true; - field_bw.set_value(75); // pre-default deviation FM for WFM - // field_bw.set_value(transmitter_model.channel_bandwidth() / 1000); - //if (rx_enabled) - rxaudio(rx_enabled); //Update now if we have RX audio on - options_tone_key.hidden(0); // we are in WFM mode , we should have active the Key-tones & CTCSS option. - - rxbw.emplace_back(" 200k-WFM ", 0); // We allow the user selection of the 3 x WFM BW filters, (0) WFM-200K, (1) WFM-180K , (2) WFM-40K . - rxbw.emplace_back(" 180k-WFM ", 1); - rxbw.emplace_back(" 40k-WFM ", 2); - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - field_rxbw.hidden(0); // we are in WFM mode, we need to show to the user the selected BW WFM filter . - field_bw.hidden(0); // we are in WFM mode, we need to allow WFM deviation parameter , in non FM mode. - break; - case 2: //{ "AM", 2 } - enable_am = true; - rxaudio(rx_enabled); //Update now if we have RX audio on - options_tone_key.set_selected_index(0); // we are NOT in FM mode , we reset the possible previous key-tones &CTCSS selection. - set_dirty(); // Refresh display - options_tone_key.hidden(1); // we hide that Key-tones & CTCSS input selecction, (no meaning in AM/DSB/SSB). - - rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k . - rxbw.emplace_back(" DSB2-6k ", 1); - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - field_rxbw.hidden(0); // we show fixed RX AM BW 6Khz - field_bw.hidden(1); // we hide the FM TX deviation parameter , in non FM mode. - check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection. - break; - case 3: //{ "USB", 3 } - enable_usb = true; - rxaudio(rx_enabled); //Update now if we have RX audio on - check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. - check_rogerbeep.hidden(1); // hide that roger beep selection. - - rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it . - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - set_dirty(); // Refresh display - break; - case 4: //{ "LSB", 4 } - enable_lsb = true; - rxaudio(rx_enabled); //Update now if we have RX audio on - check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. - check_rogerbeep.hidden(1); // hide that roger beep selection. - - rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it . - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - set_dirty(); // Refresh display - break; - case 5: //{ "DSB-SC", 5 } - enable_dsb = true; - rxaudio(rx_enabled); //Update now if we have RX audio on - check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection. - - rxbw.emplace_back("SSB1:USB+3k", 0); // added dynamically two options (index 0,1) to that DSB-C case to the field_rxbw value. - rxbw.emplace_back("SSB2:LSB-3k", 1); - - field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. - - break; - } - //configure_baseband(); - }; - - - /* - check_va.on_select = [this](Checkbox&, bool v) { - va_enabled = v; - text_ptt.hidden(v); //hide / show PTT text - check_rxactive.hidden(v); //hide / show the RX AUDIO - set_dirty(); //Refresh display - }; - */ - field_va.set_selected_index(1); - field_va.on_change = [this](size_t, int32_t v) { - switch(v) { - case 0: - va_enabled = 0; - this->set_ptt_visibility(0); - check_rxactive.hidden(0); - ptt_enabled = 0; - break; - case 1: - va_enabled = 0; - this->set_ptt_visibility(1); - check_rxactive.hidden(0); - ptt_enabled = 1; - break; - case 2: - if (!rx_enabled) { - va_enabled = 1; - this->set_ptt_visibility(0); - check_rxactive.hidden(1); - ptt_enabled = 0; - } else { - field_va.set_selected_index(1); - } - break; - } - set_dirty(); - }; - - - check_rogerbeep.on_select = [this](Checkbox&, bool v) { - rogerbeep_enabled = v; - }; + NavigationView& nav) { + portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming + + baseband::run_image(portapack::spi_flash::image_tag_mic_tx); + + if (audio::debug::codec_name() == "WM8731") { + add_children({&labels_WM8731, // we have audio codec WM8731, same MIC menu as original. + &vumeter, + &options_gain, // MIC GAIN float factor on the GUI. + &options_wm8731_boost_mode, + // &check_va, + &field_va, + &field_va_level, + &field_va_attack, + &field_va_decay, + &field_bw, + &tx_view, // it already integrates previous rfgain, rfamp. + &options_mode, + &field_frequency, + &options_tone_key, + &check_rogerbeep, + &check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX + &check_rxactive, + &field_volume, + &field_rxbw, + &field_squelch, + &field_rxfrequency, + &field_rxlna, + &field_rxvga, + &field_rxamp, + &tx_button}); + + } else { + add_children({&labels_AK4951, // we have audio codec AK4951, enable Automatic Level Control + &vumeter, + &options_gain, + &options_ak4951_alc_mode, + // &check_va, + &field_va, + &field_va_level, + &field_va_attack, + &field_va_decay, + &field_bw, + &tx_view, // it already integrates previous rfgain, rfamp. + &options_mode, + &field_frequency, + &options_tone_key, + &check_rogerbeep, + &check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX + &check_rxactive, + &field_volume, + &field_rxbw, + &field_squelch, + &field_rxfrequency, + &field_rxlna, + &field_rxvga, + &field_rxamp, + &tx_button}); + } + + tone_keys_populate(options_tone_key); + options_tone_key.on_change = [this](size_t i, int32_t) { + tone_key_index = i; + }; + options_tone_key.set_selected_index(0); + + options_gain.on_change = [this](size_t, int32_t v) { + mic_gain = v / 10.0; + configure_baseband(); + }; + options_gain.set_selected_index(1); // x1.0 preselected default. + + if (audio::debug::codec_name() == "WM8731") { + options_wm8731_boost_mode.on_change = [this](size_t, int8_t v) { + switch (v) { + case 0: // +12 dB’s respect reference level orig fw 1.5.x fw FM : when +20dB's boost ON) and shift bits (>>8), + shift_bits_s16 = 6; // now mic-boost on (+20dBs) and shift bits (>>6), +20+12=32 dB’s (orig fw +20 dBs+ 0dBs)=> +12dB's respect ref. + break; + case 1: // +06 dB’s reference level , (when +20dB's boost ON) + shift_bits_s16 = 7; // now mic-boost on (+20dBs) and shift bits (>>7), +20+06=26 dB’s (orig fw +20 dBs+ 0dBs) => +06dB's respect ref. + break; + case 2: + shift_bits_s16 = 4; // +04 dB’s respect ref level , (when +20dB's boost OFF) + break; // now mic-boost off (+00dBs) shift bits (4) (+0+24dB's)=24 dBs => +04dB's respect ref. + case 3: + shift_bits_s16 = 5; // -02 dB’s respect ref level , (when +20dB's boost OFF) + break; // now mic-boost off (+00dBs) shift bits (5) (+0+18dB's)=18 dBs => -02dB's respect ref. + case 4: + shift_bits_s16 = 6; // -08 dB’s respect ref level , (when +20dB's boost OFF) + break; // now mic-boost off (+00dBs) shift bits (6) (+0+12dB's)=12 dBs => -08dB's respect ref. + } + ak4951_alc_and_wm8731_boost_GUI = v; // 0,..4 WM8731_boost dB's options, (combination boost on/off , and effective gain in captured data >>x) + audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (WM8731) , set up the proper wm_boost on/off , 0..4 (0,1) boost_on , (2,3,4) boost_0ff + configure_baseband(); // to update in real timme,sending msg , var-parameters >>shift_bits FM msg ,to audio_tx from M0 to M4 Proc - + }; + options_wm8731_boost_mode.set_selected_index(3); // preset GUI index 3 as default WM -> -02 dB's . + } else { + shift_bits_s16 = 8; // Initialized default fixed >>8_FM for FM tx mod , shift audio data for AK4951 ,using top 8 bits s16 data (>>8) + options_ak4951_alc_mode.on_change = [this](size_t, int8_t v) { + ak4951_alc_and_wm8731_boost_GUI = v; // 0,..11, AK4951 Mic -Automatic volume Level Control options, + audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (AK4951) ==> Set up proper ALC mode from 0..11 options + configure_baseband(); // sending fixed >>8_FM , var-parameters msg , to audiotx from this M0 to M4 process. + }; + } + + // options_ak4951_alc_mode.set_selected_index(0); + + tx_frequency = transmitter_model.tuning_frequency(); + field_frequency.set_value(transmitter_model.tuning_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + tx_frequency = f; + if (!rx_enabled) { // not activated receiver. just update freq TX + transmitter_model.set_tuning_frequency(f); + } else { // activated receiver. + if (bool_same_F_tx_rx_enabled) // user selected common freq- TX = RX + receiver_model.set_tuning_frequency(f); // Update common freq also for RX + } + }; + field_frequency.on_edit = [this, &nav]() { + focused_ui = 0; + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(tx_frequency); + new_view->on_changed = [this](rf::Frequency f) { + tx_frequency = f; + if (!rx_enabled) { + transmitter_model.set_tuning_frequency(f); + } else { + if (bool_same_F_tx_rx_enabled) + receiver_model.set_tuning_frequency(f); // Update freq also for RX + } + this->field_frequency.set_value(f); + set_dirty(); + }; + }; + + field_bw.on_change = [this](uint32_t v) { + transmitter_model.set_channel_bandwidth(v * 1000); + }; + field_bw.set_value(10); // pre-default first time, TX deviation FM for NFM / FM + + // now , no need direct update , field_rfgain , field_rfamp (it is done in ui_transmitter.cpp) + + options_mode.on_change = [this](size_t, int32_t v) { //{ "NFM/FM", 0 }, { " WFM ", 1 },{ "AM", 2 },{ "USB", 3 },{ "LSB", 4 },{ "DSB", 5 } + enable_am = false; + enable_usb = false; + enable_lsb = false; + enable_dsb = false; + enable_wfm = false; + + using option_t = std::pair; + using options_t = std::vector; + options_t rxbw; // Aux structure to change dynamically field_rxbw contents, + + switch (v) { + case 0: //{ "FM", 0 } + enable_am = false; + enable_usb = false; + enable_lsb = false; + enable_dsb = false; + field_bw.set_value(10); // pre-default deviation FM for WFM + // field_bw.set_value(transmitter_model.channel_bandwidth() / 1000); + // if (rx_enabled) + rxaudio(rx_enabled); // Update now if we have RX audio on + options_tone_key.hidden(0); // we are in FM mode , we should have active the Key-tones & CTCSS option. + + rxbw.emplace_back(" NFM1:8k5 ", 0); // restore the original dynamic field_rxbw value. + rxbw.emplace_back(" NFM2:11k ", 1); + rxbw.emplace_back(" FM :16k ", 2); + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + field_rxbw.hidden(0); // we are in FM mode, we need to allow the user set up of the RX NFM BW selection (8K5, 11K, 16K) + field_bw.hidden(0); // we are in FM mode, we need to allow FM deviation parameter , in non FM mode. + break; + case 1: //{ "WFM", 1 } + enable_am = false; + enable_usb = false; + enable_lsb = false; + enable_dsb = false; + enable_wfm = true; + field_bw.set_value(75); // pre-default deviation FM for WFM + // field_bw.set_value(transmitter_model.channel_bandwidth() / 1000); + // if (rx_enabled) + rxaudio(rx_enabled); // Update now if we have RX audio on + options_tone_key.hidden(0); // we are in WFM mode , we should have active the Key-tones & CTCSS option. + + rxbw.emplace_back(" 200k-WFM ", 0); // We allow the user selection of the 3 x WFM BW filters, (0) WFM-200K, (1) WFM-180K , (2) WFM-40K . + rxbw.emplace_back(" 180k-WFM ", 1); + rxbw.emplace_back(" 40k-WFM ", 2); + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + field_rxbw.hidden(0); // we are in WFM mode, we need to show to the user the selected BW WFM filter . + field_bw.hidden(0); // we are in WFM mode, we need to allow WFM deviation parameter , in non FM mode. + break; + case 2: //{ "AM", 2 } + enable_am = true; + rxaudio(rx_enabled); // Update now if we have RX audio on + options_tone_key.set_selected_index(0); // we are NOT in FM mode , we reset the possible previous key-tones &CTCSS selection. + set_dirty(); // Refresh display + options_tone_key.hidden(1); // we hide that Key-tones & CTCSS input selecction, (no meaning in AM/DSB/SSB). + + rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k . + rxbw.emplace_back(" DSB2-6k ", 1); + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + field_rxbw.hidden(0); // we show fixed RX AM BW 6Khz + field_bw.hidden(1); // we hide the FM TX deviation parameter , in non FM mode. + check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection. + break; + case 3: //{ "USB", 3 } + enable_usb = true; + rxaudio(rx_enabled); // Update now if we have RX audio on + check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. + check_rogerbeep.hidden(1); // hide that roger beep selection. + + rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it . + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + set_dirty(); // Refresh display + break; + case 4: //{ "LSB", 4 } + enable_lsb = true; + rxaudio(rx_enabled); // Update now if we have RX audio on + check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. + check_rogerbeep.hidden(1); // hide that roger beep selection. + + rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it . + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + set_dirty(); // Refresh display + break; + case 5: //{ "DSB-SC", 5 } + enable_dsb = true; + rxaudio(rx_enabled); // Update now if we have RX audio on + check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection. + + rxbw.emplace_back("SSB1:USB+3k", 0); // added dynamically two options (index 0,1) to that DSB-C case to the field_rxbw value. + rxbw.emplace_back("SSB2:LSB-3k", 1); + + field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. + + break; + } + // configure_baseband(); + }; + + /* + check_va.on_select = [this](Checkbox&, bool v) { + va_enabled = v; + text_ptt.hidden(v); //hide / show PTT text + check_rxactive.hidden(v); //hide / show the RX AUDIO + set_dirty(); //Refresh display + }; + */ + field_va.set_selected_index(1); + field_va.on_change = [this](size_t, int32_t v) { + switch (v) { + case 0: + va_enabled = 0; + this->set_ptt_visibility(0); + check_rxactive.hidden(0); + ptt_enabled = 0; + break; + case 1: + va_enabled = 0; + this->set_ptt_visibility(1); + check_rxactive.hidden(0); + ptt_enabled = 1; + break; + case 2: + if (!rx_enabled) { + va_enabled = 1; + this->set_ptt_visibility(0); + check_rxactive.hidden(1); + ptt_enabled = 0; + } else { + field_va.set_selected_index(1); + } + break; + } + set_dirty(); + }; + + check_rogerbeep.on_select = [this](Checkbox&, bool v) { + rogerbeep_enabled = v; + }; check_common_freq_tx_rx.on_select = [this](Checkbox&, bool v) { - bool_same_F_tx_rx_enabled = v; - field_rxfrequency.hidden(v); //Hide or show separated freq RX field . (When no hide user can enter down indep. freq for RX) - set_dirty(); //Refresh GUI interface - receiver_model.set_tuning_frequency(v ? tx_frequency : rx_frequency); // To go to the proper tuned freq. when toggling it - }; - - field_va_level.on_change = [this](int32_t v) { - va_level = v; - vumeter.set_mark(v); - }; - field_va_level.set_value(40); - - field_va_attack.on_change = [this](int32_t v) { - attack_ms = v; - }; - field_va_attack.set_value(500); - - field_va_decay.on_change = [this](int32_t v) { - decay_ms = v; - }; - field_va_decay.set_value(1000); - - check_rxactive.on_select = [this](Checkbox&, bool v) { -// vumeter.set_value(0); //Start with a clean vumeter - rx_enabled = v; -// check_va.hidden(v); //Hide or show voice activation - rxaudio(v); //Activate-Deactivate audio rx accordingly - set_dirty(); //Refresh interface - }; - - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; - - field_rxbw.on_change = [this](size_t, int32_t v) { - if (!(enable_am || enable_usb || enable_lsb || enable_dsb || enable_wfm )) { - // In Previous fw versions, that nbfm_configuration(n) was done in any mode (FM/AM/SSB/DSB)...strictly speaking only need it in (NFM/FM) - receiver_model.set_nbfm_configuration(v ); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter , NFM BW 8K5(0), NFM BW 11K(1) , FM BW 16K (2) - } - else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-SC) - if (enable_am) { // we are in AM TX mode , we will allow both independent RX audio BW : AM 9K (9K00AE3 / AM 6K (6K00AE3). (In AM option v can be 0 (9k) , 1 (6k) - receiver_model.set_am_configuration(v); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1), - } - if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band). in that submenu, v is 0 (SSB1 USB) or 1 (SSB2 LSB) - receiver_model.set_am_configuration(v +2 ); // we are in DSB-SC TX mode , we need to select proper SSB filter. 0+2 =>usb(2), 1+2=3 =>lsb(3), - } - if (enable_wfm) { - receiver_model.set_wfm_configuration(v); // we are in WFM case, we need to select proper WFB RX BW filter , WFM BW 200K(0), WFM BW 180K(1) , WFM BW 40K(2) - } - } - }; - - field_squelch.on_change = [this](int32_t v) { - receiver_model.set_squelch_level(100 - v); - }; - field_squelch.set_value(0); - receiver_model.set_squelch_level(0); - - rx_frequency = receiver_model.tuning_frequency(); - field_rxfrequency.set_value(rx_frequency); - field_rxfrequency.set_step(receiver_model.frequency_step()); - field_rxfrequency.on_change = [this](rf::Frequency f) { // available when field rxfrequency not hidden => user selected separated freq RX/TX- - rx_frequency = f; - if(rx_enabled) - receiver_model.set_tuning_frequency(f); - }; - field_rxfrequency.on_edit = [this, &nav]() { // available when field rxfrequency not hidden => user selected separated freq RX/TX- - focused_ui = 1; - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(rx_frequency); - new_view->on_changed = [this](rf::Frequency f) { - rx_frequency = f; - if(rx_enabled) - receiver_model.set_tuning_frequency(f); - this->field_rxfrequency.set_value(f); - set_dirty(); - }; - }; - - - rx_lna = receiver_model.lna(); - field_rxlna.on_change = [this](int32_t v) { - rx_lna = v; - if(rx_enabled) - receiver_model.set_lna(v); - }; - field_rxlna.set_value(rx_lna); - - rx_vga = receiver_model.vga(); - field_rxvga.on_change = [this](int32_t v) { - rx_vga = v; - if(rx_enabled) - receiver_model.set_vga(v); - }; - field_rxvga.set_value(rx_vga); - - rx_amp = receiver_model.rf_amp(); - field_rxamp.on_change = [this](int32_t v) { - rx_amp = v; - if(rx_enabled) - receiver_model.set_rf_amp(rx_amp); - }; - field_rxamp.set_value(rx_amp); - - tx_button.on_select = [this](Button&) { - if(ptt_enabled && !transmitting) { - set_tx(true); - } - }; - - tx_button.on_touch_release = [this](Button&) { - if(button_touch) { - button_touch = false; - set_tx(false); - } - }; - - tx_button.on_touch_press = [this](Button&) { - if(!transmitting) { - button_touch = true; - } - }; - - transmitter_model.set_sampling_rate(sampling_rate); - transmitter_model.set_baseband_bandwidth(1750000); - - set_tx(false); - - audio::set_rate(audio::Rate::Hz_24000); - audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF. + bool_same_F_tx_rx_enabled = v; + field_rxfrequency.hidden(v); // Hide or show separated freq RX field . (When no hide user can enter down indep. freq for RX) + set_dirty(); // Refresh GUI interface + receiver_model.set_tuning_frequency(v ? tx_frequency : rx_frequency); // To go to the proper tuned freq. when toggling it + }; + + field_va_level.on_change = [this](int32_t v) { + va_level = v; + vumeter.set_mark(v); + }; + field_va_level.set_value(40); + + field_va_attack.on_change = [this](int32_t v) { + attack_ms = v; + }; + field_va_attack.set_value(500); + + field_va_decay.on_change = [this](int32_t v) { + decay_ms = v; + }; + field_va_decay.set_value(1000); + + check_rxactive.on_select = [this](Checkbox&, bool v) { + // vumeter.set_value(0); //Start with a clean vumeter + rx_enabled = v; + // check_va.hidden(v); //Hide or show voice activation + rxaudio(v); // Activate-Deactivate audio rx accordingly + set_dirty(); // Refresh interface + }; + + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + + field_rxbw.on_change = [this](size_t, int32_t v) { + if (!(enable_am || enable_usb || enable_lsb || enable_dsb || enable_wfm)) { + // In Previous fw versions, that nbfm_configuration(n) was done in any mode (FM/AM/SSB/DSB)...strictly speaking only need it in (NFM/FM) + receiver_model.set_nbfm_configuration(v); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter , NFM BW 8K5(0), NFM BW 11K(1) , FM BW 16K (2) + } else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-SC) + if (enable_am) { // we are in AM TX mode , we will allow both independent RX audio BW : AM 9K (9K00AE3 / AM 6K (6K00AE3). (In AM option v can be 0 (9k) , 1 (6k) + receiver_model.set_am_configuration(v); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1), + } + if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band). in that submenu, v is 0 (SSB1 USB) or 1 (SSB2 LSB) + receiver_model.set_am_configuration(v + 2); // we are in DSB-SC TX mode , we need to select proper SSB filter. 0+2 =>usb(2), 1+2=3 =>lsb(3), + } + if (enable_wfm) { + receiver_model.set_wfm_configuration(v); // we are in WFM case, we need to select proper WFB RX BW filter , WFM BW 200K(0), WFM BW 180K(1) , WFM BW 40K(2) + } + } + }; + + field_squelch.on_change = [this](int32_t v) { + receiver_model.set_squelch_level(100 - v); + }; + field_squelch.set_value(0); + receiver_model.set_squelch_level(0); + + rx_frequency = receiver_model.tuning_frequency(); + field_rxfrequency.set_value(rx_frequency); + field_rxfrequency.set_step(receiver_model.frequency_step()); + field_rxfrequency.on_change = [this](rf::Frequency f) { // available when field rxfrequency not hidden => user selected separated freq RX/TX- + rx_frequency = f; + if (rx_enabled) + receiver_model.set_tuning_frequency(f); + }; + field_rxfrequency.on_edit = [this, &nav]() { // available when field rxfrequency not hidden => user selected separated freq RX/TX- + focused_ui = 1; + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(rx_frequency); + new_view->on_changed = [this](rf::Frequency f) { + rx_frequency = f; + if (rx_enabled) + receiver_model.set_tuning_frequency(f); + this->field_rxfrequency.set_value(f); + set_dirty(); + }; + }; + + rx_lna = receiver_model.lna(); + field_rxlna.on_change = [this](int32_t v) { + rx_lna = v; + if (rx_enabled) + receiver_model.set_lna(v); + }; + field_rxlna.set_value(rx_lna); + + rx_vga = receiver_model.vga(); + field_rxvga.on_change = [this](int32_t v) { + rx_vga = v; + if (rx_enabled) + receiver_model.set_vga(v); + }; + field_rxvga.set_value(rx_vga); + + rx_amp = receiver_model.rf_amp(); + field_rxamp.on_change = [this](int32_t v) { + rx_amp = v; + if (rx_enabled) + receiver_model.set_rf_amp(rx_amp); + }; + field_rxamp.set_value(rx_amp); + + tx_button.on_select = [this](Button&) { + if (ptt_enabled && !transmitting) { + set_tx(true); + } + }; + + tx_button.on_touch_release = [this](Button&) { + if (button_touch) { + button_touch = false; + set_tx(false); + } + }; + + tx_button.on_touch_press = [this](Button&) { + if (!transmitting) { + button_touch = true; + } + }; + + transmitter_model.set_sampling_rate(sampling_rate); + transmitter_model.set_baseband_bandwidth(1750000); + + set_tx(false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF. } MicTXView::~MicTXView() { - audio::input::stop(); - transmitter_model.set_tuning_frequency(tx_frequency); // Save Tx frequency instead of Rx. Or maybe we need some "System Wide" changes to seperate Tx and Rx frequency. - transmitter_model.disable(); - if (rx_enabled) //Also turn off audio rx if enabled - rxaudio(false); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + audio::input::stop(); + transmitter_model.set_tuning_frequency(tx_frequency); // Save Tx frequency instead of Rx. Or maybe we need some "System Wide" changes to seperate Tx and Rx frequency. + transmitter_model.disable(); + if (rx_enabled) // Also turn off audio rx if enabled + rxaudio(false); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } -} +} // namespace ui diff --git a/firmware/application/apps/ui_mictx.hpp b/firmware/application/apps/ui_mictx.hpp index 737953aed..f6af62feb 100644 --- a/firmware/application/apps/ui_mictx.hpp +++ b/firmware/application/apps/ui_mictx.hpp @@ -23,8 +23,8 @@ #ifndef __UI_MICTX_H__ #define __UI_MICTX_H__ -#define SHORT_UI true -#define NORMAL_UI false +#define SHORT_UI true +#define NORMAL_UI false #include "ui.hpp" #include "ui_widget.hpp" @@ -39,351 +39,324 @@ namespace ui { class MicTXView : public View { -public: - MicTXView(NavigationView& nav); - ~MicTXView(); - - MicTXView(const MicTXView&) = delete; - MicTXView(MicTXView&&) = delete; - MicTXView& operator=(const MicTXView&) = delete; - MicTXView& operator=(MicTXView&&) = delete; - - void focus() override; - - // PTT: Enable through KeyEvent (only works with presses), disable by polling :( - // This is the old "RIGHT BUTTON" method. - /* - bool on_key(const KeyEvent key) { - if ((key == KeyEvent::Right) && (!va_enabled) && ptt_enabled) { - set_tx(true); - return true; - } else - return false; - }; - */ - - std::string title() const override { return "Microphone"; }; - -private: - static constexpr uint32_t sampling_rate = 1536000U; - static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point /60 - - void update_vumeter(); - void do_timing(); - void set_tx(bool enable); -// void on_tuning_frequency_changed(rf::Frequency f); - void on_tx_progress(const bool done); - void configure_baseband(); - - void rxaudio(bool is_on); - void on_headphone_volume_changed(int32_t v); - - void set_ptt_visibility(bool v); - - bool transmitting { false }; - bool va_enabled { false }; - bool ptt_enabled { true }; - bool rogerbeep_enabled { false }; - bool bool_same_F_tx_rx_enabled { false }; - bool rx_enabled { false }; - uint32_t tone_key_index { }; - float mic_gain { 1.0 }; - uint8_t ak4951_alc_and_wm8731_boost_GUI { 0 }; - uint32_t audio_level { 0 }; - uint32_t va_level { }; - uint32_t attack_ms { }; - uint32_t decay_ms { }; - uint32_t attack_timer { 0 }; - uint32_t decay_timer { 0 }; - int32_t tx_gain { 47 }; - bool rf_amp { false }; - int32_t rx_lna { 32 }; - int32_t rx_vga { 32 }; - bool rx_amp { false }; - rf::Frequency tx_frequency { 0 }; - rf::Frequency rx_frequency { 0 }; - int32_t focused_ui { 2 }; - bool button_touch { false }; - uint8_t shift_bits_s16 {4} ; // shift bits factor to the captured ADC S16 audio sample. - - //AM TX Stuff - bool enable_am { false }; - bool enable_dsb { false }; - bool enable_usb { false }; - bool enable_lsb { false }; - bool enable_wfm { false }; // added to distinguish in the FM mode , RX BW : NFM (8K5, 11K), FM (16K), WFM(200K) - - - Labels labels_WM8731 { - { { 3 * 8, 1 * 8 }, "MIC-GAIN:", Color::light_grey() }, - { { 17 * 8, 1 * 8 }, "Boost", Color::light_grey() }, - { { 3 * 8, 3 * 8 }, "F:", Color::light_grey() }, - { { 15 * 8, 3 * 8 }, "FM TXBW: kHz", Color::light_grey() }, // to be more symetric and consistent to the below FM RXBW - { { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() }, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp - { { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() }, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() }, - { { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() }, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() }, - { {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() }, - { {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() }, - { { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() }, - { { 7 * 8, 23 * 8 }, "VOL:", Color::light_grey() }, - { {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW. - { {20 * 8, 25 * 8 }, "SQ:", Color::light_grey() }, - { { 5 * 8, 25 * 8 }, "F_RX:", Color::light_grey() }, - { { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()}, - { {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()}, - { {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()} - }; - Labels labels_AK4951 { - { { 3 * 8, 1 * 8 }, "MIC-GAIN:", Color::light_grey() }, - { { 17 * 8, 1 * 8 }, "ALC", Color::light_grey() }, - { { 3 * 8, 3 * 8 }, "F:", Color::light_grey() }, - { { 15 * 8, 3 * 8 }, "FM TXBW: kHz", Color::light_grey() }, - { { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() }, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp - { { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() }, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() }, - { { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() }, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() }, - { {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() }, - { {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() }, - { { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() }, - { { (6 * 8)+4, 23 * 8 }, "VOL:", Color::light_grey() }, - { {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW. - { {20 * 8, 25 * 8 }, "SQ:", Color::light_grey() }, - { { 5 * 8, 25 * 8 }, "F_RX:", Color::light_grey() }, - { { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()}, - { {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()}, - { {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()} - }; - - VuMeter vumeter { - { 0 * 8, 1 * 8, 2 * 8, 33 * 8 }, - 12, - true - }; - - - OptionsField options_gain { - { 12 * 8, 1 * 8 }, - 4, - { - { "x0.5", 5 }, - { "x1.0", 10 }, - { "x1.5", 15 }, - { "x2.0", 20 } - } - }; - - OptionsField options_ak4951_alc_mode { - { 20 * 8, 1 * 8 }, // Coordinates are: int:x (px), int:y (px) - 11, - { - { " OFF-12kHz", 0 }, // Nothing changed from ORIGINAL,keeping ALL programmable AK4951 Digital Block->OFF, sampling 24Khz) - { "+12dB-6kHz", 1 }, // ALC-> on, (+12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "+09dB-6kHz", 2 }, // ALC-> on, (+09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "+06dB-6kHz", 3 }, // ALC-> on, (+06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "+03dB-2kHz", 4 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 3,5k + Pre-amp Mic (+21dB=original)+ EQ boosting ~<2kHz (f0~1k1,fb:1,7K, k=1,8) - { "+03dB-4kHz", 5 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 4kHz + Pre-amp Mic (+21dB=original)+ EQ boosting ~<3kHz (f0~1k4,fb~2,4k, k=1,8) - { "+03dB-6kHz", 6 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "+00dB-6kHz", 7 }, // ALC-> on, (+00dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "-03dB-6kHz", 8 }, // ALC-> on, (-03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "-06dB-6kHz", 9 }, // ALC-> on, (-06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - { "-09dB-6kHz", 10 }, // ALC-> on, (-09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -3dB (18dB's) - { "-12dB-6kHz", 11 }, // ALC-> on, (-12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -6dB (15dB's) - } - }; - -OptionsField options_wm8731_boost_mode { - { 22 * 8, 1 * 8 }, // Coordinates are: int:x (px), int:y (px) - 5, - { - { "ON +12dB", 0 }, // WM8731 Mic Boost ON ,original+12dBs condition, easy to saturate ADC sat in high voice ,relative G = +12 dB's respect ref level - { "ON +06dB", 1 }, // WM8731 Mic Boost ON ,original+6 dBs condition, easy to saturate ADC sat in high voice ,relative G = +06 dB's respect ref level - { "OFF+04dB", 2 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = +04 dB's (respect ref level) , always effective sampling 24khz - { "OFF-02dB", 3 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -02 dB's (respect ref level) - { "OFF-08dB", 4 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -12 dB's (respect ref level) - } - }; - - FrequencyField field_frequency { - { 5 * 8, 3 * 8 }, - }; - NumberField field_bw { - { 23 * 8, 3 * 8 }, - 3, - { 0, 150 }, - 1, - ' ' - }; - - TransmitterView2 tx_view { // new handling of NumberField field_rfgain, NumberField field_rfamp - // 3*8, 2*8, SHORT_UI // x(columns), y (line) position. (used in Replay / GPS Simul / Playlist App's) - 3*8, 2*8, NORMAL_UI // x(columns), y (line) position. (used in Mic App) - }; - - OptionsField options_mode { - { 24 * 8, 5 * 8 }, - 4, - { - { "NFM/FM", 0 }, - { " WFM ", 1 }, - { " AM ", 2 }, // in fact that TX mode = AM -DSB with carrier . - { " USB ", 3 }, - { " LSB ", 4 }, - { "DSB-SC", 5 } // We are TX Double Side AM Band with suppressed carrier, and allowing in RX both indep SSB lateral band (USB/LSB). - } - }; - /* - Checkbox check_va { - { 3 * 8, (10 * 8) - 4 }, - 7, - "Voice activation", - false - }; - */ - - OptionsField field_va { - { 17 * 8, 8 * 8 }, - 3, - { - {" OFF", 0}, - {" PTT", 1}, - {"AUTO", 2} - } - }; - - NumberField field_va_level { - { 8 * 8, 10 * 8 }, - 3, - { 0, 255 }, - 2, - ' ' - }; - NumberField field_va_attack { - { 16 * 8, 10 * 8 }, - 3, - { 0, 999 }, - 20, - ' ' - }; - NumberField field_va_decay { - { 24 * 8, 10 * 8 }, - 4, - { 0, 9999 }, - 100, - ' ' - }; - - OptionsField options_tone_key { - { 10 * 8, ( 15 * 8 ) - 2 }, - 23, - { } - }; - - Checkbox check_rogerbeep { - { 3 * 8, ( 16 * 8 ) + 7 }, - 10, - "Roger beep", - false - }; - - Checkbox check_rxactive { - { 3 * 8, ( 21 * 8 ) - 4 }, - 18, // it was 8, but if it is string size should be 18 - "RX audio listening", - false - }; - - Checkbox check_common_freq_tx_rx { - { 18 * 8, ( 16* 8 ) + 7 }, - 8, - "F = F_RX", - false - }; - - NumberField field_volume { - { 11* 8, 23 * 8 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - OptionsField field_rxbw { - { 19* 8, 23 * 8}, - 3, - { - {" NFM1:8k5 ", 0}, - {" NFM2:11k ", 1}, - {" FM :16k ", 2}, - } - }; - - NumberField field_squelch { - { 23 * 8, 25 * 8 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - FrequencyField field_rxfrequency { - { 10 * 8, 25 * 8 }, - }; - - NumberField field_rxlna { - { 9 * 8, 27 * 8 }, - 2, - { 0, 40 }, - 8, - ' ', - }; - - NumberField field_rxvga { - { 16 * 8, 27 * 8 }, - 2, - { 0, 62 }, - 2, - ' ', - }; - - NumberField field_rxamp { - { 24 * 8, 27 * 8 }, - 1, - { 0, 1 }, - 1, - ' ', - }; - - Button tx_button { - { 10 * 8, 30 * 8, 10 * 8, 5 * 8 }, - "TX", - true - }; - - - MessageHandlerRegistration message_handler_lcd_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->do_timing(); - this->update_vumeter(); - } - }; - - MessageHandlerRegistration message_handler_audio_level { - Message::ID::AudioLevelReport, - [this](const Message* const p) { - const auto message = static_cast(p); - this->audio_level = message->value; - } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.done); - } - }; + public: + MicTXView(NavigationView& nav); + ~MicTXView(); + + MicTXView(const MicTXView&) = delete; + MicTXView(MicTXView&&) = delete; + MicTXView& operator=(const MicTXView&) = delete; + MicTXView& operator=(MicTXView&&) = delete; + + void focus() override; + + // PTT: Enable through KeyEvent (only works with presses), disable by polling :( + // This is the old "RIGHT BUTTON" method. + /* + bool on_key(const KeyEvent key) { + if ((key == KeyEvent::Right) && (!va_enabled) && ptt_enabled) { + set_tx(true); + return true; + } else + return false; + }; + */ + + std::string title() const override { return "Microphone"; }; + + private: + static constexpr uint32_t sampling_rate = 1536000U; + static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point /60 + + void update_vumeter(); + void do_timing(); + void set_tx(bool enable); + // void on_tuning_frequency_changed(rf::Frequency f); + void on_tx_progress(const bool done); + void configure_baseband(); + + void rxaudio(bool is_on); + void on_headphone_volume_changed(int32_t v); + + void set_ptt_visibility(bool v); + + bool transmitting{false}; + bool va_enabled{false}; + bool ptt_enabled{true}; + bool rogerbeep_enabled{false}; + bool bool_same_F_tx_rx_enabled{false}; + bool rx_enabled{false}; + uint32_t tone_key_index{}; + float mic_gain{1.0}; + uint8_t ak4951_alc_and_wm8731_boost_GUI{0}; + uint32_t audio_level{0}; + uint32_t va_level{}; + uint32_t attack_ms{}; + uint32_t decay_ms{}; + uint32_t attack_timer{0}; + uint32_t decay_timer{0}; + int32_t tx_gain{47}; + bool rf_amp{false}; + int32_t rx_lna{32}; + int32_t rx_vga{32}; + bool rx_amp{false}; + rf::Frequency tx_frequency{0}; + rf::Frequency rx_frequency{0}; + int32_t focused_ui{2}; + bool button_touch{false}; + uint8_t shift_bits_s16{4}; // shift bits factor to the captured ADC S16 audio sample. + + // AM TX Stuff + bool enable_am{false}; + bool enable_dsb{false}; + bool enable_usb{false}; + bool enable_lsb{false}; + bool enable_wfm{false}; // added to distinguish in the FM mode , RX BW : NFM (8K5, 11K), FM (16K), WFM(200K) + + Labels labels_WM8731{ + {{3 * 8, 1 * 8}, "MIC-GAIN:", Color::light_grey()}, + {{17 * 8, 1 * 8}, "Boost", Color::light_grey()}, + {{3 * 8, 3 * 8}, "F:", Color::light_grey()}, + {{15 * 8, 3 * 8}, "FM TXBW: kHz", Color::light_grey()}, // to be more symetric and consistent to the below FM RXBW + {{18 * 8, (5 * 8)}, "Mode:", Color::light_grey()}, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp + {{3 * 8, 8 * 8}, "TX Activation:", Color::light_grey()}, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() }, + {{4 * 8, 10 * 8}, "LVL:", Color::light_grey()}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() }, + {{12 * 8, 10 * 8}, "ATT:", Color::light_grey()}, + {{20 * 8, 10 * 8}, "DEC:", Color::light_grey()}, + {{4 * 8, (13 * 8) - 2}, "TONE KEY:", Color::light_grey()}, + {{7 * 8, 23 * 8}, "VOL:", Color::light_grey()}, + {{14 * 8, 23 * 8}, "RXBW:", Color::light_grey()}, // we remove the label "FM" because we will display all MOD types RX_BW. + {{20 * 8, 25 * 8}, "SQ:", Color::light_grey()}, + {{5 * 8, 25 * 8}, "F_RX:", Color::light_grey()}, + {{5 * 8, 27 * 8}, "LNA:", Color::light_grey()}, + {{12 * 8, 27 * 8}, "VGA:", Color::light_grey()}, + {{19 * 8, 27 * 8}, "AMP:", Color::light_grey()}}; + Labels labels_AK4951{ + {{3 * 8, 1 * 8}, "MIC-GAIN:", Color::light_grey()}, + {{17 * 8, 1 * 8}, "ALC", Color::light_grey()}, + {{3 * 8, 3 * 8}, "F:", Color::light_grey()}, + {{15 * 8, 3 * 8}, "FM TXBW: kHz", Color::light_grey()}, + {{18 * 8, (5 * 8)}, "Mode:", Color::light_grey()}, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp + {{3 * 8, 8 * 8}, "TX Activation:", Color::light_grey()}, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() }, + {{4 * 8, 10 * 8}, "LVL:", Color::light_grey()}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() }, + {{12 * 8, 10 * 8}, "ATT:", Color::light_grey()}, + {{20 * 8, 10 * 8}, "DEC:", Color::light_grey()}, + {{4 * 8, (13 * 8) - 2}, "TONE KEY:", Color::light_grey()}, + {{(6 * 8) + 4, 23 * 8}, "VOL:", Color::light_grey()}, + {{14 * 8, 23 * 8}, "RXBW:", Color::light_grey()}, // we remove the label "FM" because we will display all MOD types RX_BW. + {{20 * 8, 25 * 8}, "SQ:", Color::light_grey()}, + {{5 * 8, 25 * 8}, "F_RX:", Color::light_grey()}, + {{5 * 8, 27 * 8}, "LNA:", Color::light_grey()}, + {{12 * 8, 27 * 8}, "VGA:", Color::light_grey()}, + {{19 * 8, 27 * 8}, "AMP:", Color::light_grey()}}; + + VuMeter vumeter{ + {0 * 8, 1 * 8, 2 * 8, 33 * 8}, + 12, + true}; + + OptionsField options_gain{ + {12 * 8, 1 * 8}, + 4, + {{"x0.5", 5}, + {"x1.0", 10}, + {"x1.5", 15}, + {"x2.0", 20}}}; + + OptionsField options_ak4951_alc_mode{ + {20 * 8, 1 * 8}, // Coordinates are: int:x (px), int:y (px) + 11, + { + {" OFF-12kHz", 0}, // Nothing changed from ORIGINAL,keeping ALL programmable AK4951 Digital Block->OFF, sampling 24Khz) + {"+12dB-6kHz", 1}, // ALC-> on, (+12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"+09dB-6kHz", 2}, // ALC-> on, (+09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"+06dB-6kHz", 3}, // ALC-> on, (+06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"+03dB-2kHz", 4}, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 3,5k + Pre-amp Mic (+21dB=original)+ EQ boosting ~<2kHz (f0~1k1,fb:1,7K, k=1,8) + {"+03dB-4kHz", 5}, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 4kHz + Pre-amp Mic (+21dB=original)+ EQ boosting ~<3kHz (f0~1k4,fb~2,4k, k=1,8) + {"+03dB-6kHz", 6}, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"+00dB-6kHz", 7}, // ALC-> on, (+00dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"-03dB-6kHz", 8}, // ALC-> on, (-03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"-06dB-6kHz", 9}, // ALC-> on, (-06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + {"-09dB-6kHz", 10}, // ALC-> on, (-09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -3dB (18dB's) + {"-12dB-6kHz", 11}, // ALC-> on, (-12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -6dB (15dB's) + }}; + + OptionsField options_wm8731_boost_mode{ + {22 * 8, 1 * 8}, // Coordinates are: int:x (px), int:y (px) + 5, + { + {"ON +12dB", 0}, // WM8731 Mic Boost ON ,original+12dBs condition, easy to saturate ADC sat in high voice ,relative G = +12 dB's respect ref level + {"ON +06dB", 1}, // WM8731 Mic Boost ON ,original+6 dBs condition, easy to saturate ADC sat in high voice ,relative G = +06 dB's respect ref level + {"OFF+04dB", 2}, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = +04 dB's (respect ref level) , always effective sampling 24khz + {"OFF-02dB", 3}, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -02 dB's (respect ref level) + {"OFF-08dB", 4}, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -12 dB's (respect ref level) + }}; + + FrequencyField field_frequency{ + {5 * 8, 3 * 8}, + }; + NumberField field_bw{ + {23 * 8, 3 * 8}, + 3, + {0, 150}, + 1, + ' '}; + + TransmitterView2 tx_view{ + // new handling of NumberField field_rfgain, NumberField field_rfamp + // 3*8, 2*8, SHORT_UI // x(columns), y (line) position. (used in Replay / GPS Simul / Playlist App's) + 3 * 8, 2 * 8, NORMAL_UI // x(columns), y (line) position. (used in Mic App) + }; + + OptionsField options_mode{ + {24 * 8, 5 * 8}, + 4, + { + {"NFM/FM", 0}, + {" WFM ", 1}, + {" AM ", 2}, // in fact that TX mode = AM -DSB with carrier . + {" USB ", 3}, + {" LSB ", 4}, + {"DSB-SC", 5} // We are TX Double Side AM Band with suppressed carrier, and allowing in RX both indep SSB lateral band (USB/LSB). + }}; + /* + Checkbox check_va { + { 3 * 8, (10 * 8) - 4 }, + 7, + "Voice activation", + false + }; + */ + + OptionsField field_va{ + {17 * 8, 8 * 8}, + 3, + {{" OFF", 0}, + {" PTT", 1}, + {"AUTO", 2}}}; + + NumberField field_va_level{ + {8 * 8, 10 * 8}, + 3, + {0, 255}, + 2, + ' '}; + NumberField field_va_attack{ + {16 * 8, 10 * 8}, + 3, + {0, 999}, + 20, + ' '}; + NumberField field_va_decay{ + {24 * 8, 10 * 8}, + 4, + {0, 9999}, + 100, + ' '}; + + OptionsField options_tone_key{ + {10 * 8, (15 * 8) - 2}, + 23, + {}}; + + Checkbox check_rogerbeep{ + {3 * 8, (16 * 8) + 7}, + 10, + "Roger beep", + false}; + + Checkbox check_rxactive{ + {3 * 8, (21 * 8) - 4}, + 18, // it was 8, but if it is string size should be 18 + "RX audio listening", + false}; + + Checkbox check_common_freq_tx_rx{ + {18 * 8, (16 * 8) + 7}, + 8, + "F = F_RX", + false}; + + NumberField field_volume{ + {11 * 8, 23 * 8}, + 2, + {0, 99}, + 1, + ' ', + }; + + OptionsField field_rxbw{ + {19 * 8, 23 * 8}, + 3, + { + {" NFM1:8k5 ", 0}, + {" NFM2:11k ", 1}, + {" FM :16k ", 2}, + }}; + + NumberField field_squelch{ + {23 * 8, 25 * 8}, + 2, + {0, 99}, + 1, + ' ', + }; + + FrequencyField field_rxfrequency{ + {10 * 8, 25 * 8}, + }; + + NumberField field_rxlna{ + {9 * 8, 27 * 8}, + 2, + {0, 40}, + 8, + ' ', + }; + + NumberField field_rxvga{ + {16 * 8, 27 * 8}, + 2, + {0, 62}, + 2, + ' ', + }; + + NumberField field_rxamp{ + {24 * 8, 27 * 8}, + 1, + {0, 1}, + 1, + ' ', + }; + + Button tx_button{ + {10 * 8, 30 * 8, 10 * 8, 5 * 8}, + "TX", + true}; + + MessageHandlerRegistration message_handler_lcd_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->do_timing(); + this->update_vumeter(); + }}; + + MessageHandlerRegistration message_handler_audio_level{ + Message::ID::AudioLevelReport, + [this](const Message* const p) { + const auto message = static_cast(p); + this->audio_level = message->value; + }}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.done); + }}; }; } /* namespace ui */ -#endif/*__UI_MICTX_H__*/ +#endif /*__UI_MICTX_H__*/ diff --git a/firmware/application/apps/ui_modemsetup.cpp b/firmware/application/apps/ui_modemsetup.cpp index 6d28cf90f..7eb80cc45 100644 --- a/firmware/application/apps/ui_modemsetup.cpp +++ b/firmware/application/apps/ui_modemsetup.cpp @@ -32,79 +32,75 @@ using namespace modems; namespace ui { void ModemSetupView::focus() { - field_baudrate.focus(); + field_baudrate.focus(); } ModemSetupView::ModemSetupView( - NavigationView& nav -) -{ - using option_t = std::pair; - using options_t = std::vector; - options_t modem_options; - - add_children({ - &labels, - &field_baudrate, - &field_mark, - &field_space, - &field_repeat, - &options_modem, - &button_set_modem, - &sym_format, - &button_save - }); - - // Only list AFSK modems for now - for (size_t i = 0; i < MODEM_DEF_COUNT; i++) { - if (modem_defs[i].modulation == AFSK) - modem_options.emplace_back(std::make_pair(modem_defs[i].name, i)); - } - options_modem.set_options(modem_options); - options_modem.set_selected_index(0); - - sym_format.set_symbol_list(0, "6789"); // Data bits - sym_format.set_symbol_list(1, "NEo"); // Parity - sym_format.set_symbol_list(2, "012"); // Stop bits - sym_format.set_symbol_list(3, "ML"); // MSB/LSB first - - sym_format.set_sym(0, persistent_memory::serial_format().data_bits - 6); - sym_format.set_sym(1, persistent_memory::serial_format().parity); - sym_format.set_sym(2, persistent_memory::serial_format().stop_bits); - sym_format.set_sym(3, persistent_memory::serial_format().bit_order); - - field_mark.set_value(persistent_memory::afsk_mark_freq()); - field_space.set_value(persistent_memory::afsk_space_freq()); - field_repeat.set_value(persistent_memory::modem_repeat()); - - field_baudrate.set_value(persistent_memory::modem_baudrate()); - - button_set_modem.on_select = [this, &nav](Button&) { - size_t modem_def_index = options_modem.selected_index(); - - field_mark.set_value(modem_defs[modem_def_index].mark_freq); - field_space.set_value(modem_defs[modem_def_index].space_freq); - field_baudrate.set_value(modem_defs[modem_def_index].baudrate); - }; - - button_save.on_select = [this,&nav](Button&) { - serial_format_t serial_format; - - persistent_memory::set_afsk_mark(field_mark.value()); - persistent_memory::set_afsk_space(field_space.value()); - - persistent_memory::set_modem_baudrate(field_baudrate.value()); - persistent_memory::set_modem_repeat(field_repeat.value()); - - serial_format.data_bits = sym_format.get_sym(0) + 6; - serial_format.parity = (parity_enum)sym_format.get_sym(1); - serial_format.stop_bits = sym_format.get_sym(2); - serial_format.bit_order = (order_enum)sym_format.get_sym(3); - - persistent_memory::set_serial_format(serial_format); - - nav.pop(); - }; + NavigationView& nav) { + using option_t = std::pair; + using options_t = std::vector; + options_t modem_options; + + add_children({&labels, + &field_baudrate, + &field_mark, + &field_space, + &field_repeat, + &options_modem, + &button_set_modem, + &sym_format, + &button_save}); + + // Only list AFSK modems for now + for (size_t i = 0; i < MODEM_DEF_COUNT; i++) { + if (modem_defs[i].modulation == AFSK) + modem_options.emplace_back(std::make_pair(modem_defs[i].name, i)); + } + options_modem.set_options(modem_options); + options_modem.set_selected_index(0); + + sym_format.set_symbol_list(0, "6789"); // Data bits + sym_format.set_symbol_list(1, "NEo"); // Parity + sym_format.set_symbol_list(2, "012"); // Stop bits + sym_format.set_symbol_list(3, "ML"); // MSB/LSB first + + sym_format.set_sym(0, persistent_memory::serial_format().data_bits - 6); + sym_format.set_sym(1, persistent_memory::serial_format().parity); + sym_format.set_sym(2, persistent_memory::serial_format().stop_bits); + sym_format.set_sym(3, persistent_memory::serial_format().bit_order); + + field_mark.set_value(persistent_memory::afsk_mark_freq()); + field_space.set_value(persistent_memory::afsk_space_freq()); + field_repeat.set_value(persistent_memory::modem_repeat()); + + field_baudrate.set_value(persistent_memory::modem_baudrate()); + + button_set_modem.on_select = [this, &nav](Button&) { + size_t modem_def_index = options_modem.selected_index(); + + field_mark.set_value(modem_defs[modem_def_index].mark_freq); + field_space.set_value(modem_defs[modem_def_index].space_freq); + field_baudrate.set_value(modem_defs[modem_def_index].baudrate); + }; + + button_save.on_select = [this, &nav](Button&) { + serial_format_t serial_format; + + persistent_memory::set_afsk_mark(field_mark.value()); + persistent_memory::set_afsk_space(field_space.value()); + + persistent_memory::set_modem_baudrate(field_baudrate.value()); + persistent_memory::set_modem_repeat(field_repeat.value()); + + serial_format.data_bits = sym_format.get_sym(0) + 6; + serial_format.parity = (parity_enum)sym_format.get_sym(1); + serial_format.stop_bits = sym_format.get_sym(2); + serial_format.bit_order = (order_enum)sym_format.get_sym(3); + + persistent_memory::set_serial_format(serial_format); + + nav.pop(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_modemsetup.hpp b/firmware/application/apps/ui_modemsetup.hpp index e50cb98a5..534252eb2 100644 --- a/firmware/application/apps/ui_modemsetup.hpp +++ b/firmware/application/apps/ui_modemsetup.hpp @@ -30,79 +30,69 @@ namespace ui { class ModemSetupView : public View { -public: - ModemSetupView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Modem setup"; }; - -private: - void update_freq(rf::Frequency f); - - Labels labels { - { { 2 * 8, 11 * 8 }, "Baudrate:", Color::light_grey() }, - { { 2 * 8, 13 * 8 }, "Mark: Hz", Color::light_grey() }, - { { 2 * 8, 15 * 8 }, "Space: Hz", Color::light_grey() }, - { { 140, 15 * 8 }, "Repeat:", Color::light_grey() }, - { { 1 * 8, 6 * 8 }, "Modem preset:", Color::light_grey() }, - { { 2 * 8, 22 * 8 }, "Serial format:", Color::light_grey() } - }; - - NumberField field_baudrate { - { 11 * 8, 11 * 8 }, - 5, - { 50, 9600 }, - 25, - ' ' - }; - - NumberField field_mark { - { 8 * 8, 13 * 8 }, - 5, - { 100, 15000 }, - 25, - ' ' - }; - - NumberField field_space { - { 8 * 8, 15 * 8 }, - 5, - { 100, 15000 }, - 25, - ' ' - }; - - NumberField field_repeat { - { 204, 15 * 8 }, - 2, - { 1, 99 }, - 1, - ' ' - }; - - OptionsField options_modem { - { 15 * 8, 6 * 8 }, - 7, - { - } - }; - - SymField sym_format { - { 16 * 8, 22 * 8 }, - 4, - SymField::SYMFIELD_DEF - }; - - Button button_set_modem { - { 23 * 8, 6 * 8 - 4, 6 * 8, 24 }, - "SET" - }; - - Button button_save { - { 9 * 8, 250, 96, 40 }, - "Save" - }; + public: + ModemSetupView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Modem setup"; }; + + private: + void update_freq(rf::Frequency f); + + Labels labels{ + {{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()}, + {{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()}, + {{2 * 8, 15 * 8}, "Space: Hz", Color::light_grey()}, + {{140, 15 * 8}, "Repeat:", Color::light_grey()}, + {{1 * 8, 6 * 8}, "Modem preset:", Color::light_grey()}, + {{2 * 8, 22 * 8}, "Serial format:", Color::light_grey()}}; + + NumberField field_baudrate{ + {11 * 8, 11 * 8}, + 5, + {50, 9600}, + 25, + ' '}; + + NumberField field_mark{ + {8 * 8, 13 * 8}, + 5, + {100, 15000}, + 25, + ' '}; + + NumberField field_space{ + {8 * 8, 15 * 8}, + 5, + {100, 15000}, + 25, + ' '}; + + NumberField field_repeat{ + {204, 15 * 8}, + 2, + {1, 99}, + 1, + ' '}; + + OptionsField options_modem{ + {15 * 8, 6 * 8}, + 7, + {}}; + + SymField sym_format{ + {16 * 8, 22 * 8}, + 4, + SymField::SYMFIELD_DEF}; + + Button button_set_modem{ + {23 * 8, 6 * 8 - 4, 6 * 8, 24}, + "SET"}; + + Button button_save{ + {9 * 8, 250, 96, 40}, + "Save"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_morse.cpp b/firmware/application/apps/ui_morse.cpp index 5191047d3..bc7b1212a 100644 --- a/firmware/application/apps/ui_morse.cpp +++ b/firmware/application/apps/ui_morse.cpp @@ -41,246 +41,243 @@ namespace ui { static WORKING_AREA(ookthread_wa, 256); -static msg_t ookthread_fn(void * arg) { - uint32_t v = 0, delay = 0; - uint8_t * message_symbols = shared_memory.bb_data.tones_data.message; - uint8_t symbol; - MorseView * arg_c = (MorseView*)arg; - - chRegSetThreadName("ookthread"); - - for (uint32_t i = 0; i < arg_c->symbol_count; i++) { - if (chThdShouldTerminate()) break; - - symbol = message_symbols[i]; - - v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause - delay = morse_symbols[symbol]; - - gpio_og_tx.write(v); - arg_c->on_tx_progress(i, false); - - chThdSleepMilliseconds(delay * arg_c->time_unit_ms); - } - - gpio_og_tx.write(0); // Ensure TX is off - arg_c->on_tx_progress(0, true); - chThdExit(0); - - return 0; +static msg_t ookthread_fn(void* arg) { + uint32_t v = 0, delay = 0; + uint8_t* message_symbols = shared_memory.bb_data.tones_data.message; + uint8_t symbol; + MorseView* arg_c = (MorseView*)arg; + + chRegSetThreadName("ookthread"); + + for (uint32_t i = 0; i < arg_c->symbol_count; i++) { + if (chThdShouldTerminate()) break; + + symbol = message_symbols[i]; + + v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause + delay = morse_symbols[symbol]; + + gpio_og_tx.write(v); + arg_c->on_tx_progress(i, false); + + chThdSleepMilliseconds(delay * arg_c->time_unit_ms); + } + + gpio_og_tx.write(0); // Ensure TX is off + arg_c->on_tx_progress(0, true); + chThdExit(0); + + return 0; } -static msg_t loopthread_fn(void * arg) { - MorseView * arg_c = (MorseView*)arg; - uint32_t wait = arg_c->loop; +static msg_t loopthread_fn(void* arg) { + MorseView* arg_c = (MorseView*)arg; + uint32_t wait = arg_c->loop; + + chRegSetThreadName("loopthread"); - chRegSetThreadName("loopthread"); + for (uint32_t i = 0; i < wait; i++) { + if (chThdShouldTerminate()) break; - for (uint32_t i = 0; i < wait; i++) { - if (chThdShouldTerminate()) break; + arg_c->on_loop_progress(i, false); + chThdSleepMilliseconds(1000); + } - arg_c->on_loop_progress(i, false); - chThdSleepMilliseconds(1000); - } + arg_c->on_loop_progress(0, true); + chThdExit(0); - arg_c->on_loop_progress(0, true); - chThdExit(0); - - return 0; + return 0; } void MorseView::on_set_text(NavigationView& nav) { - text_prompt(nav, buffer, 28); + text_prompt(nav, buffer, 28); } void MorseView::focus() { - button_message.focus(); + button_message.focus(); } MorseView::~MorseView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_morse", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_morse", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void MorseView::paint(Painter&) { - message = buffer; - text_message.set(message); - update_tx_duration(); + message = buffer; + text_message.set(message); + update_tx_duration(); } bool MorseView::start_tx() { - // Re-generate message, just in case - update_tx_duration(); - - if (!symbol_count) { - nav_.display_modal("Error", "Message too long,\nmust be < 256 symbols.", INFO, nullptr); - return false; - } - - progressbar.set_max(symbol_count); - - transmitter_model.set_sampling_rate(1536000U); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - if (modulation == CW) { - ookthread = chThdCreateStatic(ookthread_wa, sizeof(ookthread_wa), NORMALPRIO + 10, ookthread_fn, this); - } else if (modulation == FM) { - baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, symbol_count, false, false); - } - - return true; + // Re-generate message, just in case + update_tx_duration(); + + if (!symbol_count) { + nav_.display_modal("Error", "Message too long,\nmust be < 256 symbols.", INFO, nullptr); + return false; + } + + progressbar.set_max(symbol_count); + + transmitter_model.set_sampling_rate(1536000U); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + if (modulation == CW) { + ookthread = chThdCreateStatic(ookthread_wa, sizeof(ookthread_wa), NORMALPRIO + 10, ookthread_fn, this); + } else if (modulation == FM) { + baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, symbol_count, false, false); + } + + return true; } void MorseView::update_tx_duration() { - uint32_t duration_ms; - - time_unit_ms = 1200 / field_speed.value(); - symbol_count = morse_encode(message, time_unit_ms, field_tone.value(), &time_units); - - if (symbol_count) { - duration_ms = time_units * time_unit_ms; - text_tx_duration.set(to_string_dec_uint(duration_ms / 1000) + "." + to_string_dec_uint((duration_ms / 100) % 10, 1) + "s "); - } else { - text_tx_duration.set("-"); // Error - } + uint32_t duration_ms; + + time_unit_ms = 1200 / field_speed.value(); + symbol_count = morse_encode(message, time_unit_ms, field_tone.value(), &time_units); + + if (symbol_count) { + duration_ms = time_units * time_unit_ms; + text_tx_duration.set(to_string_dec_uint(duration_ms / 1000) + "." + to_string_dec_uint((duration_ms / 100) % 10, 1) + "s "); + } else { + text_tx_duration.set("-"); // Error + } } void MorseView::on_tx_progress(const uint32_t progress, const bool done) { - if (done) { - transmitter_model.disable(); - progressbar.set_value(0); - - if (loop && run) { - text_tx_duration.set("wait"); - progressbar.set_max(loop); - progressbar.set_value(0); - - if (loopthread) { - chThdRelease(loopthread); - loopthread = nullptr; - } - - loopthread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO, loopthread_fn, this); - } else { - tx_view.set_transmitting(false); - } - } else - progressbar.set_value(progress); + if (done) { + transmitter_model.disable(); + progressbar.set_value(0); + + if (loop && run) { + text_tx_duration.set("wait"); + progressbar.set_max(loop); + progressbar.set_value(0); + + if (loopthread) { + chThdRelease(loopthread); + loopthread = nullptr; + } + + loopthread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO, loopthread_fn, this); + } else { + tx_view.set_transmitting(false); + } + } else + progressbar.set_value(progress); } void MorseView::on_loop_progress(const uint32_t progress, const bool done) { - if (done) { - start_tx(); - } else - progressbar.set_value(progress); + if (done) { + start_tx(); + } else + progressbar.set_value(progress); } void MorseView::set_foxhunt(size_t i) { - message = foxhunt_codes[i]; - buffer = message.c_str(); - text_message.set(message); - update_tx_duration(); + message = foxhunt_codes[i]; + buffer = message.c_str(); + text_message.set(message); + update_tx_duration(); } MorseView::MorseView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_tones); - - add_children({ - &labels, - &checkbox_foxhunt, - &options_foxhunt, - &field_speed, - &field_tone, - &options_modulation, - &options_loop, - &text_tx_duration, - &text_message, - &button_message, - &progressbar, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_morse", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - // Default settings - field_speed.set_value(15); // 15wps - field_tone.set_value(700); // 700Hz FM tone - options_modulation.set_selected_index(0); // CW mode - options_loop.set_selected_index(0); // Off - - checkbox_foxhunt.on_select = [this](Checkbox&, bool value) { - foxhunt_mode = value; - - if (foxhunt_mode) - set_foxhunt(options_foxhunt.selected_index_value()); - }; - - options_foxhunt.on_change = [this](size_t i, int32_t) { - if (foxhunt_mode) - set_foxhunt(i); - }; - - options_modulation.on_change = [this](size_t i, int32_t) { - modulation = (modulation_t)i; - }; - - options_loop.on_change = [this](size_t i, uint32_t n) { - (void)i; //avoid unused warning - loop = n; - }; - - field_speed.on_change = [this](int32_t) { - update_tx_duration(); - }; - - button_message.on_select = [this, &nav](Button&) { - this->on_set_text(nav); - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (start_tx()) { - run = true; - tx_view.set_transmitting(true); - } - }; - - tx_view.on_stop = [this]() { - run = false; - if (ookthread) chThdTerminate(ookthread); - - if (loopthread) { - chThdTerminate(loopthread); - chThdWait(loopthread); - loopthread = nullptr; - } - - transmitter_model.disable(); - baseband::kill_tone(); - tx_view.set_transmitting(false); - }; + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_tones); + + add_children({&labels, + &checkbox_foxhunt, + &options_foxhunt, + &field_speed, + &field_tone, + &options_modulation, + &options_loop, + &text_tx_duration, + &text_message, + &button_message, + &progressbar, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_morse", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + // Default settings + field_speed.set_value(15); // 15wps + field_tone.set_value(700); // 700Hz FM tone + options_modulation.set_selected_index(0); // CW mode + options_loop.set_selected_index(0); // Off + + checkbox_foxhunt.on_select = [this](Checkbox&, bool value) { + foxhunt_mode = value; + + if (foxhunt_mode) + set_foxhunt(options_foxhunt.selected_index_value()); + }; + + options_foxhunt.on_change = [this](size_t i, int32_t) { + if (foxhunt_mode) + set_foxhunt(i); + }; + + options_modulation.on_change = [this](size_t i, int32_t) { + modulation = (modulation_t)i; + }; + + options_loop.on_change = [this](size_t i, uint32_t n) { + (void)i; // avoid unused warning + loop = n; + }; + + field_speed.on_change = [this](int32_t) { + update_tx_duration(); + }; + + button_message.on_select = [this, &nav](Button&) { + this->on_set_text(nav); + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (start_tx()) { + run = true; + tx_view.set_transmitting(true); + } + }; + + tx_view.on_stop = [this]() { + run = false; + if (ookthread) chThdTerminate(ookthread); + + if (loopthread) { + chThdTerminate(loopthread); + chThdWait(loopthread); + loopthread = nullptr; + } + + transmitter_model.disable(); + baseband::kill_tone(); + tx_view.set_transmitting(false); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_morse.hpp b/firmware/application/apps/ui_morse.hpp index 90b60b499..8999aedd7 100644 --- a/firmware/application/apps/ui_morse.hpp +++ b/firmware/application/apps/ui_morse.hpp @@ -41,156 +41,138 @@ using namespace morse; namespace ui { class MorseView : public View { -public: - MorseView(NavigationView& nav); - ~MorseView(); - - MorseView(const MorseView&) = delete; - MorseView(MorseView&&) = delete; - MorseView& operator=(const MorseView&) = delete; - MorseView& operator=(MorseView&&) = delete; - - void focus() override; - void paint(Painter& painter) override; - - void on_tx_progress(const uint32_t progress, const bool done); - void on_loop_progress(const uint32_t progress, const bool done); - - std::string title() const override { return "Morse TX"; }; - - uint32_t time_unit_ms { 0 }; - size_t symbol_count { 0 }; - uint32_t loop { 0 }; -private: - NavigationView& nav_; - std::string buffer { "PORTAPACK" }; - std::string message { }; - uint32_t time_units { 0 }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - enum modulation_t { - CW = 0, - FM = 1 - }; - modulation_t modulation { CW }; - - bool start_tx(); - void update_tx_duration(); - void on_set_text(NavigationView& nav); - void set_foxhunt(size_t i); - - Thread * ookthread { nullptr }; - Thread * loopthread { nullptr }; - bool foxhunt_mode { false }; - bool run { false }; - - Labels labels { - { { 4 * 8, 6 * 8 }, "Speed: wps", Color::light_grey() }, - { { 4 * 8, 8 * 8 }, "Tone: Hz", Color::light_grey() }, - { { 4 * 8, 10 * 8 }, "Modulation:", Color::light_grey() }, - { { 4 * 8, 12 * 8 }, "Loop:", Color::light_grey() }, - { { 1 * 8, 25 * 8 }, "TX will last", Color::light_grey() } - }; - - Checkbox checkbox_foxhunt { - { 4 * 8, 16 }, - 8, - "Foxhunt:" - }; - OptionsField options_foxhunt { - { 17 * 8, 16 + 4 }, - 7, - { - { "1 (MOE)", 0 }, - { "2 (MOI)", 1 }, - { "3 (MOS)", 2 }, - { "4 (MOH)", 3 }, - { "5 (MO5)", 4 }, - { "6 (MON)", 5 }, - { "7 (MOD)", 6 }, - { "8 (MOB)", 7 }, - { "9 (MO6)", 8 }, - { "X (MO) ", 9 }, - { "T (S) ", 10 } - } - }; - - NumberField field_speed { - { 10 * 8, 6 * 8 }, - 3, - { 10, 45 }, - 1, - ' ' - }; - - NumberField field_tone { - { 9 * 8, 8 * 8 }, - 4, - { 100, 9999 }, - 20, - ' ' - }; - - OptionsField options_modulation { - { 15 * 8, 10 * 8 }, - 2, - { - { "CW", 0 }, - { "FM", 1 } - } - }; - - OptionsField options_loop { - { 9 * 8, 12 * 8 }, - 6, - { - { "Off", 0 }, - { "5 sec", 5 }, - { "10 sec", 10 }, - { "30 sec", 30 }, - { "1 min", 60 }, - { "3 min", 180 }, - { "5 min", 300 } - } - }; - - Text text_tx_duration { - { 14 * 8, 25 * 8, 4 * 8, 16 }, - "-" - }; - - Text text_message { - { 1 * 8, 15 * 8, 28 * 8, 16 }, - "" - }; - - Button button_message { - { 1 * 8, 17 * 8, 12 * 8, 28 }, - "Set message" - }; - - ProgressBar progressbar { - { 2 * 8, 28 * 8, 208, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + MorseView(NavigationView& nav); + ~MorseView(); + + MorseView(const MorseView&) = delete; + MorseView(MorseView&&) = delete; + MorseView& operator=(const MorseView&) = delete; + MorseView& operator=(MorseView&&) = delete; + + void focus() override; + void paint(Painter& painter) override; + + void on_tx_progress(const uint32_t progress, const bool done); + void on_loop_progress(const uint32_t progress, const bool done); + + std::string title() const override { return "Morse TX"; }; + + uint32_t time_unit_ms{0}; + size_t symbol_count{0}; + uint32_t loop{0}; + + private: + NavigationView& nav_; + std::string buffer{"PORTAPACK"}; + std::string message{}; + uint32_t time_units{0}; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + enum modulation_t { + CW = 0, + FM = 1 + }; + modulation_t modulation{CW}; + + bool start_tx(); + void update_tx_duration(); + void on_set_text(NavigationView& nav); + void set_foxhunt(size_t i); + + Thread* ookthread{nullptr}; + Thread* loopthread{nullptr}; + bool foxhunt_mode{false}; + bool run{false}; + + Labels labels{ + {{4 * 8, 6 * 8}, "Speed: wps", Color::light_grey()}, + {{4 * 8, 8 * 8}, "Tone: Hz", Color::light_grey()}, + {{4 * 8, 10 * 8}, "Modulation:", Color::light_grey()}, + {{4 * 8, 12 * 8}, "Loop:", Color::light_grey()}, + {{1 * 8, 25 * 8}, "TX will last", Color::light_grey()}}; + + Checkbox checkbox_foxhunt{ + {4 * 8, 16}, + 8, + "Foxhunt:"}; + OptionsField options_foxhunt{ + {17 * 8, 16 + 4}, + 7, + {{"1 (MOE)", 0}, + {"2 (MOI)", 1}, + {"3 (MOS)", 2}, + {"4 (MOH)", 3}, + {"5 (MO5)", 4}, + {"6 (MON)", 5}, + {"7 (MOD)", 6}, + {"8 (MOB)", 7}, + {"9 (MO6)", 8}, + {"X (MO) ", 9}, + {"T (S) ", 10}}}; + + NumberField field_speed{ + {10 * 8, 6 * 8}, + 3, + {10, 45}, + 1, + ' '}; + + NumberField field_tone{ + {9 * 8, 8 * 8}, + 4, + {100, 9999}, + 20, + ' '}; + + OptionsField options_modulation{ + {15 * 8, 10 * 8}, + 2, + {{"CW", 0}, + {"FM", 1}}}; + + OptionsField options_loop{ + {9 * 8, 12 * 8}, + 6, + {{"Off", 0}, + {"5 sec", 5}, + {"10 sec", 10}, + {"30 sec", 30}, + {"1 min", 60}, + {"3 min", 180}, + {"5 min", 300}}}; + + Text text_tx_duration{ + {14 * 8, 25 * 8, 4 * 8, 16}, + "-"}; + + Text text_message{ + {1 * 8, 15 * 8, 28 * 8, 16}, + ""}; + + Button button_message{ + {1 * 8, 17 * 8, 12 * 8, 28}, + "Set message"}; + + ProgressBar progressbar{ + {2 * 8, 28 * 8, 208, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ -#endif/*__MORSE_TX_H__*/ +#endif /*__MORSE_TX_H__*/ diff --git a/firmware/application/apps/ui_nrf_rx.cpp b/firmware/application/apps/ui_nrf_rx.cpp index 3af8d1d1f..0d0753fb7 100644 --- a/firmware/application/apps/ui_nrf_rx.cpp +++ b/firmware/application/apps/ui_nrf_rx.cpp @@ -37,134 +37,127 @@ using namespace modems; namespace ui { void NRFRxView::focus() { - field_frequency.focus(); + field_frequency.focus(); } void NRFRxView::update_freq(rf::Frequency f) { - receiver_model.set_tuning_frequency(f); + receiver_model.set_tuning_frequency(f); } NRFRxView::NRFRxView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_nrf_rx); - - add_children({ - &rssi, - &channel, - &field_rf_amp, - &field_lna, - &field_vga, - &field_frequency, - &button_modem_setup, - &console - }); - - // load app settings - auto rc = settings.load("rx_nrf", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - } - - // Auto-configure modem for LCR RX (will be removed later) - update_freq(2480000000); - auto def_bell202 = &modem_defs[0]; - persistent_memory::set_modem_baudrate(def_bell202->baudrate); - serial_format_t serial_format; - serial_format.data_bits = 7; - serial_format.parity = EVEN; - serial_format.stop_bits = 1; - serial_format.bit_order = LSB_FIRST; - persistent_memory::set_serial_format(serial_format); - - field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(100); - field_frequency.on_change = [this](rf::Frequency f) { - update_freq(f); - }; - field_frequency.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - update_freq(f); - field_frequency.set_value(f); - }; - }; - - button_modem_setup.on_select = [&nav](Button&) { - nav.push(); - }; - - - // Auto-configure modem for LCR RX (will be removed later) - baseband::set_nrf(persistent_memory::modem_baudrate(), 8, 0, false); - - audio::set_rate(audio::Rate::Hz_24000); - audio::output::start(); - - receiver_model.set_sampling_rate(4000000); - receiver_model.set_baseband_bandwidth(4000000); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - receiver_model.enable(); + baseband::run_image(portapack::spi_flash::image_tag_nrf_rx); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &button_modem_setup, + &console}); + + // load app settings + auto rc = settings.load("rx_nrf", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + } + + // Auto-configure modem for LCR RX (will be removed later) + update_freq(2480000000); + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); + }; + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_nrf(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(4000000); + receiver_model.set_baseband_bandwidth(4000000); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.enable(); } void NRFRxView::on_data(uint32_t value, bool is_data) { - //std::string str_console = "\x1B"; - std::string str_console = ""; - if (is_data) { - // Colorize differently after message splits - //str_console += (char)((console_color & 3) + 9); - - //value &= 0xFF; // ABCDEFGH - //value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD - //value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB - //value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA - //value &= 0x7F; // Ignore parity, which is the MSB now - - //if ((value >= 32) && (value < 127)) { - // str_console += (char)value; // Printable - //} - - //str_console += (char)'A'; - //str_console += (char)value; - //str_console += "[" + to_string_hex(value, 2) + "]"; - str_console += " " + to_string_hex(value, 2) ; - console.write(str_console); - - - - /*if ((value != 0x7F) && (prev_value == 0x7F)) { - // Message split - console.writeln(""); - console_color++; - - - }*/ - //prev_value = value; - } else { - // Baudrate estimation - //text_debug.set("~" + to_string_dec_uint(value)); - if (value == 'A') - {console.write("addr:");} - else if (value == 'B') - {console.write(" data:");} - else if (value == 'C') - { - console.writeln(""); - console.writeln(""); - } - //console.writeln(""); - } + // std::string str_console = "\x1B"; + std::string str_console = ""; + if (is_data) { + // Colorize differently after message splits + // str_console += (char)((console_color & 3) + 9); + + // value &= 0xFF; // ABCDEFGH + // value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + // value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + // value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + // value &= 0x7F; // Ignore parity, which is the MSB now + + // if ((value >= 32) && (value < 127)) { + // str_console += (char)value; // Printable + // } + + // str_console += (char)'A'; + // str_console += (char)value; + // str_console += "[" + to_string_hex(value, 2) + "]"; + str_console += " " + to_string_hex(value, 2); + console.write(str_console); + + /*if ((value != 0x7F) && (prev_value == 0x7F)) { + // Message split + console.writeln(""); + console_color++; + + + }*/ + // prev_value = value; + } else { + // Baudrate estimation + // text_debug.set("~" + to_string_dec_uint(value)); + if (value == 'A') { + console.write("addr:"); + } else if (value == 'B') { + console.write(" data:"); + } else if (value == 'C') { + console.writeln(""); + console.writeln(""); + } + // console.writeln(""); + } } NRFRxView::~NRFRxView() { + // save app settings + app_settings.rx_frequency = field_frequency.value(); + settings.save("rx_nrf", &app_settings); - // save app settings - app_settings.rx_frequency = field_frequency.value(); - settings.save("rx_nrf", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_nrf_rx.hpp b/firmware/application/apps/ui_nrf_rx.hpp index fdf049770..c2a1de130 100644 --- a/firmware/application/apps/ui_nrf_rx.hpp +++ b/firmware/application/apps/ui_nrf_rx.hpp @@ -28,72 +28,66 @@ #include "ui_navigation.hpp" #include "ui_receiver.hpp" #include "app_settings.hpp" -#include "ui_record_view.hpp" // DEBUG +#include "ui_record_view.hpp" // DEBUG #include "utility.hpp" namespace ui { class NRFRxView : public View { -public: - NRFRxView(NavigationView& nav); - ~NRFRxView(); - - void focus() override; - - std::string title() const override { return "NRF RX"; }; - -private: - void on_data(uint32_t value, bool is_data); - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - uint8_t console_color { 0 }; - uint32_t prev_value { 0 }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - Channel channel { - { 21 * 8, 5, 6 * 8, 4 }, - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 16 }, - }; - - Button button_modem_setup { - { 240 - 12 * 8, 1 * 16, 96, 24 }, - "Modem setup" - }; - - Console console { - { 0, 4 * 16, 240, 240 } - }; - - void update_freq(rf::Frequency f); - //void on_data_afsk(const AFSKDataMessage& message); - - MessageHandlerRegistration message_handler_packet { - Message::ID::AFSKData, - [this](Message* const p) { - const auto message = static_cast(p); - this->on_data(message->value, message->is_data); - } - }; + public: + NRFRxView(NavigationView& nav); + ~NRFRxView(); + + void focus() override; + + std::string title() const override { return "NRF RX"; }; + + private: + void on_data(uint32_t value, bool is_data); + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + uint8_t console_color{0}; + uint32_t prev_value{0}; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + Channel channel{ + {21 * 8, 5, 6 * 8, 4}, + }; + + FrequencyField field_frequency{ + {0 * 8, 0 * 16}, + }; + + Button button_modem_setup{ + {240 - 12 * 8, 1 * 16, 96, 24}, + "Modem setup"}; + + Console console{ + {0, 4 * 16, 240, 240}}; + + void update_freq(rf::Frequency f); + // void on_data_afsk(const AFSKDataMessage& message); + + MessageHandlerRegistration message_handler_packet{ + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + }}; }; } /* namespace ui */ -#endif/*__UI_NRF_RX_H__*/ +#endif /*__UI_NRF_RX_H__*/ diff --git a/firmware/application/apps/ui_numbers.cpp b/firmware/application/apps/ui_numbers.cpp index 616f9907f..f57b9b79d 100644 --- a/firmware/application/apps/ui_numbers.cpp +++ b/firmware/application/apps/ui_numbers.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -37,269 +37,265 @@ namespace ui { // TODO: This app takes way too much space, find a way to shrink/simplify or make it an SD card module (loadable) void NumbersStationView::focus() { - if (file_error) - nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT, nullptr); - else - button_exit.focus(); + if (file_error) + nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT, nullptr); + else + button_exit.focus(); } NumbersStationView::~NumbersStationView() { - transmitter_model.disable(); - baseband::shutdown(); + transmitter_model.disable(); + baseband::shutdown(); } -NumbersStationView::wav_file_t * NumbersStationView::get_wav(uint32_t index) { - return ¤t_voice->available_wavs[index]; +NumbersStationView::wav_file_t* NumbersStationView::get_wav(uint32_t index) { + return ¤t_voice->available_wavs[index]; } void NumbersStationView::prepare_audio() { - uint8_t code; - wav_file_t * wav_file; - - if (sample_counter >= sample_length) { - if (segment == ANNOUNCE) { - if (!announce_loop) { - code_index = 0; - segment = MESSAGE; - } else { - wav_file = get_wav(11); - reader->open(current_voice->dir + file_names[wav_file->index].name + ".wav"); - sample_length = wav_file->length; - announce_loop--; - } - } - - if (segment == MESSAGE) { - if (code_index == 25) { - transmitter_model.disable(); - return; - } - - code = symfield_code.get_sym(code_index); - - if (code >= 10) { - memset(audio_buffer, 0, 1024); - if (code == 10) { - pause = 11025; // p: 0.25s @ 44100Hz - } else if (code == 11) { - pause = 33075; // P: 0.75s @ 44100Hz - } else if (code == 12) { - transmitter_model.disable(); - return; - } - } else { - wav_file = get_wav(code); - reader->open(current_voice->dir + file_names[code].name + ".wav"); - sample_length = wav_file->length; - } - code_index++; - } - sample_counter = 0; - } - - if (!pause) { - auto bytes_read = reader->read(audio_buffer, 1024).value(); - - // Unsigned to signed, pretty stupid :/ - for (size_t n = 0; n < bytes_read; n++) - audio_buffer[n] -= 0x80; - for (size_t n = bytes_read; n < 1024; n++) - audio_buffer[n] = 0; - - sample_counter += 1024; - } else { - if (pause >= 1024) { - pause -= 1024; - } else { - sample_counter = sample_length; - pause = 0; - } - } - - baseband::set_fifo_data(audio_buffer); + uint8_t code; + wav_file_t* wav_file; + + if (sample_counter >= sample_length) { + if (segment == ANNOUNCE) { + if (!announce_loop) { + code_index = 0; + segment = MESSAGE; + } else { + wav_file = get_wav(11); + reader->open(current_voice->dir + file_names[wav_file->index].name + ".wav"); + sample_length = wav_file->length; + announce_loop--; + } + } + + if (segment == MESSAGE) { + if (code_index == 25) { + transmitter_model.disable(); + return; + } + + code = symfield_code.get_sym(code_index); + + if (code >= 10) { + memset(audio_buffer, 0, 1024); + if (code == 10) { + pause = 11025; // p: 0.25s @ 44100Hz + } else if (code == 11) { + pause = 33075; // P: 0.75s @ 44100Hz + } else if (code == 12) { + transmitter_model.disable(); + return; + } + } else { + wav_file = get_wav(code); + reader->open(current_voice->dir + file_names[code].name + ".wav"); + sample_length = wav_file->length; + } + code_index++; + } + sample_counter = 0; + } + + if (!pause) { + auto bytes_read = reader->read(audio_buffer, 1024).value(); + + // Unsigned to signed, pretty stupid :/ + for (size_t n = 0; n < bytes_read; n++) + audio_buffer[n] -= 0x80; + for (size_t n = bytes_read; n < 1024; n++) + audio_buffer[n] = 0; + + sample_counter += 1024; + } else { + if (pause >= 1024) { + pause -= 1024; + } else { + sample_counter = sample_length; + pause = 0; + } + } + + baseband::set_fifo_data(audio_buffer); } void NumbersStationView::start_tx() { - //sample_length = sound_sizes[10]; // Announce - sample_counter = sample_length; - - code_index = 0; - announce_loop = 2; - segment = ANNOUNCE; - - prepare_audio(); - - transmitter_model.set_sampling_rate(1536000U); - transmitter_model.set_rf_amp(true); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_audiotx_data( - (1536000 / 44100) - 1, // TODO: Read wav file's samplerate - 12000, - 1, - false, - 0 - ); + // sample_length = sound_sizes[10]; // Announce + sample_counter = sample_length; + + code_index = 0; + announce_loop = 2; + segment = ANNOUNCE; + + prepare_audio(); + + transmitter_model.set_sampling_rate(1536000U); + transmitter_model.set_rf_amp(true); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_audiotx_data( + (1536000 / 44100) - 1, // TODO: Read wav file's samplerate + 12000, + 1, + false, + 0); } void NumbersStationView::on_tick_second() { - armed_blink = not armed_blink; - - if (armed_blink) - check_armed.set_style(&style_red); - else - check_armed.set_style(&style()); - - check_armed.set_dirty(); + armed_blink = not armed_blink; + + if (armed_blink) + check_armed.set_style(&style_red); + else + check_armed.set_style(&style()); + + check_armed.set_dirty(); } void NumbersStationView::on_voice_changed(size_t index) { - std::string code_list = ""; - - for (const auto& wavs : voices[index].available_wavs) - code_list += wavs.code; - - for (uint32_t c = 0; c < 25; c++) - symfield_code.set_symbol_list(c, code_list); - - current_voice = &voices[index]; + std::string code_list = ""; + + for (const auto& wavs : voices[index].available_wavs) + code_list += wavs.code; + + for (uint32_t c = 0; c < 25; c++) + symfield_code.set_symbol_list(c, code_list); + + current_voice = &voices[index]; } bool NumbersStationView::check_wav_validity(const std::string dir, const std::string file) { - if (reader->open("/numbers/" + dir + "/" + file)) { - // Check format (mono, 8 bits) - if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) - return true; - else - return false; - } else - return false; + if (reader->open("/numbers/" + dir + "/" + file)) { + // Check format (mono, 8 bits) + if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) + return true; + else + return false; + } else + return false; } NumbersStationView::NumbersStationView( - NavigationView& nav -) : nav_ (nav) -{ - std::vector directory_list; - using option_t = std::pair; - using options_t = std::vector; - options_t voice_options; - voice_t temp_voice { }; - bool valid; - uint32_t c; - //uint8_t y, m, d, dayofweek; - - reader = std::make_unique(); - - // Search for valid voice directories - directory_list = scan_root_directories("/numbers"); - if (!directory_list.size()) { - file_error = true; - return; - } - - for (const auto& dir : directory_list) { - c = 0; - for (const auto& file_name : file_names) { - valid = check_wav_validity(dir.string(), file_name.name + ".wav"); - if ((!valid) && (file_name.required)) { - temp_voice.available_wavs.clear(); - break; // Invalid required file means invalid voice - } else if (valid) { - temp_voice.available_wavs.push_back({ file_name.code, c++, 0, 0 }); // TODO: Needs length and samplerate - } - } - if (!temp_voice.available_wavs.empty()) { - // Voice can be used, are there accent files ? - c = 0; - for (const auto& file_name : file_names) { - valid = check_wav_validity(dir.string(), file_name.name + "a.wav"); - if ((!valid) && (file_name.required)) { - c = 0; - break; // Invalid required file means accents can't be used - } else if (valid) { - c++; - } - } - - temp_voice.accent = c ? true : false; - temp_voice.dir = dir.string(); - - voices.push_back(temp_voice); - } - } - - if (voices.empty()) { - file_error = true; - return; - } - - baseband::run_image(portapack::spi_flash::image_tag_audio_tx); - - add_children({ - &labels, - &symfield_code, - &check_armed, - &options_voices, - &text_voice_flags, - //&button_tx_now, - &button_exit - }); - - for (const auto& voice : voices) - voice_options.emplace_back(voice.dir.substr(0, 4), 0); - - options_voices.set_options(voice_options); - options_voices.on_change = [this](size_t i, int32_t) { - this->on_voice_changed(i); - }; - options_voices.set_selected_index(0); - - check_armed.set_value(false); - - check_armed.on_select = [this](Checkbox&, bool v) { - if (v) { - armed_blink = false; - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; - } else { - check_armed.set_style(&style()); - rtc_time::signal_tick_second -= signal_token_tick_second; - } - }; - - // DEBUG - symfield_code.set_sym(0, 10); - symfield_code.set_sym(1, 3); - symfield_code.set_sym(2, 4); - symfield_code.set_sym(3, 11); - symfield_code.set_sym(4, 6); - symfield_code.set_sym(5, 1); - symfield_code.set_sym(6, 9); - symfield_code.set_sym(7, 7); - symfield_code.set_sym(8, 8); - symfield_code.set_sym(9, 0); - symfield_code.set_sym(10, 12); // End + NavigationView& nav) + : nav_(nav) { + std::vector directory_list; + using option_t = std::pair; + using options_t = std::vector; + options_t voice_options; + voice_t temp_voice{}; + bool valid; + uint32_t c; + // uint8_t y, m, d, dayofweek; -/* - rtc::RTC datetime; - rtcGetTime(&RTCD1, &datetime); - - // Thanks, Sakamoto-sama ! - y = datetime.year(); - m = datetime.month(); - d = datetime.day(); - y -= m < 3; - dayofweek = (y + y/4 - y/100 + y/400 + month_table[m-1] + d) % 7; - - text_title.set(day_of_week[dayofweek]); + reader = std::make_unique(); + + // Search for valid voice directories + directory_list = scan_root_directories("/numbers"); + if (!directory_list.size()) { + file_error = true; + return; + } + + for (const auto& dir : directory_list) { + c = 0; + for (const auto& file_name : file_names) { + valid = check_wav_validity(dir.string(), file_name.name + ".wav"); + if ((!valid) && (file_name.required)) { + temp_voice.available_wavs.clear(); + break; // Invalid required file means invalid voice + } else if (valid) { + temp_voice.available_wavs.push_back({file_name.code, c++, 0, 0}); // TODO: Needs length and samplerate + } + } + if (!temp_voice.available_wavs.empty()) { + // Voice can be used, are there accent files ? + c = 0; + for (const auto& file_name : file_names) { + valid = check_wav_validity(dir.string(), file_name.name + "a.wav"); + if ((!valid) && (file_name.required)) { + c = 0; + break; // Invalid required file means accents can't be used + } else if (valid) { + c++; + } + } + + temp_voice.accent = c ? true : false; + temp_voice.dir = dir.string(); + + voices.push_back(temp_voice); + } + } + + if (voices.empty()) { + file_error = true; + return; + } + + baseband::run_image(portapack::spi_flash::image_tag_audio_tx); + + add_children({&labels, + &symfield_code, + &check_armed, + &options_voices, + &text_voice_flags, + //&button_tx_now, + &button_exit}); + + for (const auto& voice : voices) + voice_options.emplace_back(voice.dir.substr(0, 4), 0); + + options_voices.set_options(voice_options); + options_voices.on_change = [this](size_t i, int32_t) { + this->on_voice_changed(i); + }; + options_voices.set_selected_index(0); + + check_armed.set_value(false); + + check_armed.on_select = [this](Checkbox&, bool v) { + if (v) { + armed_blink = false; + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; + } else { + check_armed.set_style(&style()); + rtc_time::signal_tick_second -= signal_token_tick_second; + } + }; + + // DEBUG + symfield_code.set_sym(0, 10); + symfield_code.set_sym(1, 3); + symfield_code.set_sym(2, 4); + symfield_code.set_sym(3, 11); + symfield_code.set_sym(4, 6); + symfield_code.set_sym(5, 1); + symfield_code.set_sym(6, 9); + symfield_code.set_sym(7, 7); + symfield_code.set_sym(8, 8); + symfield_code.set_sym(9, 0); + symfield_code.set_sym(10, 12); // End + + /* + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + + // Thanks, Sakamoto-sama ! + y = datetime.year(); + m = datetime.month(); + d = datetime.day(); + y -= m < 3; + dayofweek = (y + y/4 - y/100 + y/400 + month_table[m-1] + d) % 7; + + text_title.set(day_of_week[dayofweek]); */ - button_exit.on_select = [&nav](Button&){ - nav.pop(); - }; + button_exit.on_select = [&nav](Button&) { + nav.pop(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_numbers.hpp b/firmware/application/apps/ui_numbers.hpp index 3dcc05b2e..39982a232 100644 --- a/firmware/application/apps/ui_numbers.hpp +++ b/firmware/application/apps/ui_numbers.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -39,142 +39,133 @@ namespace ui { class NumbersStationView : public View { -public: - NumbersStationView(NavigationView& nav); - ~NumbersStationView(); - - NumbersStationView(const NumbersStationView&) = delete; - NumbersStationView(NumbersStationView&&) = delete; - NumbersStationView& operator=(const NumbersStationView&) = delete; - NumbersStationView& operator=(NumbersStationView&&) = delete; - - void focus() override; - - std::string title() const override { return "Station"; }; - -private: - NavigationView& nav_; - - // Sequencing state machine - enum segments { - IDLE = 0, - ANNOUNCE, - MESSAGE, - SIGNOFF - }; - - Style style_red { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red() - }; - - typedef struct { - char code; - uint32_t index; - uint32_t length; - uint32_t samplerate; - } wav_file_t; - - struct voice_t { - std::string dir; - std::vector available_wavs; - bool accent; - }; - - std::vector voices { }; - voice_t * current_voice { }; - - struct wav_file_list_t { - std::string name; - bool required; - char code; - }; - - const std::vector file_names = { - { "0", true, '0' }, - { "1", true, '1' }, - { "2", true, '2' }, - { "3", true, '3' }, - { "4", true, '4' }, - { "5", true, '5' }, - { "6", true, '6' }, - { "7", true, '7' }, - { "8", true, '8' }, - { "9", true, '9' }, - { "announce", false, 'A' } - }; - - segments segment { IDLE }; - bool armed { false }; - bool file_error { false }; - - // const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; - // const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; - - std::unique_ptr reader { }; - - uint8_t code_index { 0 }, announce_loop { 0 }; - uint32_t sample_counter { 0 }; - uint32_t sample_length { 0 }; - int8_t audio_buffer[1024] { }; - uint32_t pause { 0 }; - bool armed_blink { false }; - SignalToken signal_token_tick_second { }; - - wav_file_t * get_wav(uint32_t index); - bool check_wav_validity(const std::string dir, const std::string file); - void on_voice_changed(size_t index); - void on_tick_second(); - void prepare_audio(); - void start_tx(); - - Labels labels { - { { 2 * 8, 5 * 8 }, "Voice: Flags:", Color::light_grey() }, - { { 1 * 8, 8 * 8 }, "Code:", Color::light_grey() } - }; - - OptionsField options_voices { - { 8 * 8, 1 * 8 }, - 4, - { } - }; - Text text_voice_flags { - { 19 * 8, 1 * 8, 2 * 8, 16 }, - "" - }; - - SymField symfield_code { - { 1 * 8, 10 * 8 }, - 25, - SymField::SYMFIELD_DEF - }; - - Checkbox check_armed { - { 2 * 8, 13 * 16 }, - 5, - "Armed" - }; - /*Button button_tx_now { - { 18 * 8, 13 * 16, 10 * 8, 32 }, - "TX now" - };*/ - Button button_exit { - { 21 * 8, 16 * 16, 64, 32 }, - "Exit" - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->prepare_audio(); - } - } - }; + public: + NumbersStationView(NavigationView& nav); + ~NumbersStationView(); + + NumbersStationView(const NumbersStationView&) = delete; + NumbersStationView(NumbersStationView&&) = delete; + NumbersStationView& operator=(const NumbersStationView&) = delete; + NumbersStationView& operator=(NumbersStationView&&) = delete; + + void focus() override; + + std::string title() const override { return "Station"; }; + + private: + NavigationView& nav_; + + // Sequencing state machine + enum segments { + IDLE = 0, + ANNOUNCE, + MESSAGE, + SIGNOFF + }; + + Style style_red{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red()}; + + typedef struct { + char code; + uint32_t index; + uint32_t length; + uint32_t samplerate; + } wav_file_t; + + struct voice_t { + std::string dir; + std::vector available_wavs; + bool accent; + }; + + std::vector voices{}; + voice_t* current_voice{}; + + struct wav_file_list_t { + std::string name; + bool required; + char code; + }; + + const std::vector file_names = { + {"0", true, '0'}, + {"1", true, '1'}, + {"2", true, '2'}, + {"3", true, '3'}, + {"4", true, '4'}, + {"5", true, '5'}, + {"6", true, '6'}, + {"7", true, '7'}, + {"8", true, '8'}, + {"9", true, '9'}, + {"announce", false, 'A'}}; + + segments segment{IDLE}; + bool armed{false}; + bool file_error{false}; + + // const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; + // const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; + + std::unique_ptr reader{}; + + uint8_t code_index{0}, announce_loop{0}; + uint32_t sample_counter{0}; + uint32_t sample_length{0}; + int8_t audio_buffer[1024]{}; + uint32_t pause{0}; + bool armed_blink{false}; + SignalToken signal_token_tick_second{}; + + wav_file_t* get_wav(uint32_t index); + bool check_wav_validity(const std::string dir, const std::string file); + void on_voice_changed(size_t index); + void on_tick_second(); + void prepare_audio(); + void start_tx(); + + Labels labels{ + {{2 * 8, 5 * 8}, "Voice: Flags:", Color::light_grey()}, + {{1 * 8, 8 * 8}, "Code:", Color::light_grey()}}; + + OptionsField options_voices{ + {8 * 8, 1 * 8}, + 4, + {}}; + Text text_voice_flags{ + {19 * 8, 1 * 8, 2 * 8, 16}, + ""}; + + SymField symfield_code{ + {1 * 8, 10 * 8}, + 25, + SymField::SYMFIELD_DEF}; + + Checkbox check_armed{ + {2 * 8, 13 * 16}, + 5, + "Armed"}; + /*Button button_tx_now { + { 18 * 8, 13 * 16, 10 * 8, 32 }, + "TX now" + };*/ + Button button_exit{ + {21 * 8, 16 * 16, 64, 32}, + "Exit"}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->prepare_audio(); + } + }}; }; } /* namespace ui */ -#endif/*__UI_NUMBERS_H__*/ +#endif /*__UI_NUMBERS_H__*/ diff --git a/firmware/application/apps/ui_nuoptix.cpp b/firmware/application/apps/ui_nuoptix.cpp index 541d5a264..6378a9433 100644 --- a/firmware/application/apps/ui_nuoptix.cpp +++ b/firmware/application/apps/ui_nuoptix.cpp @@ -37,156 +37,152 @@ using namespace portapack; namespace ui { void NuoptixView::focus() { - number_timecode.focus(); + number_timecode.focus(); } NuoptixView::~NuoptixView() { - transmitter_model.disable(); - baseband::shutdown(); + transmitter_model.disable(); + baseband::shutdown(); } void NuoptixView::on_tx_progress(const uint32_t progress, const bool done) { - if (done) - transmit(false); - else - progressbar.set_value(progress); + if (done) + transmit(false); + else + progressbar.set_value(progress); } void NuoptixView::transmit(bool setup) { - uint8_t mod, tone_code; - uint8_t c; - uint8_t dtmf_message[6]; - rtc::RTC datetime; - - if (!tx_mode) { - transmitter_model.disable(); - return; - } - - if (tx_mode == IMPROVISE) - timecode = lfsr_iterate(timecode) % 1999; // Could be 9999 but that would be one long audio track ! - - if (setup) { - progressbar.set_max(6 * 2); - - if (tx_mode == IMPROVISE) { - // Seed from RTC - rtcGetTime(&RTCD1, &datetime); - timecode = datetime.day() + datetime.second(); - } else { - timecode = number_timecode.value(); - } - - transmitter_model.set_sampling_rate(1536000U); - transmitter_model.set_rf_amp(true); - transmitter_model.set_lna(40); - transmitter_model.set_vga(40); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - dtmf_message[0] = '*'; // "Pre-tone for restart" method #1 - dtmf_message[1] = 'A'; // "Restart" method #1 - } else { - dtmf_message[0] = '#'; - dtmf_message[1] = (timecode / 1000) % 10; - chThdSleepMilliseconds(92); // 141-49ms - number_timecode.set_value(timecode); - } - - progressbar.set_value(0); - - dtmf_message[2] = (timecode / 100) % 10; - dtmf_message[3] = (timecode / 10) % 10; - dtmf_message[4] = timecode % 10; - - mod = 0; - for (c = 1; c < 5; c++) - if (dtmf_message[c] <= 9) - mod += dtmf_message[c]; - - mod = 10 - (mod % 10); - if (mod == 10) mod = 0; // Is this right ? - - text_mod.set("Mod: " + to_string_dec_uint(mod)); - - dtmf_message[5] = mod; - - for (c = 0; c < 6; c++) { - tone_code = dtmf_message[c]; - - if (tone_code == 'A') - tone_code = 10; - else if (tone_code == 'B') - tone_code = 11; - else if (tone_code == 'C') - tone_code = 12; - else if (tone_code == 'D') - tone_code = 13; - else if (tone_code == '#') - tone_code = 14; - else if (tone_code == '*') - tone_code = 15; - - shared_memory.bb_data.tones_data.message[c * 2] = tone_code; - shared_memory.bb_data.tones_data.message[c * 2 + 1] = 0xFF; // Silence - } - - for (c = 0; c < 16; c++) { - baseband::set_tone(c * 2, dtmf_deltas[c][0], NUOPTIX_TONE_LENGTH); - baseband::set_tone(c * 2 + 1, dtmf_deltas[c][1], NUOPTIX_TONE_LENGTH); - } - shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space - - audio::set_rate(audio::Rate::Hz_24000); - baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, 6 * 2, true, true); - - timecode++; + uint8_t mod, tone_code; + uint8_t c; + uint8_t dtmf_message[6]; + rtc::RTC datetime; + + if (!tx_mode) { + transmitter_model.disable(); + return; + } + + if (tx_mode == IMPROVISE) + timecode = lfsr_iterate(timecode) % 1999; // Could be 9999 but that would be one long audio track ! + + if (setup) { + progressbar.set_max(6 * 2); + + if (tx_mode == IMPROVISE) { + // Seed from RTC + rtcGetTime(&RTCD1, &datetime); + timecode = datetime.day() + datetime.second(); + } else { + timecode = number_timecode.value(); + } + + transmitter_model.set_sampling_rate(1536000U); + transmitter_model.set_rf_amp(true); + transmitter_model.set_lna(40); + transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + dtmf_message[0] = '*'; // "Pre-tone for restart" method #1 + dtmf_message[1] = 'A'; // "Restart" method #1 + } else { + dtmf_message[0] = '#'; + dtmf_message[1] = (timecode / 1000) % 10; + chThdSleepMilliseconds(92); // 141-49ms + number_timecode.set_value(timecode); + } + + progressbar.set_value(0); + + dtmf_message[2] = (timecode / 100) % 10; + dtmf_message[3] = (timecode / 10) % 10; + dtmf_message[4] = timecode % 10; + + mod = 0; + for (c = 1; c < 5; c++) + if (dtmf_message[c] <= 9) + mod += dtmf_message[c]; + + mod = 10 - (mod % 10); + if (mod == 10) mod = 0; // Is this right ? + + text_mod.set("Mod: " + to_string_dec_uint(mod)); + + dtmf_message[5] = mod; + + for (c = 0; c < 6; c++) { + tone_code = dtmf_message[c]; + + if (tone_code == 'A') + tone_code = 10; + else if (tone_code == 'B') + tone_code = 11; + else if (tone_code == 'C') + tone_code = 12; + else if (tone_code == 'D') + tone_code = 13; + else if (tone_code == '#') + tone_code = 14; + else if (tone_code == '*') + tone_code = 15; + + shared_memory.bb_data.tones_data.message[c * 2] = tone_code; + shared_memory.bb_data.tones_data.message[c * 2 + 1] = 0xFF; // Silence + } + + for (c = 0; c < 16; c++) { + baseband::set_tone(c * 2, dtmf_deltas[c][0], NUOPTIX_TONE_LENGTH); + baseband::set_tone(c * 2 + 1, dtmf_deltas[c][1], NUOPTIX_TONE_LENGTH); + } + shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space + + audio::set_rate(audio::Rate::Hz_24000); + baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, 6 * 2, true, true); + + timecode++; } NuoptixView::NuoptixView( - NavigationView& nav -) -{ - baseband::run_image(portapack::spi_flash::image_tag_tones); - - add_children({ - &number_timecode, - &text_timecode, - &text_mod, - &progressbar, - &tx_view - }); - - number_timecode.set_value(1); - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - tx_view.set_transmitting(true); - tx_mode = NORMAL; - transmit(true); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - tx_mode = IDLE; - }; - - /*button_impro.on_select = [this](Button&){ - if (tx_mode == IMPROVISE) { - tx_mode = IDLE; - button_impro.set_text("IMPROVISE"); - } else if (tx_mode == IDLE) { - tx_mode = IMPROVISE; - button_impro.set_text("STOP"); - transmit(true); - } - };*/ -} + NavigationView& nav) { + baseband::run_image(portapack::spi_flash::image_tag_tones); + add_children({&number_timecode, + &text_timecode, + &text_mod, + &progressbar, + &tx_view}); + + number_timecode.set_value(1); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + tx_view.set_transmitting(true); + tx_mode = NORMAL; + transmit(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + tx_mode = IDLE; + }; + + /*button_impro.on_select = [this](Button&){ + if (tx_mode == IMPROVISE) { + tx_mode = IDLE; + button_impro.set_text("IMPROVISE"); + } else if (tx_mode == IDLE) { + tx_mode = IMPROVISE; + button_impro.set_text("STOP"); + transmit(true); + } + };*/ } + +} // namespace ui diff --git a/firmware/application/apps/ui_nuoptix.hpp b/firmware/application/apps/ui_nuoptix.hpp index 9fa36d52e..f5445804d 100644 --- a/firmware/application/apps/ui_nuoptix.hpp +++ b/firmware/application/apps/ui_nuoptix.hpp @@ -35,76 +35,70 @@ #include "volume.hpp" #include "audio.hpp" -#define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms +#define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms namespace ui { - + class NuoptixView : public View { -public: - NuoptixView(NavigationView& nav); - ~NuoptixView(); - - void focus() override; - - std::string title() const override { return "Nuoptix sync"; }; - -private: - enum tx_modes { - IDLE = 0, - NORMAL, - IMPROVISE - }; - - tx_modes tx_mode { IDLE }; - - void on_tuning_frequency_changed(rf::Frequency f); - void transmit(bool setup); - void on_tx_progress(const uint32_t progress, const bool done); - - uint32_t timecode { 0 }; - - Text text_timecode { - { 10 * 8, 2 * 16, 9 * 8, 16 }, - "Timecode:" - }; - - NumberField number_timecode { - { 13 * 8, 3 * 16 }, - 4, - { 1, 9999 }, - 1, - '0' - }; - - Text text_mod { - { 10 * 8, 5 * 16, 6 * 8, 16 }, - "Mod: " - }; - - ProgressBar progressbar { - { 16, 14 * 16, 208, 16 } - }; - - /*Button button_impro { - { 64, 184, 112, 40 }, - "IMPROVISE" - };*/ - - TransmitterView tx_view { - 16 * 16, - 10000, - 15 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + NuoptixView(NavigationView& nav); + ~NuoptixView(); + + void focus() override; + + std::string title() const override { return "Nuoptix sync"; }; + + private: + enum tx_modes { + IDLE = 0, + NORMAL, + IMPROVISE + }; + + tx_modes tx_mode{IDLE}; + + void on_tuning_frequency_changed(rf::Frequency f); + void transmit(bool setup); + void on_tx_progress(const uint32_t progress, const bool done); + + uint32_t timecode{0}; + + Text text_timecode{ + {10 * 8, 2 * 16, 9 * 8, 16}, + "Timecode:"}; + + NumberField number_timecode{ + {13 * 8, 3 * 16}, + 4, + {1, 9999}, + 1, + '0'}; + + Text text_mod{ + {10 * 8, 5 * 16, 6 * 8, 16}, + "Mod: "}; + + ProgressBar progressbar{ + {16, 14 * 16, 208, 16}}; + + /*Button button_impro { + { 64, 184, 112, 40 }, + "IMPROVISE" + };*/ + + TransmitterView tx_view{ + 16 * 16, + 10000, + 15}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ -#endif/*__UI_NUOPTIX_H__*/ +#endif /*__UI_NUOPTIX_H__*/ diff --git a/firmware/application/apps/ui_playlist.cpp b/firmware/application/apps/ui_playlist.cpp index 9775cdaee..b68ecf0a8 100644 --- a/firmware/application/apps/ui_playlist.cpp +++ b/firmware/application/apps/ui_playlist.cpp @@ -37,277 +37,271 @@ using namespace portapack; namespace ui { void PlaylistView::set_ready() { - ready_signal = true; + ready_signal = true; } +void PlaylistView::load_file(std::filesystem::path playlist_path) { + File playlist_file; + + auto error = playlist_file.open(playlist_path.string()); + if (!error.is_valid()) { + std::string line; + char one_char[1]; + for (size_t pointer = 0; pointer < playlist_file.size(); pointer++) { + playlist_file.seek(pointer); + playlist_file.read(one_char, 1); + if ((int)one_char[0] >= ' ') { + line += one_char[0]; + } else if (one_char[0] == '\n') { + txtline_process(line); + line.clear(); + } + } + if (line.length() > 0) { + txtline_process(line); + } + } + playlist_masterdb = playlist_db; + return; +} - void PlaylistView::load_file(std::filesystem::path playlist_path) { - File playlist_file; - - auto error = playlist_file.open(playlist_path.string()); - if (!error.is_valid()) { - std::string line; - char one_char[1]; - for (size_t pointer =0 ; pointer < playlist_file.size(); pointer++) { - playlist_file.seek(pointer); - playlist_file.read(one_char, 1); - if ((int) one_char[0] >= ' ') { - line += one_char[0]; - } else if (one_char[0] == '\n') { - txtline_process(line); - line.clear(); - } - } - if (line.length() > 0) { - txtline_process(line); - } - } - playlist_masterdb = playlist_db; - return ; - } - - - void PlaylistView::txtline_process(std::string &line) { - playlist_entry new_item; - rf::Frequency f; - size_t previous = 0; - size_t current = line.find(','); - std::string freqs = line.substr(0, current); - previous = current +1; - current = line.find(',', previous); - std::string file = line.substr(previous, current - previous); - previous = current +1; - current = line.find(',', previous); - uint32_t sample_rate = strtoll(line.substr(previous).c_str(), nullptr, 10); - - f = strtoll(freqs.c_str(), nullptr, 0); - new_item.replay_frequency = f; - new_item.replay_file = "/" + file; - new_item.sample_rate = sample_rate; - new_item.next_delay = 0; - - playlist_db.push_back(std::move(new_item)); - } +void PlaylistView::txtline_process(std::string& line) { + playlist_entry new_item; + rf::Frequency f; + size_t previous = 0; + size_t current = line.find(','); + std::string freqs = line.substr(0, current); + previous = current + 1; + current = line.find(',', previous); + std::string file = line.substr(previous, current - previous); + previous = current + 1; + current = line.find(',', previous); + uint32_t sample_rate = strtoll(line.substr(previous).c_str(), nullptr, 10); + + f = strtoll(freqs.c_str(), nullptr, 0); + new_item.replay_frequency = f; + new_item.replay_file = "/" + file; + new_item.sample_rate = sample_rate; + new_item.next_delay = 0; + + playlist_db.push_back(std::move(new_item)); +} void PlaylistView::on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate) { - File data_file; - // Get file size - auto data_open_error = data_file.open("/" + new_file_path.string()); - if (data_open_error.is_valid()) { - file_error(); - return; - } - - file_path = new_file_path; - field_frequency.set_value(replay_frequency); - - sample_rate = replay_sample_rate; - - text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz"); - - auto file_size = data_file.size(); - auto duration = (file_size * 1000) / (2 * 2 * sample_rate); - - progressbar.set_max(file_size); - text_filename.set(file_path.filename().string().substr(0, 12)); - text_duration.set(to_string_time_ms(duration)); - - button_play.focus(); + File data_file; + // Get file size + auto data_open_error = data_file.open("/" + new_file_path.string()); + if (data_open_error.is_valid()) { + file_error(); + return; + } + + file_path = new_file_path; + field_frequency.set_value(replay_frequency); + + sample_rate = replay_sample_rate; + + text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz"); + + auto file_size = data_file.size(); + auto duration = (file_size * 1000) / (2 * 2 * sample_rate); + + progressbar.set_max(file_size); + text_filename.set(file_path.filename().string().substr(0, 12)); + text_duration.set(to_string_time_ms(duration)); + + button_play.focus(); } void PlaylistView::on_tx_progress(const uint32_t progress) { - progressbar.set_value(progress); + progressbar.set_value(progress); } void PlaylistView::focus() { - button_open.focus(); + button_open.focus(); } void PlaylistView::file_error() { - nav_.display_modal("Error", "File "+file_path.string() +" read error. " +file_path.string()); + nav_.display_modal("Error", "File " + file_path.string() + " read error. " + file_path.string()); } bool PlaylistView::is_active() const { - return (bool)replay_thread; + return (bool)replay_thread; } bool PlaylistView::loop() const { - return (bool) playlist_db.size(); + return (bool)playlist_db.size(); } void PlaylistView::toggle() { - if( is_active() ) { - stop(false); - } else { - - start(); - } + if (is_active()) { + stop(false); + } else { + start(); + } } void PlaylistView::start() { - stop(false); - - playlist_entry item = playlist_db.front(); - playlist_db.pop_front(); - // playlist_entry item = playlist_db[0]; - // for (playlist_entry item : playlist_db) { - // file_path = item.replay_file; - // rf::Frequency replay_frequency = strtoll(item.replay_frequency.c_str(),nullptr,10); - on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate); - on_target_frequency_changed(item.replay_frequency); - - std::unique_ptr reader; - - auto p = std::make_unique(); - auto open_error = p->open(file_path); - if( open_error.is_valid() ) { - file_error(); - return; // Fixes TX bug if there's a file error - } else { - reader = std::move(p); - } - - if( reader ) { - button_play.set_bitmap(&bitmap_stop); - baseband::set_sample_rate(sample_rate * 8); - - replay_thread = std::make_unique( - std::move(reader), - read_size, buffer_count, - &ready_signal, - [](uint32_t return_code) { - ReplayThreadDoneMessage message { return_code }; - EventDispatcher::send_message(message); - } - ); - } - - //Enable Bias Tee if selected - radio::set_antenna_bias(portapack::get_antenna_bias()); - - rf_amp =(transmitter_model.rf_amp() ); // recover rf_amp settings applied from ui_transmiter.cpp - - radio::enable({ - receiver_model.tuning_frequency(), - sample_rate * 8, - baseband_bandwidth, - rf::Direction::Transmit, - rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()) - }); - -// } -} + stop(false); + + playlist_entry item = playlist_db.front(); + playlist_db.pop_front(); + // playlist_entry item = playlist_db[0]; + // for (playlist_entry item : playlist_db) { + // file_path = item.replay_file; + // rf::Frequency replay_frequency = strtoll(item.replay_frequency.c_str(),nullptr,10); + on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate); + on_target_frequency_changed(item.replay_frequency); + + std::unique_ptr reader; + + auto p = std::make_unique(); + auto open_error = p->open(file_path); + if (open_error.is_valid()) { + file_error(); + return; // Fixes TX bug if there's a file error + } else { + reader = std::move(p); + } + + if (reader) { + button_play.set_bitmap(&bitmap_stop); + baseband::set_sample_rate(sample_rate * 8); + + replay_thread = std::make_unique( + std::move(reader), + read_size, buffer_count, + &ready_signal, + [](uint32_t return_code) { + ReplayThreadDoneMessage message{return_code}; + EventDispatcher::send_message(message); + }); + } + + // Enable Bias Tee if selected + radio::set_antenna_bias(portapack::get_antenna_bias()); + + rf_amp = (transmitter_model.rf_amp()); // recover rf_amp settings applied from ui_transmiter.cpp + + radio::enable({receiver_model.tuning_frequency(), + sample_rate * 8, + baseband_bandwidth, + rf::Direction::Transmit, + rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga())}); + + // } +} void PlaylistView::stop(const bool do_loop) { - if( is_active() ) { - replay_thread.reset(); - } - if (do_loop) { - if (playlist_db.size() > 0 ) { - start(); - } else { - playlist_db = playlist_masterdb; - start(); - } - } else { - radio::set_antenna_bias(false); //Turn off Bias Tee - radio::disable(); - button_play.set_bitmap(&bitmap_play); - } - - ready_signal = false; + if (is_active()) { + replay_thread.reset(); + } + if (do_loop) { + if (playlist_db.size() > 0) { + start(); + } else { + playlist_db = playlist_masterdb; + start(); + } + } else { + radio::set_antenna_bias(false); // Turn off Bias Tee + radio::disable(); + button_play.set_bitmap(&bitmap_play); + } + + ready_signal = false; } void PlaylistView::handle_replay_thread_done(const uint32_t return_code) { - if (return_code == ReplayThread::END_OF_FILE) { - stop(true); - } else if (return_code == ReplayThread::READ_ERROR) { - stop(false); - file_error(); - } - - progressbar.set_value(0); + if (return_code == ReplayThread::END_OF_FILE) { + stop(true); + } else if (return_code == ReplayThread::READ_ERROR) { + stop(false); + file_error(); + } + + progressbar.set_value(0); } PlaylistView::PlaylistView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_replay); - - add_children({ - &button_open, - &text_filename, - &text_sample_rate, - &text_duration, - &progressbar, - &field_frequency, - &tx_view, // this handles now the previous rfgain, rfamp - &check_loop, - &button_play, - &waterfall, - }); - - field_frequency.set_value(target_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); - field_frequency.on_change = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(this->target_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - field_frequency.set_step(5000); - - button_play.on_select = [this](ImageButton&) { - this->toggle(); - }; - - button_open.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this](std::filesystem::path new_file_path) { - load_file(new_file_path); - }; - }; + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_replay); + + add_children({ + &button_open, + &text_filename, + &text_sample_rate, + &text_duration, + &progressbar, + &field_frequency, + &tx_view, // this handles now the previous rfgain, rfamp + &check_loop, + &button_play, + &waterfall, + }); + + field_frequency.set_value(target_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(this->target_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.set_step(5000); + + button_play.on_select = [this](ImageButton&) { + this->toggle(); + }; + + button_open.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this](std::filesystem::path new_file_path) { + load_file(new_file_path); + }; + }; } PlaylistView::~PlaylistView() { - radio::disable(); - baseband::shutdown(); + radio::disable(); + baseband::shutdown(); } void PlaylistView::on_hide() { - stop(false); - // TODO: Terrible kludge because widget system doesn't notify Waterfall that - // it's being shown or hidden. - waterfall.on_hide(); - View::on_hide(); + stop(false); + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + waterfall.on_hide(); + View::on_hide(); } void PlaylistView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; - waterfall.set_parent_rect(waterfall_rect); + const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; + waterfall.set_parent_rect(waterfall_rect); } void PlaylistView::on_target_frequency_changed(rf::Frequency f) { - set_target_frequency(f); + set_target_frequency(f); } void PlaylistView::set_target_frequency(const rf::Frequency new_value) { - persistent_memory::set_tuned_frequency(new_value);; + persistent_memory::set_tuned_frequency(new_value); + ; } rf::Frequency PlaylistView::target_frequency() const { - return persistent_memory::tuned_frequency(); + return persistent_memory::tuned_frequency(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_playlist.hpp b/firmware/application/apps/ui_playlist.hpp index 9d1402465..7efca2c9f 100644 --- a/firmware/application/apps/ui_playlist.hpp +++ b/firmware/application/apps/ui_playlist.hpp @@ -20,8 +20,8 @@ * Boston, MA 02110-1301, USA. */ -#define SHORT_UI true -#define NORMAL_UI false +#define SHORT_UI true +#define NORMAL_UI false #include "ui_widget.hpp" #include "ui_navigation.hpp" @@ -37,129 +37,119 @@ namespace ui { class PlaylistView : public View { -public: - PlaylistView(NavigationView& nav); - ~PlaylistView(); - - void on_hide() override; - void set_parent_rect(const Rect new_parent_rect) override; - void focus() override; - - std::string title() const override { return "Playlist"; }; - -private: - NavigationView& nav_; - - static constexpr ui::Dim header_height = 3 * 16; - - struct playlist_entry { - rf::Frequency replay_frequency { 0 }; - std::string replay_file{}; - uint32_t sample_rate{}; - uint32_t next_delay{}; - }; - std::deque playlist_db{}; - std::deque playlist_masterdb{}; - uint32_t sample_rate = 0; - int32_t tx_gain { 47 }; - bool rf_amp { true }; // aux private var to store temporal, Replay App rf_amp user selection. - static constexpr uint32_t baseband_bandwidth = 2500000; - const size_t read_size { 16384 }; - const size_t buffer_count { 3 }; - void load_file(std::filesystem::path playlist_path); - void txtline_process(std::string &); - void on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate); - void on_target_frequency_changed(rf::Frequency f); - void on_tx_progress(const uint32_t progress); - - void set_target_frequency(const rf::Frequency new_value); - rf::Frequency target_frequency() const; - - void toggle(); - void start(); - void stop(const bool do_loop); - bool is_active() const; - bool loop() const; - void set_ready(); - void handle_replay_thread_done(const uint32_t return_code); - void file_error(); - - std::filesystem::path file_path { }; - std::unique_ptr replay_thread { }; - bool ready_signal { false }; - - Button button_open { - { 0 * 8, 0 * 16, 10 * 8, 2 * 16 }, - "Open file" - }; - - - Text text_filename { - { 11 * 8, 0 * 16, 12 * 8, 16 }, - "-" - }; - Text text_sample_rate { - { 24 * 8, 0 * 16, 6 * 8, 16 }, - "-" - }; - - Text text_duration { - { 11 * 8, 1 * 16, 6 * 8, 16 }, - "-" - }; - ProgressBar progressbar { - { 18 * 8, 1 * 16, 12 * 8, 16 } - }; - - FrequencyField field_frequency { - { 0 * 8, 2 * 16 }, - }; - - TransmitterView2 tx_view { // new handling of NumberField field_rfgain, NumberField field_rfamp - 74, 1*8, SHORT_UI // x(columns), y (line) position. (Used in Repay / GPS Simul / Play list App) -// 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) - }; - - Checkbox check_loop { - { 21 * 8, 2 * 16 }, - 4, - "Loop", - true - }; - ImageButton button_play { - { 28 * 8, 2 * 16, 2 * 8, 1 * 16 }, - &bitmap_play, - Color::green(), - Color::black() - }; - - spectrum::WaterfallWidget waterfall { }; - - MessageHandlerRegistration message_handler_replay_thread_error { - Message::ID::ReplayThreadDone, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_replay_thread_done(message.return_code); - } - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->set_ready(); - } - } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress); - } - }; + public: + PlaylistView(NavigationView& nav); + ~PlaylistView(); + + void on_hide() override; + void set_parent_rect(const Rect new_parent_rect) override; + void focus() override; + + std::string title() const override { return "Playlist"; }; + + private: + NavigationView& nav_; + + static constexpr ui::Dim header_height = 3 * 16; + + struct playlist_entry { + rf::Frequency replay_frequency{0}; + std::string replay_file{}; + uint32_t sample_rate{}; + uint32_t next_delay{}; + }; + std::deque playlist_db{}; + std::deque playlist_masterdb{}; + uint32_t sample_rate = 0; + int32_t tx_gain{47}; + bool rf_amp{true}; // aux private var to store temporal, Replay App rf_amp user selection. + static constexpr uint32_t baseband_bandwidth = 2500000; + const size_t read_size{16384}; + const size_t buffer_count{3}; + void load_file(std::filesystem::path playlist_path); + void txtline_process(std::string&); + void on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate); + void on_target_frequency_changed(rf::Frequency f); + void on_tx_progress(const uint32_t progress); + + void set_target_frequency(const rf::Frequency new_value); + rf::Frequency target_frequency() const; + + void toggle(); + void start(); + void stop(const bool do_loop); + bool is_active() const; + bool loop() const; + void set_ready(); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); + + std::filesystem::path file_path{}; + std::unique_ptr replay_thread{}; + bool ready_signal{false}; + + Button button_open{ + {0 * 8, 0 * 16, 10 * 8, 2 * 16}, + "Open file"}; + + Text text_filename{ + {11 * 8, 0 * 16, 12 * 8, 16}, + "-"}; + Text text_sample_rate{ + {24 * 8, 0 * 16, 6 * 8, 16}, + "-"}; + + Text text_duration{ + {11 * 8, 1 * 16, 6 * 8, 16}, + "-"}; + ProgressBar progressbar{ + {18 * 8, 1 * 16, 12 * 8, 16}}; + + FrequencyField field_frequency{ + {0 * 8, 2 * 16}, + }; + + TransmitterView2 tx_view{ + // new handling of NumberField field_rfgain, NumberField field_rfamp + 74, 1 * 8, SHORT_UI // x(columns), y (line) position. (Used in Repay / GPS Simul / Play list App) + // 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App) + }; + + Checkbox check_loop{ + {21 * 8, 2 * 16}, + 4, + "Loop", + true}; + ImageButton button_play{ + {28 * 8, 2 * 16, 2 * 8, 1 * 16}, + &bitmap_play, + Color::green(), + Color::black()}; + + spectrum::WaterfallWidget waterfall{}; + + MessageHandlerRegistration message_handler_replay_thread_error{ + Message::ID::ReplayThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_replay_thread_done(message.return_code); + }}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->set_ready(); + } + }}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_pocsag_tx.cpp b/firmware/application/apps/ui_pocsag_tx.cpp index 0a329676c..73513a460 100644 --- a/firmware/application/apps/ui_pocsag_tx.cpp +++ b/firmware/application/apps/ui_pocsag_tx.cpp @@ -35,163 +35,158 @@ using namespace pocsag; namespace ui { void POCSAGTXView::focus() { - field_address.focus(); + field_address.focus(); } POCSAGTXView::~POCSAGTXView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_pocsag", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_pocsag", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void POCSAGTXView::on_tx_progress(const uint32_t progress, const bool done) { - if (done) { - transmitter_model.disable(); - progressbar.set_value(0); - tx_view.set_transmitting(false); - } else - progressbar.set_value(progress); + if (done) { + transmitter_model.disable(); + progressbar.set_value(0); + tx_view.set_transmitting(false); + } else + progressbar.set_value(progress); } bool POCSAGTXView::start_tx() { - uint32_t total_frames, i, codeword, bi, address; - pocsag::BitRate bitrate; - std::vector codewords; - - address = field_address.value_dec_u32(); - if (address > 0x1FFFFFU) { - nav_.display_modal("Bad address", "Address must be less\nthan 2097152."); - return false; - } - - MessageType type = (MessageType)options_type.selected_index_value(); - - if (type == MessageType::NUMERIC_ONLY) { - // Check for invalid characters - if (message.find_first_not_of("0123456789SU -][") != std::string::npos) { - nav_.display_modal("Bad message", "A numeric only message must\nonly contain:\n0123456789SU][- or space."); - return false; - } - } - MessageType phase = (MessageType)options_phase.selected_index_value(); - - pocsag_encode(type, BCH_code, options_function.selected_index_value(), message, address, codewords); - - total_frames = codewords.size() / 2; - - progressbar.set_max(total_frames); - - transmitter_model.set_sampling_rate(2280000); - transmitter_model.set_rf_amp(true); - transmitter_model.set_lna(40); - transmitter_model.set_vga(40); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - uint8_t * data_ptr = shared_memory.bb_data.data; - - bi = 0; - for (i = 0; i < codewords.size(); i++) { - if (phase == 0) - codeword = ~(codewords[i]); - else - codeword = codewords[i]; - - data_ptr[bi++] = (codeword >> 24) & 0xFF; - data_ptr[bi++] = (codeword >> 16) & 0xFF; - data_ptr[bi++] = (codeword >> 8) & 0xFF; - data_ptr[bi++] = codeword & 0xFF; - } - - bitrate = pocsag_bitrates[options_bitrate.selected_index()]; - - baseband::set_fsk_data( - codewords.size() * 32, - 2280000 / bitrate, - 4500, - 64 - ); - - return true; + uint32_t total_frames, i, codeword, bi, address; + pocsag::BitRate bitrate; + std::vector codewords; + + address = field_address.value_dec_u32(); + if (address > 0x1FFFFFU) { + nav_.display_modal("Bad address", "Address must be less\nthan 2097152."); + return false; + } + + MessageType type = (MessageType)options_type.selected_index_value(); + + if (type == MessageType::NUMERIC_ONLY) { + // Check for invalid characters + if (message.find_first_not_of("0123456789SU -][") != std::string::npos) { + nav_.display_modal("Bad message", "A numeric only message must\nonly contain:\n0123456789SU][- or space."); + return false; + } + } + MessageType phase = (MessageType)options_phase.selected_index_value(); + + pocsag_encode(type, BCH_code, options_function.selected_index_value(), message, address, codewords); + + total_frames = codewords.size() / 2; + + progressbar.set_max(total_frames); + + transmitter_model.set_sampling_rate(2280000); + transmitter_model.set_rf_amp(true); + transmitter_model.set_lna(40); + transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + uint8_t* data_ptr = shared_memory.bb_data.data; + + bi = 0; + for (i = 0; i < codewords.size(); i++) { + if (phase == 0) + codeword = ~(codewords[i]); + else + codeword = codewords[i]; + + data_ptr[bi++] = (codeword >> 24) & 0xFF; + data_ptr[bi++] = (codeword >> 16) & 0xFF; + data_ptr[bi++] = (codeword >> 8) & 0xFF; + data_ptr[bi++] = codeword & 0xFF; + } + + bitrate = pocsag_bitrates[options_bitrate.selected_index()]; + + baseband::set_fsk_data( + codewords.size() * 32, + 2280000 / bitrate, + 4500, + 64); + + return true; } void POCSAGTXView::paint(Painter&) { - message = buffer; - text_message.set(message); + message = buffer; + text_message.set(message); } void POCSAGTXView::on_set_text(NavigationView& nav) { - text_prompt(nav, buffer, 30); + text_prompt(nav, buffer, 30); } POCSAGTXView::POCSAGTXView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_fsktx); - - add_children({ - &labels, - &options_bitrate, - &field_address, - &options_type, - &options_function, - &options_phase, - &text_message, - &button_message, - &progressbar, - &tx_view - }); - - // load app settings - auto rc = settings.load("tx_pocsag", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - options_bitrate.set_selected_index(1); // 1200bps - options_type.set_selected_index(0); // Address only - - // TODO: set_value for whole symfield - uint32_t reload_address = persistent_memory::pocsag_last_address(); - for (uint32_t c = 0; c < 7; c++) { - field_address.set_sym(6 - c, reload_address % 10); - reload_address /= 10; - } - - options_type.on_change = [this](size_t, int32_t i) { - if (i == 2) - options_function.set_selected_index(3); - }; - - button_message.on_select = [this, &nav](Button&) { - this->on_set_text(nav); - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(transmitter_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - if (start_tx()) - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - tx_view.set_transmitting(false); - transmitter_model.disable(); - }; - + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_fsktx); + + add_children({&labels, + &options_bitrate, + &field_address, + &options_type, + &options_function, + &options_phase, + &text_message, + &button_message, + &progressbar, + &tx_view}); + + // load app settings + auto rc = settings.load("tx_pocsag", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + options_bitrate.set_selected_index(1); // 1200bps + options_type.set_selected_index(0); // Address only + + // TODO: set_value for whole symfield + uint32_t reload_address = persistent_memory::pocsag_last_address(); + for (uint32_t c = 0; c < 7; c++) { + field_address.set_sym(6 - c, reload_address % 10); + reload_address /= 10; + } + + options_type.on_change = [this](size_t, int32_t i) { + if (i == 2) + options_function.set_selected_index(3); + }; + + button_message.on_select = [this, &nav](Button&) { + this->on_set_text(nav); + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + if (start_tx()) + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + transmitter_model.disable(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_pocsag_tx.hpp b/firmware/application/apps/ui_pocsag_tx.hpp index 470ce0fea..4fa9a1702 100644 --- a/firmware/application/apps/ui_pocsag_tx.hpp +++ b/firmware/application/apps/ui_pocsag_tx.hpp @@ -39,122 +39,107 @@ using namespace pocsag; namespace ui { class POCSAGTXView : public View { -public: - POCSAGTXView(NavigationView& nav); - ~POCSAGTXView(); - - POCSAGTXView(const POCSAGTXView&) = delete; - POCSAGTXView(POCSAGTXView&&) = delete; - POCSAGTXView& operator=(const POCSAGTXView&) = delete; - POCSAGTXView& operator=(POCSAGTXView&&) = delete; - - void focus() override; - void paint(Painter&) override; - - std::string title() const override { return "POCSAG TX"; }; - -private: - std::string buffer { "PORTAPACK" }; - std::string message { }; - NavigationView& nav_; - - BCHCode BCH_code { - { 1, 0, 1, 0, 0, 1 }, - 5, 31, 21, 2 - }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - void on_set_text(NavigationView& nav); - void on_tx_progress(const uint32_t progress, const bool done); - bool start_tx(); - - Labels labels { - { { 3 * 8, 4 * 8 }, "Bitrate:", Color::light_grey() }, - { { 3 * 8, 6 * 8 }, "Address:", Color::light_grey() }, - { { 6 * 8, 8 * 8 }, "Type:", Color::light_grey() }, - { { 2 * 8, 10 * 8 }, "Function:", Color::light_grey() }, - { { 5 * 8, 12 * 8 }, "Phase:", Color::light_grey() }, - { { 0 * 8, 14 * 8 }, "Message:", Color::light_grey() } - }; - - OptionsField options_bitrate { - { 11 * 8, 4 * 8 }, - 8, - { - { "512 bps ", 0 }, - { "1200 bps", 1 }, - { "2400 bps", 2 } - } - }; - - SymField field_address { - { 11 * 8, 6 * 8 }, - 7, - SymField::SYMFIELD_DEC - }; - - OptionsField options_type { - { 11 * 8, 8 * 8 }, - 12, - { - { "Address only", MessageType::ADDRESS_ONLY }, - { "Numeric only", MessageType::NUMERIC_ONLY }, - { "Alphanumeric", MessageType::ALPHANUMERIC } - } - }; - - OptionsField options_function { - { 11 * 8, 10 * 8 }, - 1, - { - { "A", 0 }, - { "B", 1 }, - { "C", 2 }, - { "D", 3 } - } - }; - - OptionsField options_phase { - { 11 * 8, 12 * 8 }, - 1, - { - { "P", 0 }, - { "N", 1 }, - } - }; - - Text text_message { - { 0 * 8, 16 * 8, 16 * 8, 16 }, - "" - }; - - Button button_message { - { 0 * 8, 18 * 8, 14 * 8, 32 }, - "Set message" - }; - - ProgressBar progressbar { - { 16, 200, 208, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 9 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + POCSAGTXView(NavigationView& nav); + ~POCSAGTXView(); + + POCSAGTXView(const POCSAGTXView&) = delete; + POCSAGTXView(POCSAGTXView&&) = delete; + POCSAGTXView& operator=(const POCSAGTXView&) = delete; + POCSAGTXView& operator=(POCSAGTXView&&) = delete; + + void focus() override; + void paint(Painter&) override; + + std::string title() const override { return "POCSAG TX"; }; + + private: + std::string buffer{"PORTAPACK"}; + std::string message{}; + NavigationView& nav_; + + BCHCode BCH_code{ + {1, 0, 1, 0, 0, 1}, + 5, + 31, + 21, + 2}; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + void on_set_text(NavigationView& nav); + void on_tx_progress(const uint32_t progress, const bool done); + bool start_tx(); + + Labels labels{ + {{3 * 8, 4 * 8}, "Bitrate:", Color::light_grey()}, + {{3 * 8, 6 * 8}, "Address:", Color::light_grey()}, + {{6 * 8, 8 * 8}, "Type:", Color::light_grey()}, + {{2 * 8, 10 * 8}, "Function:", Color::light_grey()}, + {{5 * 8, 12 * 8}, "Phase:", Color::light_grey()}, + {{0 * 8, 14 * 8}, "Message:", Color::light_grey()}}; + + OptionsField options_bitrate{ + {11 * 8, 4 * 8}, + 8, + {{"512 bps ", 0}, + {"1200 bps", 1}, + {"2400 bps", 2}}}; + + SymField field_address{ + {11 * 8, 6 * 8}, + 7, + SymField::SYMFIELD_DEC}; + + OptionsField options_type{ + {11 * 8, 8 * 8}, + 12, + {{"Address only", MessageType::ADDRESS_ONLY}, + {"Numeric only", MessageType::NUMERIC_ONLY}, + {"Alphanumeric", MessageType::ALPHANUMERIC}}}; + + OptionsField options_function{ + {11 * 8, 10 * 8}, + 1, + {{"A", 0}, + {"B", 1}, + {"C", 2}, + {"D", 3}}}; + + OptionsField options_phase{ + {11 * 8, 12 * 8}, + 1, + { + {"P", 0}, + {"N", 1}, + }}; + + Text text_message{ + {0 * 8, 16 * 8, 16 * 8, 16}, + ""}; + + Button button_message{ + {0 * 8, 18 * 8, 14 * 8, 32}, + "Set message"}; + + ProgressBar progressbar{ + {16, 200, 208, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 9}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ -#endif/*__POCSAG_TX_H__*/ +#endif /*__POCSAG_TX_H__*/ diff --git a/firmware/application/apps/ui_rds.cpp b/firmware/application/apps/ui_rds.cpp index 35ac84361..2091c48f4 100644 --- a/firmware/application/apps/ui_rds.cpp +++ b/firmware/application/apps/ui_rds.cpp @@ -35,245 +35,228 @@ using namespace rds; namespace ui { RDSPSNView::RDSPSNView( - NavigationView& nav, - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("PSN"); - - add_children({ - &labels, - &text_psn, - &button_set, - &check_mono_stereo, - &check_TA, - &check_MS - }); - - set_enabled(true); - - check_TA.set_value(true); - - check_mono_stereo.on_select = [this](Checkbox&, bool value) { - mono_stereo = value; - }; - check_TA.on_select = [this](Checkbox&, bool value) { - TA = value; - }; - check_MS.on_select = [this](Checkbox&, bool value) { - MS = value; - }; - - button_set.on_select = [this, &nav](Button&) { - text_prompt( - nav, - PSN, - 8, - [this](std::string& s) { - text_psn.set(s); - } - ); - }; + NavigationView& nav, + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("PSN"); + + add_children({&labels, + &text_psn, + &button_set, + &check_mono_stereo, + &check_TA, + &check_MS}); + + set_enabled(true); + + check_TA.set_value(true); + + check_mono_stereo.on_select = [this](Checkbox&, bool value) { + mono_stereo = value; + }; + check_TA.on_select = [this](Checkbox&, bool value) { + TA = value; + }; + check_MS.on_select = [this](Checkbox&, bool value) { + MS = value; + }; + + button_set.on_select = [this, &nav](Button&) { + text_prompt( + nav, + PSN, + 8, + [this](std::string& s) { + text_psn.set(s); + }); + }; } RDSRadioTextView::RDSRadioTextView( - NavigationView& nav, - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("Radiotext"); - - add_children({ - &labels, - &button_set, - &text_radiotext - }); - - button_set.on_select = [this, &nav](Button&){ - text_prompt( - nav, - radiotext, - 28, - [this](std::string& s) { - text_radiotext.set(s); - } - ); - }; + NavigationView& nav, + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("Radiotext"); + + add_children({&labels, + &button_set, + &text_radiotext}); + + button_set.on_select = [this, &nav](Button&) { + text_prompt( + nav, + radiotext, + 28, + [this](std::string& s) { + text_radiotext.set(s); + }); + }; } RDSDateTimeView::RDSDateTimeView( - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("date & time"); - - add_children({ - &labels - }); + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("date & time"); + + add_children({&labels}); } RDSAudioView::RDSAudioView( - Rect parent_rect -) : OptionTabView(parent_rect) -{ - set_type("audio"); - - add_children({ - &labels - }); + Rect parent_rect) + : OptionTabView(parent_rect) { + set_type("audio"); + + add_children({&labels}); } RDSThread::RDSThread( - std::vector** frames -) : frames_ { std::move(frames) } -{ - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, RDSThread::static_fn, this); + std::vector** frames) + : frames_{std::move(frames)} { + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, RDSThread::static_fn, this); } RDSThread::~RDSThread() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } + if (thread) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } } msg_t RDSThread::static_fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); - return 0; + auto obj = static_cast(arg); + obj->run(); + return 0; } void RDSThread::run() { - std::vector* frame_ptr; - size_t block_count, c; - uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data; - uint32_t frame_index = 0; - - while( !chThdShouldTerminate() ) { - - do { - frame_ptr = frames_[frame_index]; - - if (frame_index == 2) { - frame_index = 0; - } else { - frame_index++; - } - } while(!(block_count = frame_ptr->size() * 4)); - - for (c = 0; c < block_count; c++) - tx_data_u32[c] = frame_ptr->at(c >> 2).block[c & 3]; - - baseband::set_rds_data(block_count * 26); - - chThdSleepMilliseconds(1000); - } + std::vector* frame_ptr; + size_t block_count, c; + uint32_t* tx_data_u32 = (uint32_t*)shared_memory.bb_data.data; + uint32_t frame_index = 0; + + while (!chThdShouldTerminate()) { + do { + frame_ptr = frames_[frame_index]; + + if (frame_index == 2) { + frame_index = 0; + } else { + frame_index++; + } + } while (!(block_count = frame_ptr->size() * 4)); + + for (c = 0; c < block_count; c++) + tx_data_u32[c] = frame_ptr->at(c >> 2).block[c & 3]; + + baseband::set_rds_data(block_count * 26); + + chThdSleepMilliseconds(1000); + } } void RDSView::focus() { - tab_view.focus(); + tab_view.focus(); } RDSView::~RDSView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_rds", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_rds", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void RDSView::start_tx() { - rds_flags.PI_code = sym_pi_code.value_hex_u64(); - rds_flags.PTY = options_pty.selected_index_value(); - rds_flags.DI = view_PSN.mono_stereo ? 1 : 0; - rds_flags.TP = check_TP.value(); - rds_flags.TA = view_PSN.TA; - rds_flags.MS = view_PSN.MS; - - if (view_PSN.is_enabled()) - gen_PSN(frame_psn, view_PSN.PSN, &rds_flags); - else - frame_psn.clear(); - - if (view_radiotext.is_enabled()) - gen_RadioText(frame_radiotext, view_radiotext.radiotext, 0, &rds_flags); - else - frame_radiotext.clear(); - - // DEBUG - if (view_datetime.is_enabled()) - gen_ClockTime(frame_datetime, &rds_flags, 2016, 12, 1, 9, 23, 2); - else - frame_datetime.clear(); - - transmitter_model.set_sampling_rate(2280000U); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - tx_thread = std::make_unique(frames); + rds_flags.PI_code = sym_pi_code.value_hex_u64(); + rds_flags.PTY = options_pty.selected_index_value(); + rds_flags.DI = view_PSN.mono_stereo ? 1 : 0; + rds_flags.TP = check_TP.value(); + rds_flags.TA = view_PSN.TA; + rds_flags.MS = view_PSN.MS; + + if (view_PSN.is_enabled()) + gen_PSN(frame_psn, view_PSN.PSN, &rds_flags); + else + frame_psn.clear(); + + if (view_radiotext.is_enabled()) + gen_RadioText(frame_radiotext, view_radiotext.radiotext, 0, &rds_flags); + else + frame_radiotext.clear(); + + // DEBUG + if (view_datetime.is_enabled()) + gen_ClockTime(frame_datetime, &rds_flags, 2016, 12, 1, 9, 23, 2); + else + frame_datetime.clear(); + + transmitter_model.set_sampling_rate(2280000U); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + tx_thread = std::make_unique(frames); } RDSView::RDSView( - NavigationView& nav -) : nav_ { nav } -{ - baseband::run_image(portapack::spi_flash::image_tag_rds); - - add_children({ - &tab_view, - &labels, - &sym_pi_code, - &check_TP, - &options_pty, - &view_PSN, - &view_radiotext, - &view_datetime, - &view_audio, - &tx_view, - }); - - // load app settings - auto rc = settings.load("tx_rds", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - check_TP.set_value(true); - - sym_pi_code.set_sym(0, 0xF); - sym_pi_code.set_sym(1, 0x3); - sym_pi_code.set_sym(2, 0xE); - sym_pi_code.set_sym(3, 0x0); - sym_pi_code.on_change = [this]() { - rds_flags.PI_code = sym_pi_code.value_hex_u64(); - }; - - options_pty.set_selected_index(0); // None - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - txing = true; - }; - - tx_view.on_stop = [this]() { - // Kill tx_thread here ? - tx_view.set_transmitting(false); - transmitter_model.disable(); - txing = false; - }; + NavigationView& nav) + : nav_{nav} { + baseband::run_image(portapack::spi_flash::image_tag_rds); + + add_children({ + &tab_view, + &labels, + &sym_pi_code, + &check_TP, + &options_pty, + &view_PSN, + &view_radiotext, + &view_datetime, + &view_audio, + &tx_view, + }); + + // load app settings + auto rc = settings.load("tx_rds", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + check_TP.set_value(true); + + sym_pi_code.set_sym(0, 0xF); + sym_pi_code.set_sym(1, 0x3); + sym_pi_code.set_sym(2, 0xE); + sym_pi_code.set_sym(3, 0x0); + sym_pi_code.on_change = [this]() { + rds_flags.PI_code = sym_pi_code.value_hex_u64(); + }; + + options_pty.set_selected_index(0); // None + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + txing = true; + }; + + tx_view.on_stop = [this]() { + // Kill tx_thread here ? + tx_view.set_transmitting(false); + transmitter_model.disable(); + txing = false; + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_rds.hpp b/firmware/application/apps/ui_rds.hpp index bbffb1ed9..09798187d 100644 --- a/firmware/application/apps/ui_rds.hpp +++ b/firmware/application/apps/ui_rds.hpp @@ -32,311 +32,293 @@ using namespace rds; namespace ui { class RDSPSNView : public OptionTabView { -public: - RDSPSNView(NavigationView& nav, Rect parent_rect); - - std::string PSN { "TEST1234" }; - bool mono_stereo { false }; - bool TA { false }; - bool MS { false }; - -private: - Labels labels { - { { 1 * 8, 3 * 8 }, "Program Service Name", Color::light_grey() }, - { { 2 * 8, 7 * 8 }, "PSN:", Color::light_grey() } - }; - - Button button_set { - { 18 * 8, 3 * 16, 80, 32 }, - "Set" - }; - Text text_psn { - { 6 * 8, 3 * 16 + 8, 8 * 8, 16 }, - "" - }; - - Checkbox check_mono_stereo { - { 2 * 8, 12 * 8 }, - 6, - "Stereo" - }; - Checkbox check_MS { - { 14 * 8, 12 * 8 }, - 5, - "Music" - }; - Checkbox check_TA { - { 2 * 8, 16 * 8 }, - 20, - "Traffic announcement" - }; + public: + RDSPSNView(NavigationView& nav, Rect parent_rect); + + std::string PSN{"TEST1234"}; + bool mono_stereo{false}; + bool TA{false}; + bool MS{false}; + + private: + Labels labels{ + {{1 * 8, 3 * 8}, "Program Service Name", Color::light_grey()}, + {{2 * 8, 7 * 8}, "PSN:", Color::light_grey()}}; + + Button button_set{ + {18 * 8, 3 * 16, 80, 32}, + "Set"}; + Text text_psn{ + {6 * 8, 3 * 16 + 8, 8 * 8, 16}, + ""}; + + Checkbox check_mono_stereo{ + {2 * 8, 12 * 8}, + 6, + "Stereo"}; + Checkbox check_MS{ + {14 * 8, 12 * 8}, + 5, + "Music"}; + Checkbox check_TA{ + {2 * 8, 16 * 8}, + 20, + "Traffic announcement"}; }; class RDSRadioTextView : public OptionTabView { -public: - RDSRadioTextView(NavigationView& nav, Rect parent_rect); - - std::string radiotext { "Radiotext test ABCD1234" }; -private: - Labels labels { - { { 2 * 8, 3 * 8 }, "Radiotext", Color::light_grey() }, - { { 1 * 8, 6 * 8 }, "Text:", Color::light_grey() } - }; - - Text text_radiotext { - { 1 * 8, 4 * 16, 28 * 8, 16 }, - "-" - }; - Button button_set { - { 88, 6 * 16, 64, 32 }, - "Set" - }; + public: + RDSRadioTextView(NavigationView& nav, Rect parent_rect); + + std::string radiotext{"Radiotext test ABCD1234"}; + + private: + Labels labels{ + {{2 * 8, 3 * 8}, "Radiotext", Color::light_grey()}, + {{1 * 8, 6 * 8}, "Text:", Color::light_grey()}}; + + Text text_radiotext{ + {1 * 8, 4 * 16, 28 * 8, 16}, + "-"}; + Button button_set{ + {88, 6 * 16, 64, 32}, + "Set"}; }; class RDSDateTimeView : public OptionTabView { -public: - RDSDateTimeView(Rect parent_rect); - -private: - Labels labels { - { { 44, 5 * 16 }, "Not yet implemented", Color::red() } - }; + public: + RDSDateTimeView(Rect parent_rect); + + private: + Labels labels{ + {{44, 5 * 16}, "Not yet implemented", Color::red()}}; }; class RDSAudioView : public OptionTabView { -public: - RDSAudioView(Rect parent_rect); - -private: - Labels labels { - { { 44, 5 * 16 }, "Not yet implemented", Color::red() } - }; + public: + RDSAudioView(Rect parent_rect); + + private: + Labels labels{ + {{44, 5 * 16}, "Not yet implemented", Color::red()}}; }; class RDSThread { -public: - RDSThread(std::vector** frames); - ~RDSThread(); - - RDSThread(const RDSThread&) = delete; - RDSThread(RDSThread&&) = delete; - RDSThread& operator=(const RDSThread&) = delete; - RDSThread& operator=(RDSThread&&) = delete; - -private: - std::vector** frames_ { }; - Thread* thread { nullptr }; - - static msg_t static_fn(void* arg); - - void run(); + public: + RDSThread(std::vector** frames); + ~RDSThread(); + + RDSThread(const RDSThread&) = delete; + RDSThread(RDSThread&&) = delete; + RDSThread& operator=(const RDSThread&) = delete; + RDSThread& operator=(RDSThread&&) = delete; + + private: + std::vector** frames_{}; + Thread* thread{nullptr}; + + static msg_t static_fn(void* arg); + + void run(); }; class RDSView : public View { -public: - RDSView(NavigationView& nav); - ~RDSView(); - - RDSView(const RDSView&) = delete; - RDSView(RDSView&&) = delete; - RDSView& operator=(const RDSView&) = delete; - RDSView& operator=(RDSView&&) = delete; - - void focus() override; - - std::string title() const override { return "RDS TX"; }; - -private: - NavigationView& nav_; - RDS_flags rds_flags { }; - - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - - std::vector frame_psn { }; - std::vector frame_radiotext { }; - std::vector frame_datetime { }; - std::vector* frames[3] { &frame_psn, &frame_radiotext, &frame_datetime }; - - bool txing = false; - - uint16_t message_length { 0 }; - - void start_tx(); - - Rect view_rect = { 0, 8 * 8, 240, 192 }; - - RDSPSNView view_PSN { nav_, view_rect }; - RDSRadioTextView view_radiotext { nav_, view_rect }; - RDSDateTimeView view_datetime { view_rect }; - RDSAudioView view_audio { view_rect }; - - TabView tab_view { - { "Name", Color::cyan(), &view_PSN }, - { "Text", Color::green(), &view_radiotext }, - { "Time", Color::yellow(), &view_datetime }, - { "Audio", Color::orange(), &view_audio } - }; - - Labels labels { - { { 0 * 8, 28 }, "Program type:", Color::light_grey() }, - //{ { 14 * 8, 16 + 8 }, "CC:", Color::light_grey() }, - { { 2 * 8, 28 + 16 }, "Program ID:", Color::light_grey() }, - //{ { 13 * 8, 32 + 8 }, "Cov:", Color::light_grey() }, - }; - - OptionsField options_pty { - { 13 * 8, 28 }, - 8, - { - { "None", 0 }, - { "News", 1 }, - { "Affairs", 2 }, - { "Info", 3 }, - { "Sport", 4 }, - { "Educate", 5 }, - { "Drama", 6 }, - { "Culture", 7 }, - { "Science", 8 }, - { "Varied", 9 }, - { "Pop", 10 }, - { "Rock", 11 }, - { "Easy", 12 }, - { "Light", 13 }, - { "Classics", 14 }, - { "Other", 15 }, - { "Weather", 16 }, - { "Finance", 17 }, - { "Children", 18 }, - { "Social", 19 }, - { "Religion", 20 }, - { "PhoneIn", 21 }, - { "Travel", 22 }, - { "Leisure", 23 }, - { "Jazz", 24 }, - { "Country", 25 }, - { "National", 26 }, - { "Oldies", 27 }, - { "Folk", 28 }, - { "Docs", 29 }, - { "AlarmTst", 30 }, - { "Alarm", 31 } - } - }; - - /*OptionsField options_countrycode { - { 17 * 8, 16 + 8 }, - 11, - { - { "Albania", 9 }, - { "Algeria", 2 }, - { "Andorra", 3 }, - { "Austria", 10 }, - { "Azores", 8 }, - { "Belgium", 6 }, - { "Belarus", 15 }, - { "Bosnia", 15 }, - { "Bulgaria", 8 }, - { "Canaries", 14 }, - { "Croatia", 12 }, - { "Cyprus", 2 }, - { "Czech-Rep", 2 }, - { "Denmark", 9 }, - { "Egypt", 15 }, - { "Estonia", 2 }, - { "Faroe", 9 }, - { "Finland", 6 }, - { "France", 15 }, - { "Germany 1", 13 }, - { "Germany 2", 1 }, - { "Gibraltar", 10 }, - { "Greece", 1 }, - { "Hungary", 11 }, - { "Iceland", 10 }, - { "Iraq", 11 }, - { "Ireland", 2 }, - { "Israel", 4 }, - { "Italy", 5 }, - { "Jordan", 5 }, - { "Latvia", 9 }, - { "Lebanon", 10 }, - { "Libya", 13 }, - { "Liechtenst.", 9 }, - { "Lithuania", 12 }, - { "Luxembourg", 7 }, - { "Macedonia", 4 }, - { "Madeira", 8 }, - { "Malta", 12 }, - { "Moldova", 1 }, - { "Monaco", 11 }, - { "Morocco", 1 }, - { "Netherlands", 8 }, - { "Norway", 15 }, - { "Palestine", 8 }, - { "Poland", 3 }, - { "Portugal", 8 }, - { "Romania", 14 }, - { "Russia", 7 }, - { "San Marino", 3 }, - { "Slovakia", 5 }, - { "Slovenia", 9 }, - { "Spain", 14 }, - { "Sweden", 14 }, - { "Switzerland", 4 }, - { "Syria", 6 }, - { "Tunisia", 7 }, - { "Turkey", 3 }, - { "Ukraine", 6 }, - { "U.K.", 12 }, - { "Vatican", 4 }, - { "Yugoslavia", 13 } - } - };*/ - - SymField sym_pi_code { - { 13 * 8, 28 + 16 }, - 4, - SymField::SYMFIELD_HEX - }; - - /*OptionsField options_coverage { - { 17 * 8, 32 + 8 }, - 12, - { - { "Local", 0 }, - { "Internat.", 1 }, - { "National", 2 }, - { "Sup-regional", 3 }, - { "R11", 4 }, - { "R12", 5 }, - { "R13", 6 }, - { "R14", 7 }, - { "R15", 8 }, - { "R16", 9 }, - { "R17", 10 }, - { "R18", 11 }, - { "R19", 12 }, - { "R110", 13 }, - { "R111", 14 }, - { "R112", 15 } - } - };*/ - - Checkbox check_TP { - { 23 * 8, 4 * 8 }, - 2, - "TP" - }; - - TransmitterView tx_view { - 16 * 16, - 50000, - 9 - }; - - std::unique_ptr tx_thread { }; + public: + RDSView(NavigationView& nav); + ~RDSView(); + + RDSView(const RDSView&) = delete; + RDSView(RDSView&&) = delete; + RDSView& operator=(const RDSView&) = delete; + RDSView& operator=(RDSView&&) = delete; + + void focus() override; + + std::string title() const override { return "RDS TX"; }; + + private: + NavigationView& nav_; + RDS_flags rds_flags{}; + + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + std::vector frame_psn{}; + std::vector frame_radiotext{}; + std::vector frame_datetime{}; + std::vector* frames[3]{&frame_psn, &frame_radiotext, &frame_datetime}; + + bool txing = false; + + uint16_t message_length{0}; + + void start_tx(); + + Rect view_rect = {0, 8 * 8, 240, 192}; + + RDSPSNView view_PSN{nav_, view_rect}; + RDSRadioTextView view_radiotext{nav_, view_rect}; + RDSDateTimeView view_datetime{view_rect}; + RDSAudioView view_audio{view_rect}; + + TabView tab_view{ + {"Name", Color::cyan(), &view_PSN}, + {"Text", Color::green(), &view_radiotext}, + {"Time", Color::yellow(), &view_datetime}, + {"Audio", Color::orange(), &view_audio}}; + + Labels labels{ + {{0 * 8, 28}, "Program type:", Color::light_grey()}, + //{ { 14 * 8, 16 + 8 }, "CC:", Color::light_grey() }, + {{2 * 8, 28 + 16}, "Program ID:", Color::light_grey()}, + //{ { 13 * 8, 32 + 8 }, "Cov:", Color::light_grey() }, + }; + + OptionsField options_pty{ + {13 * 8, 28}, + 8, + {{"None", 0}, + {"News", 1}, + {"Affairs", 2}, + {"Info", 3}, + {"Sport", 4}, + {"Educate", 5}, + {"Drama", 6}, + {"Culture", 7}, + {"Science", 8}, + {"Varied", 9}, + {"Pop", 10}, + {"Rock", 11}, + {"Easy", 12}, + {"Light", 13}, + {"Classics", 14}, + {"Other", 15}, + {"Weather", 16}, + {"Finance", 17}, + {"Children", 18}, + {"Social", 19}, + {"Religion", 20}, + {"PhoneIn", 21}, + {"Travel", 22}, + {"Leisure", 23}, + {"Jazz", 24}, + {"Country", 25}, + {"National", 26}, + {"Oldies", 27}, + {"Folk", 28}, + {"Docs", 29}, + {"AlarmTst", 30}, + {"Alarm", 31}}}; + + /*OptionsField options_countrycode { + { 17 * 8, 16 + 8 }, + 11, + { + { "Albania", 9 }, + { "Algeria", 2 }, + { "Andorra", 3 }, + { "Austria", 10 }, + { "Azores", 8 }, + { "Belgium", 6 }, + { "Belarus", 15 }, + { "Bosnia", 15 }, + { "Bulgaria", 8 }, + { "Canaries", 14 }, + { "Croatia", 12 }, + { "Cyprus", 2 }, + { "Czech-Rep", 2 }, + { "Denmark", 9 }, + { "Egypt", 15 }, + { "Estonia", 2 }, + { "Faroe", 9 }, + { "Finland", 6 }, + { "France", 15 }, + { "Germany 1", 13 }, + { "Germany 2", 1 }, + { "Gibraltar", 10 }, + { "Greece", 1 }, + { "Hungary", 11 }, + { "Iceland", 10 }, + { "Iraq", 11 }, + { "Ireland", 2 }, + { "Israel", 4 }, + { "Italy", 5 }, + { "Jordan", 5 }, + { "Latvia", 9 }, + { "Lebanon", 10 }, + { "Libya", 13 }, + { "Liechtenst.", 9 }, + { "Lithuania", 12 }, + { "Luxembourg", 7 }, + { "Macedonia", 4 }, + { "Madeira", 8 }, + { "Malta", 12 }, + { "Moldova", 1 }, + { "Monaco", 11 }, + { "Morocco", 1 }, + { "Netherlands", 8 }, + { "Norway", 15 }, + { "Palestine", 8 }, + { "Poland", 3 }, + { "Portugal", 8 }, + { "Romania", 14 }, + { "Russia", 7 }, + { "San Marino", 3 }, + { "Slovakia", 5 }, + { "Slovenia", 9 }, + { "Spain", 14 }, + { "Sweden", 14 }, + { "Switzerland", 4 }, + { "Syria", 6 }, + { "Tunisia", 7 }, + { "Turkey", 3 }, + { "Ukraine", 6 }, + { "U.K.", 12 }, + { "Vatican", 4 }, + { "Yugoslavia", 13 } + } + };*/ + + SymField sym_pi_code{ + {13 * 8, 28 + 16}, + 4, + SymField::SYMFIELD_HEX}; + + /*OptionsField options_coverage { + { 17 * 8, 32 + 8 }, + 12, + { + { "Local", 0 }, + { "Internat.", 1 }, + { "National", 2 }, + { "Sup-regional", 3 }, + { "R11", 4 }, + { "R12", 5 }, + { "R13", 6 }, + { "R14", 7 }, + { "R15", 8 }, + { "R16", 9 }, + { "R17", 10 }, + { "R18", 11 }, + { "R19", 12 }, + { "R110", 13 }, + { "R111", 14 }, + { "R112", 15 } + } + };*/ + + Checkbox check_TP{ + {23 * 8, 4 * 8}, + 2, + "TP"}; + + TransmitterView tx_view{ + 16 * 16, + 50000, + 9}; + + std::unique_ptr tx_thread{}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon.cpp b/firmware/application/apps/ui_recon.cpp index 6f705fa2c..02432751b 100644 --- a/firmware/application/apps/ui_recon.cpp +++ b/firmware/application/apps/ui_recon.cpp @@ -29,1527 +29,1316 @@ using portapack::memory::map::backup_ram; namespace ui { - bool ReconView::ReconSaveFreq( const std::string &freq_file_path , size_t freq_index , bool warn_if_exists ) - { - File recon_file; - - if( frequency_list.size() == 0 || ( frequency_list.size() && current_index > (int32_t)frequency_list.size() ) ) - return false ; - - freqman_entry entry = frequency_list[ freq_index ] ; - entry . frequency_a = freq ; - entry . frequency_b = 0 ; - entry . modulation = last_entry.modulation ; - entry . bandwidth = last_entry.bandwidth ; - entry . type = SINGLE ; - - std::string frequency_to_add ; - get_freq_string( entry , frequency_to_add ); - - auto result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - char one_char[1]; //Read it char by char - std::string line; //and put read line in here - bool found=false; - for (size_t pointer=0; pointer < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_file.read(one_char, 1); - if ((int)one_char[0] > 31) { //ascii space upwards - line += one_char[0]; //add it to the textline - } - else if (one_char[0] == '\n') { //New Line - if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { - found=true; - break; - } - line.clear(); //Ready for next textline - } - } - if( !found) { - result = recon_file.append(freq_file_path); //Second: append if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - if(found && warn_if_exists) - { - nav_.display_modal("Error", "Frequency already exists"); - } - } - else { - result = recon_file.create( freq_file_path ); //First freq if no output file - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - return true ; - } - - bool ReconView::ReconSetupLoadStrings( const std::string &source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ) - { - File settings_file; - size_t length, file_position = 0; - char * pos; - char * line_start; - char * line_end; - char file_data[257]; - - uint32_t it = 0 ; - uint32_t nb_params = RECON_SETTINGS_NB_PARAMS ; - std::string params[ RECON_SETTINGS_NB_PARAMS ]; - - bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ; - - if( check_sd_card ) - { - auto result = settings_file.open( source ); - if( !result.is_valid() ) - { - while( it < nb_params ) - { - // Read a 256 bytes block from file - settings_file.seek(file_position); - memset(file_data, 0, 257); - auto read_size = settings_file.read(file_data, 256); - if (read_size.is_error()) - break ; - file_position += 256; - // Reset line_start to beginning of buffer - line_start = file_data; - pos=line_start; - while ((line_end = strstr(line_start, "\x0A"))) { - length = line_end - line_start - 1 ; - params[ it ] = string( pos , length ); - it ++ ; - line_start = line_end + 1; - pos=line_start ; - if (line_start - file_data >= 256) - break; - if( it >= nb_params ) - break ; - } - if (read_size.value() != 256) - break; // End of file - - // Restart at beginning of last incomplete line - file_position -= (file_data + 256 - line_start); - } - } - } - - if( it < nb_params ) - { - /* bad number of params, signal defaults */ - input_file = "RECON" ; - output_file = "RECON_RESULTS" ; - recon_lock_duration = RECON_MIN_LOCK_DURATION ; - recon_lock_nb_match = RECON_DEF_NB_MATCH ; - recon_squelch_level = -14 ; - recon_match_mode = RECON_MATCH_CONTINUOUS ; - wait = RECON_DEF_WAIT_DURATION ; - volume = 40 ; - return false ; - } - - if( it > 0 ) - input_file = params[ 0 ]; - else - input_file = "RECON" ; - - if( it > 1 ) - output_file= params[ 1 ]; - else - output_file = "RECON_RESULTS" ; - - if( it > 2 ) - recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); - else - recon_lock_duration = RECON_MIN_LOCK_DURATION ; - - if( it > 3 ) - recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); - else - recon_lock_nb_match = RECON_DEF_NB_MATCH ; - - if( it > 4 ) - recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 ); - else - recon_squelch_level = -14 ; - - if( it > 5 ) - recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 ); - else - recon_match_mode = RECON_MATCH_CONTINUOUS ; - - if( it > 6 ) - wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); - else - wait = RECON_DEF_WAIT_DURATION ; - - if( it > 7 ) - volume = strtoll( params[ 7 ].c_str() , nullptr , 10 ); - else - volume = 40 ; - - return true ; - } - - bool ReconView::ReconSetupSaveStrings( const std::string &dest, const std::string &input_file , const std::string &output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ) - { - File settings_file; - - auto result = settings_file.create( dest ); - if( result.is_valid() ) - return false ; - settings_file.write_line( input_file ); - settings_file.write_line( output_file ); - settings_file.write_line( to_string_dec_uint( recon_lock_duration ) ); - settings_file.write_line( to_string_dec_uint( recon_lock_nb_match ) ); - settings_file.write_line( to_string_dec_int( recon_squelch_level ) ); - settings_file.write_line( to_string_dec_uint( recon_match_mode ) ); - settings_file.write_line( to_string_dec_int( wait ) ); - settings_file.write_line( to_string_dec_int( volume ) ); - return true ; - } - - void ReconView::audio_output_start() - { - audio::output::start(); - this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); - } - - bool ReconView::check_sd_card() - { - return (sd_card::status() == sd_card::Status::Mounted) ? true : false; - } - - void ReconView::recon_redraw() { - if( last_rssi_min != rssi.get_min() || last_rssi_med != rssi.get_avg() || last_rssi_max != rssi.get_max() ) - { - last_rssi_min = rssi.get_min(); - last_rssi_med = rssi.get_avg(); - last_rssi_max = rssi.get_max(); - freq_stats.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+" db" ); - } - if( last_entry . frequency_a != freq ) - { - last_entry . frequency_a = freq ; - big_display.set( "FREQ: "+to_string_short_freq( freq )+" MHz" ); - } - if( last_db != db || last_list_size != frequency_list.size() || last_freq_lock != freq_lock || last_nb_match != recon_lock_nb_match ) - { - last_freq_lock = freq ; - last_list_size = frequency_list.size(); - last_db = db ; - last_nb_match = recon_lock_nb_match ; - text_max.set( "/" + to_string_dec_uint( frequency_list.size() ) + " " + to_string_dec_int( db ) + " db " + to_string_dec_uint( freq_lock ) + "/" + to_string_dec_uint( recon_lock_nb_match ) ); - if( freq_lock == 0 ) { - //NO FREQ LOCK, ONGOING STANDARD SCANNING - big_display.set_style(&style_white); - if( !userpause ) - button_pause.set_text(""); - else - button_pause.set_text(""); - } - else if( freq_lock == 1 && recon_lock_nb_match != 1 ) - { - //STARTING LOCK FREQ - big_display.set_style(&style_yellow); - button_pause.set_text(""); - } - else if( freq_lock >= recon_lock_nb_match ) - { - big_display.set_style( &style_green); - button_pause.set_text(""); - - //FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() - if( (!scanner_mode) && autosave && frequency_list.size() > 0 ) { - ReconSaveFreq( freq_file_path , current_index , false ); - } - } - } - } - - void ReconView::handle_retune() { - if( last_freq != freq ) - { - last_freq = freq ; - receiver_model.set_tuning_frequency( freq ); // Retune - } - if( frequency_list.size() > 0 ) - { - if( last_entry . modulation != frequency_list[ current_index ] . modulation && frequency_list[ current_index ] . modulation >= 0 ) - { - last_entry . modulation = frequency_list[ current_index ]. modulation; - field_mode.set_selected_index( frequency_list[ current_index ]. modulation ); - } - // Set bandwidth if any - if( last_entry . bandwidth != frequency_list[ current_index ] . bandwidth && frequency_list[ current_index ] . bandwidth >= 0 ) - { - last_entry . bandwidth = frequency_list[ current_index ]. bandwidth; - switch( frequency_list[ current_index ].modulation ) - { - case AM_MODULATION: - receiver_model.set_am_configuration( freq ); - break ; - case NFM_MODULATION: - receiver_model.set_nbfm_configuration( freq ); - break ; - case WFM_MODULATION: - receiver_model.set_wfm_configuration( freq ); - default: - break ; - } - field_bw.set_selected_index( freq ); - } - if( last_entry . step != frequency_list[ current_index ] . step && frequency_list[ current_index ] . step >= 0 ) - { - last_entry . step = frequency_list[ current_index ]. step ; - step = freqman_entry_get_step_value( last_entry . step ); - step_mode.set_selected_index( step ); - } - if( last_index != current_index ) - { - last_index = current_index ; - if( (int32_t)frequency_list.size() && current_index < (int32_t)frequency_list.size() && frequency_list[ current_index ] . type == RANGE ) - { - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - if(frequency_list[current_index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); //Show new description - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); //Show new description - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); //Show new description - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); //Show new description - } - } - } - } - - - void ReconView::focus() { - button_pause.focus(); - } - - ReconView::~ReconView() { - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); - - // save app settings - settings.save("recon", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); - } - - - ReconView::ReconView( NavigationView& nav) : nav_ { nav } { - - add_children( { - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &field_volume, - &field_bw, - &field_squelch, - &field_wait, - &field_lock_wait, - &button_recon_setup, - &button_scanner_mode, - &button_looking_glass, - &file_name, - &rssi, - &text_cycle, - &text_max, - &desc_cycle, - &big_display, - &freq_stats, - &text_timer, - &text_ctcss, - &button_manual_start, - &button_manual_end, - &button_manual_recon, - &field_mode, - &step_mode, - &button_pause, - &button_audio_app, - &button_add, - &button_dir, - &button_restart, - &button_mic_app, - &button_remove - } ); - - // Recon directory - if( check_sd_card() ) { // Check to see if SD Card is mounted - make_new_directory( u"/RECON" ); - sd_card_mounted = true ; - } - def_step = 0 ; - //HELPER: Pre-setting a manual range, based on stored frequency - rf::Frequency stored_freq = persistent_memory::tuned_frequency(); - receiver_model.set_tuning_frequency( stored_freq ); - if( stored_freq - OneMHz > 0 ) - frequency_range.min = stored_freq - OneMHz ; - else - frequency_range.min = 0 ; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - if( stored_freq + OneMHz < MAX_UFREQ ) - frequency_range.max = stored_freq + OneMHz ; - else - frequency_range.max = MAX_UFREQ ; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - // Loading settings - autostart = persistent_memory::recon_autostart_recon(); - autosave = persistent_memory::recon_autosave_freqs(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - - field_volume.set_value( volume ); - if( sd_card_mounted ) - { - // load auto common app settings - auto rc = settings.load("recon", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - receiver_model.set_rf_amp(app_settings.rx_amp); - } - } - - button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) { - auto new_view = nav_.push(frequency_range.min); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.min = f; - button_manual_start.set_text(to_string_short_freq(f)); - }; - }; - - button_manual_end.on_select = [this, &nav](ButtonWithEncoder& button) { - auto new_view = nav.push(frequency_range.max); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.max = f; - button_manual_end.set_text(to_string_short_freq(f)); - }; - }; - - text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { - if( frequency_list.size() > 0 ) - { - auto new_view = nav_.push(current_index); - new_view->on_changed = [this, &button](rf::Frequency f) { - f = f / OneMHz ; - if( f >= 1 && f <= frequency_list.size() ) - { - index_stepper = f - 1 - current_index ; - freq_lock = 0 ; - } - }; - } - }; - - button_manual_start.on_change = [this]() { - frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.min < 0 ) - { - frequency_range.min = 0 ; - } - if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - } - if( frequency_range.min > (frequency_range.max - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.max = frequency_range.min + freqman_entry_get_step_value( def_step ); - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - frequency_range.max = MAX_UFREQ ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_start.set_encoder_delta( 0 ); - }; - - button_manual_end.on_change = [this]() { - frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ); - } - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.max = MAX_UFREQ ; - } - if( frequency_range.max < (frequency_range.min + freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = frequency_range.max - freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.min = 1 ; - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ) ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_end.set_encoder_delta( 0 ); - }; - - text_cycle.on_change = [this]() { - on_index_delta( text_cycle.get_encoder_delta() ); - text_cycle.set_encoder_delta( 0 ); - }; - - button_pause.on_select = [this](ButtonWithEncoder&) { - if( frequency_list.size() > 0 ) - { - if( freq_lock > 0 ) - { - if( fwd ) - { - on_stepper_delta( 1 ); - } - else - { - on_stepper_delta( -1 ); - } - button_pause.set_text(""); //Show button for non continuous stop - } - else - { - if( userpause ) - { - recon_resume(); - } - else - { - recon_pause(); - } - } - } - }; - - button_pause.on_change = [this]() { - on_stepper_delta( button_pause.get_encoder_delta() ); - button_pause.set_encoder_delta( 0 ); - }; - - button_audio_app.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - button_looking_glass.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - - rssi.set_focusable(true); - rssi.set_peak( true , 500 ); - rssi.on_select = [this](RSSI&) { - nav_.pop(); - nav_.push(); - }; - - button_mic_app.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - button_remove.on_select = [this](ButtonWithEncoder&) { - if(frequency_list.size() > 0 ) - { - if( scanner_mode ) - { - if( current_index >= (int32_t)frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - frequency_list.erase( frequency_list.begin()+ current_index ); - if( current_index >= (int32_t)frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - if( frequency_list.size() > 0 ) - { - if(frequency_list[current_index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - - File freqman_file; - - delete_file( freq_file_path ); - - auto result = freqman_file.create( freq_file_path ); - if ( !result.is_valid() ) - { - for (size_t n = 0; n < frequency_list.size(); n++) - { - std::string line ; - get_freq_string( frequency_list[ n ] , line ); - freqman_file.write_line( line ); - } - } - } - } - else // RECON MODE / MANUAL, only remove matching from output - { - File recon_file; - File tmp_recon_file; - std::string tmp_freq_file_path = freq_file_path+".TMP" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = freq ; - entry . frequency_b = 0 ; - entry . modulation = last_entry.modulation ; - entry . bandwidth = last_entry.bandwidth ; - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - delete_file( tmp_freq_file_path ); - auto result = tmp_recon_file.create(tmp_freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - bool found=false; - result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - char one_char[1]; //Read it char by char - std::string line; //and put read line in here - for (size_t pointer=0; pointer < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_file.read(one_char, 1); - if ((int)one_char[0] > 31) { //ascii space upwards - line += one_char[0]; //add it to the textline - } - else if (one_char[0] == '\n') { //new Line - if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { - found=true; - } - else - { - tmp_recon_file.write_line( frequency_to_add ); - } - line.clear(); // ready for next textline - } - } - if( found) - { - delete_file( freq_file_path ); - rename_file( tmp_freq_file_path , freq_file_path ); - } - else - { - delete_file( tmp_freq_file_path ); - } - } - } - } - receiver_model.set_tuning_frequency( frequency_list[ current_index ] . frequency_a ); // retune - } - if( frequency_list.size() == 0 ) - { - text_cycle.set_text( " " ); - desc_cycle.set( "no entries in list" ); //Show new description - delete_file( freq_file_path ); - } - timer = 0 ; - freq_lock = 0 ; - }; - - button_remove.on_change = [this]() { - on_stepper_delta( button_remove.get_encoder_delta() ); - button_remove.set_encoder_delta( 0 ); - }; - - button_manual_recon.on_select = [this](Button&) { - scanner_mode = false ; - manual_mode = true ; - recon_pause(); - - if (!frequency_range.min || !frequency_range.max) { - nav_.display_modal("Error", "Both START and END freqs\nneed a value"); - } else if (frequency_range.min > frequency_range.max) { - nav_.display_modal("Error", "END freq\nis lower than START"); - } else { - audio::output::stop(); - - frequency_list.clear(); - freqman_entry manual_freq_entry ; - - def_step = step_mode.selected_index(); // max range val - - manual_freq_entry . type = RANGE ; - manual_freq_entry . description = - "R " + to_string_short_freq(frequency_range.min) + ">" - + to_string_short_freq(frequency_range.max) + " S" // current Manual range - + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0,1) ; //euquiq: lame kludge to reduce spacing in step freq - manual_freq_entry . frequency_a = frequency_range.min ; // min range val - manual_freq_entry . frequency_b = frequency_range.max ; // max range val - manual_freq_entry . modulation = -1 ; - manual_freq_entry . bandwidth = -1 ; - manual_freq_entry . step = def_step ; - - frequency_list . push_back( manual_freq_entry ); - - big_display.set_style(&style_white); //Back to white color - - freq_stats.set_style(&style_white); - freq_stats.set( "0/0/0" ); - - text_cycle.set_text( "1" ); - text_max.set( "/1" ); - button_scanner_mode.set_style( &style_white ); - button_scanner_mode.set_text( "MSEARCH" ); - file_name.set_style( &style_white ); - file_name.set( "MANUAL RANGE RECON" ); - desc_cycle.set_style( &style_white ); - desc_cycle.set( "MANUAL RANGE RECON" ); - - current_index = 0 ; - freq = manual_freq_entry . frequency_a ; - handle_retune(); - recon_redraw(); - recon_resume(); - } - }; - - button_dir.on_select = [this](Button&) { - if( fwd ) - { - fwd = false ; - button_dir.set_text( "" ); - } - timer = 0 ; - if ( userpause ) - recon_resume(); - }; - - button_restart.on_select = [this](Button&) { - if( frequency_list.size() > 0 ) - { - def_step = step_mode.selected_index(); //Use def_step from manual selector - frequency_file_load( true ); - if( fwd ) - { - button_dir.set_text( "FW>" ); - } - else - { - button_dir.set_text( "(input_file,output_file,recon_lock_duration,recon_lock_nb_match,recon_match_mode); - open_view -> on_changed = [this](std::vector result) { - input_file = result[0]; - output_file = result[1]; - freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - recon_lock_duration = strtol( result[2].c_str() , nullptr , 10 ); - recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 ); - recon_match_mode = strtol( result[4].c_str() , nullptr , 10 ); - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); - - autosave = persistent_memory::recon_autosave_freqs(); - autostart = persistent_memory::recon_autostart_recon(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - - field_lock_wait.set_value(recon_lock_duration); - - frequency_file_load( false ); - if( autostart ) - { - recon_resume(); - } - else - { - recon_pause(); - } - if( userpause != true ) - { - recon_resume(); - } - }; - }; - - field_wait.on_change = [this](int32_t v) - { - wait = v ; - if( wait == 0 ) - { - field_wait.set_style( &style_blue ); - } - else if( wait >= 500 ) - { - field_wait.set_style(&style_white); - } - else if( wait > -500 && wait < 500 ) - { - field_wait.set_style( &style_red ); - } - else if( wait <= -500 ) - { - field_wait.set_style( &style_green ); - } - }; - - field_lock_wait.on_change = [this](uint32_t v) - { - recon_lock_duration = v ; - if( recon_match_mode == RECON_MATCH_CONTINUOUS ) - { - if( (v / STATS_UPDATE_INTERVAL ) > recon_lock_nb_match ) - { - field_lock_wait.set_style( &style_white ); - } - else if( (v / STATS_UPDATE_INTERVAL ) == recon_lock_nb_match ) - { - field_lock_wait.set_style(&style_yellow); - } - } - else // RECON_MATCH_SPARSE - { - field_lock_wait.set_style( &style_white ); - } - }; - - field_squelch.on_change = [this](int32_t v) { - squelch = v ; - }; - - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - //PRE-CONFIGURATION: - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - file_name.set( "=>" ); - - //Loading input and output file from settings - ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , volume ); - freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - - field_squelch.set_value( squelch ); - field_wait.set_value(wait); - field_lock_wait.set_value(recon_lock_duration); - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - - // fill modulation and step options - freqman_set_modulation_option( field_mode ); - freqman_set_step_option( step_mode ); - - // set radio - change_mode(AM_MODULATION); // start on AM. - field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // first tune - - if( filedelete ) - { - delete_file( freq_file_path ); - } - - frequency_file_load( false ); /* do not stop all at start */ - if( autostart ) - { - recon_resume(); - } - else - { - recon_pause(); - } - - recon_redraw(); - } - - void ReconView::frequency_file_load( bool stop_all_before) { - - (void)(stop_all_before); - audio::output::stop(); - - def_step = step_mode.selected_index(); // use def_step from manual selector - frequency_list.clear(); // clear the existing frequency list (expected behavior) - std::string file_input = input_file ; // default recon mode - if( scanner_mode ) - { - file_input = output_file ; - file_name.set_style( &style_red ); - button_scanner_mode.set_style( &style_red ); - button_scanner_mode.set_text( "SCANNER" ); - } - else - { - file_name.set_style( &style_blue ); - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - } - file_name.set_style( &style_white ); - desc_cycle.set_style( &style_white ); - if( !load_freqman_file_ex( file_input , frequency_list, load_freqs, load_ranges, load_hamradios ) ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set(" NO " + file_input + ".TXT FILE ..." ); - file_name.set( "=> NO DATA" ); - } - else - { - file_name.set( "=> "+file_input ); - if( frequency_list.size() == 0 ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set("/0 no entries in list" ); - file_name.set( "BadOrEmpty "+file_input ); - } - else - { - if( frequency_list.size() > FREQMAN_MAX_PER_FILE ) - { - file_name.set_style( &style_yellow ); - desc_cycle.set_style( &style_yellow ); - } - } - } - - if( frequency_list[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); - else - step = freqman_entry_get_step_value( def_step ); - - switch( frequency_list[ 0 ] . type ){ - case SINGLE: - freq = frequency_list[ 0 ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list[ 0 ] . frequency_a ; - maxfreq = frequency_list[ 0 ] . frequency_b ; - if( fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - if( frequency_list[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); - break; - case HAMRADIO: - minfreq = frequency_list[ 0 ] . frequency_a ; - maxfreq = frequency_list[ 0 ] . frequency_b ; - if( fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - break; - default: - break; - } - last_entry . modulation = -1 ; - last_entry . bandwidth = -1 ; - last_entry . step = -1 ; - - step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector - receiver_model.enable(); - receiver_model.set_squelch_level(0); - std::string description = "...no description..." ; - current_index = 0 ; - if( frequency_list.size() != 0 ) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - description = "R: " + frequency_list[current_index].description ; - break ; - case HAMRADIO: - description = "H: " + frequency_list[current_index].description ; - break ; - default: - case SINGLE: - description = "S: " + frequency_list[current_index].description ; - break ; - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - else - { - text_cycle.set_text( " " ); - } - desc_cycle.set( description ); - handle_retune(); - } - - void ReconView::on_statistics_update(const ChannelStatistics& statistics) { - db = statistics.max_db ; - if( !userpause ) - { - if( !timer ) - { - status = 0 ; - continuous_lock = false ; - freq_lock = 0 ; - timer = recon_lock_duration ; - big_display.set_style(&style_white); - } - if( freq_lock < recon_lock_nb_match ) // LOCKING - { - if( status != 1 ) - { - status = 1 ; - if( wait != 0 ) - { - audio::output::stop(); - } - } - if( db > squelch ) //MATCHING LEVEL - { - continuous_lock = true ; - freq_lock ++ ; - } - else - { - // continuous, direct cut it if not consecutive match after 1 first match - if( recon_match_mode == RECON_MATCH_CONTINUOUS ) - { - if( freq_lock > 0 ) - { - timer = 0 ; - continuous_lock = false ; - } - } - } - } - if( freq_lock >= recon_lock_nb_match ) // LOCKED - { - if( status != 2 ) - { - continuous_lock = false ; - status = 2 ; - if( wait != 0 ) - { - audio_output_start(); - } - if( wait >= 0 ) - { - timer = wait ; - } - } - if( wait < 0 ) - { - if( db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY - { - timer = abs( wait ); - } - } - } - } - if( last_timer != timer ) - { - last_timer = timer ; - text_timer.set( "TIMER: " + to_string_dec_int( timer ) ); - } - if( timer ) - { - if( !continuous_lock ) - timer -= STATS_UPDATE_INTERVAL ; - if( timer < 0 ) - { - timer = 0 ; - } - } - if( recon || stepper != 0 || index_stepper != 0 ) - { - if( !timer || stepper != 0 || index_stepper != 0 ) - { - //IF THERE IS A FREQUENCY LIST ... - if (frequency_list.size() > 0 ) - { - has_looped = false ; - entry_has_changed = false ; - - if( recon || stepper != 0 || index_stepper != 0 ) - { - if( index_stepper == 0 ) - { - /* we are doing a range */ - if( frequency_list[ current_index ] . type == RANGE ) { - if ( ( fwd && stepper == 0 ) || stepper > 0 ) { - //forward - freq += step ; - // if bigger than range max - if (freq > maxfreq ) { - // when going forward we already know that we can skip a whole range => two values in the list - current_index ++ ; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - freq -= step ; - // if lower than range min - if (freq < minfreq ) { - // when back we have to check one step at a time - current_index -- ; - entry_has_changed = true ; - // looping - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - } - else if( frequency_list[ current_index ] . type == SINGLE ) { - if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward - current_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - current_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - else if( frequency_list[ current_index ] . type == HAMRADIO ) - { - if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward - if( ( minfreq != maxfreq ) && freq == minfreq ) - { - freq = maxfreq ; - } - else - { - current_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - if( ( minfreq != maxfreq ) && freq == maxfreq ) - { - freq = minfreq ; - } - else - { - current_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - } - // set index to boundary if !continuous - if( has_looped && !continuous ) - { - entry_has_changed = true ; - /* prepare values for the next run, when user will resume */ - if( ( fwd && stepper == 0 ) || stepper > 0 ) - { - current_index = 0 ; - } - else if( ( !fwd && stepper == 0 ) || stepper < 0 ) - { - current_index = frequency_list.size() - 1 ; - } - } - } - else - { - current_index += index_stepper ; - if( current_index < 0 ) - current_index += frequency_list.size(); - if( (unsigned)current_index >= frequency_list.size() ) - current_index -= frequency_list.size() ; - - entry_has_changed = true ; - - if( !recon ) // for some motive, audio output gets stopped. - audio_output_start(); - } - // reload entry if changed - if( entry_has_changed ){ - timer = 0 ; - switch( frequency_list[ current_index ] . type ){ - case SINGLE: - freq = frequency_list[ current_index ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list[ current_index ] . frequency_a ; - maxfreq = frequency_list[ current_index ] . frequency_b ; - if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - case HAMRADIO: - minfreq = frequency_list[ current_index ] . frequency_a ; - maxfreq = frequency_list[ current_index ] . frequency_b ; - if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - default: - break; - } - } - // send a pause message with the right freq - if( has_looped && !continuous ) - { - recon_pause(); - } - index_stepper = 0 ; - if( stepper < 0 ) stepper ++ ; - if( stepper > 0 ) stepper -- ; - } // if( recon || stepper != 0 || index_stepper != 0 ) - }//if (frequency_list.size() > 0 ) - } /* on_statistic_updates */ - } - handle_retune(); - recon_redraw(); - } - - void ReconView::recon_pause() { - timer = 0 ; - freq_lock = 0 ; - userpause = true; - continuous_lock=false; - recon = false ; - - audio_output_start(); - - big_display.set_style(&style_white); - button_pause.set_text(""); //PAUSED, show resume - } - - void ReconView::recon_resume() { - timer = 0 ; - freq_lock = 0 ; - userpause = false; - continuous_lock = false ; - recon = true ; - - audio::output::stop(); - - big_display.set_style(&style_white); - button_pause.set_text(""); - } - - void ReconView::on_index_delta(int32_t v) - { - if( v > 0 ) - { - fwd = true ; - button_dir.set_text( "FW>" ); - } - else - { - - fwd = false ; - button_dir.set_text( " 0 ) - index_stepper = v ; - - freq_lock = 0 ; - timer = 0 ; - } - - void ReconView::on_stepper_delta(int32_t v) - { - if( v > 0 ) - { - fwd = true ; - button_dir.set_text( "FW>" ); - } - else - { - - fwd = false ; - button_dir.set_text( " 0 ) - stepper = v ; - - freq_lock = 0 ; - timer = 0 ; - } - - void ReconView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); - } - - size_t ReconView::change_mode( freqman_index_t new_mod ) { - - field_mode.on_change = [this](size_t , OptionsField::value_t) { }; - field_bw.on_change = [this](size_t , OptionsField::value_t) { }; - - receiver_model.disable(); - baseband::shutdown(); - - switch( new_mod ) { - case AM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw DSB (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - receiver_model.set_am_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; - receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - case NFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 16k (2) default - field_bw.set_selected_index(2); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); - break; - case WFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 200k (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - receiver_model.set_wfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - default: - break; - } - field_mode.set_selected_index( new_mod ); - field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v != -1 ) - { - change_mode(v); - } - }; - - if ( !recon ) //for some motive, audio output gets stopped. - audio::output::start(); //so if recon was stopped we resume audio - receiver_model.enable(); - - return freqman_entry_get_step_value( def_step ); - } - - void ReconView::handle_coded_squelch(const uint32_t value) { - float diff, min_diff = value; - size_t min_idx { 0 }; - size_t c; - - if( field_mode.selected_index() != NFM_MODULATION ) - { - text_ctcss.set(" "); - return ; - } - - // Find nearest match - for (c = 0; c < tone_keys.size(); c++) { - diff = abs(((float)value / 100.0) - tone_keys[c].second); - if (diff < min_diff) { - min_idx = c; - min_diff = diff; - } - } - - // Arbitrary confidence threshold - if( last_squelch_index < 0 || (unsigned)last_squelch_index != min_idx ) - { - last_squelch_index = min_idx ; - if (min_diff < 40) - text_ctcss.set("T: "+tone_keys[min_idx].first); - else - text_ctcss.set(" "); - } - } +bool ReconView::ReconSaveFreq(const std::string& freq_file_path, size_t freq_index, bool warn_if_exists) { + File recon_file; + + if (frequency_list.size() == 0 || (frequency_list.size() && current_index > (int32_t)frequency_list.size())) + return false; + + freqman_entry entry = frequency_list[freq_index]; + entry.frequency_a = freq; + entry.frequency_b = 0; + entry.modulation = last_entry.modulation; + entry.bandwidth = last_entry.bandwidth; + entry.type = SINGLE; + + std::string frequency_to_add; + get_freq_string(entry, frequency_to_add); + + auto result = recon_file.open(freq_file_path); // First recon if freq is already in txt + if (!result.is_valid()) { + char one_char[1]; // Read it char by char + std::string line; // and put read line in here + bool found = false; + for (size_t pointer = 0; pointer < recon_file.size(); pointer++) { + recon_file.seek(pointer); + recon_file.read(one_char, 1); + if ((int)one_char[0] > 31) { // ascii space upwards + line += one_char[0]; // add it to the textline + } else if (one_char[0] == '\n') { // New Line + if (line.compare(0, frequency_to_add.size(), frequency_to_add) == 0) { + found = true; + break; + } + line.clear(); // Ready for next textline + } + } + if (!found) { + result = recon_file.append(freq_file_path); // Second: append if it is not there + if (!result.is_valid()) { + recon_file.write_line(frequency_to_add); + } + } + if (found && warn_if_exists) { + nav_.display_modal("Error", "Frequency already exists"); + } + } else { + result = recon_file.create(freq_file_path); // First freq if no output file + if (!result.is_valid()) { + recon_file.write_line(frequency_to_add); + } + } + return true; +} + +bool ReconView::ReconSetupLoadStrings(const std::string& source, std::string& input_file, std::string& output_file, uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, int32_t& recon_squelch_level, uint32_t& recon_match_mode, int32_t& wait, int32_t& volume) { + File settings_file; + size_t length, file_position = 0; + char* pos; + char* line_start; + char* line_end; + char file_data[257]; + + uint32_t it = 0; + uint32_t nb_params = RECON_SETTINGS_NB_PARAMS; + std::string params[RECON_SETTINGS_NB_PARAMS]; + + bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false; + + if (check_sd_card) { + auto result = settings_file.open(source); + if (!result.is_valid()) { + while (it < nb_params) { + // Read a 256 bytes block from file + settings_file.seek(file_position); + memset(file_data, 0, 257); + auto read_size = settings_file.read(file_data, 256); + if (read_size.is_error()) + break; + file_position += 256; + // Reset line_start to beginning of buffer + line_start = file_data; + pos = line_start; + while ((line_end = strstr(line_start, "\x0A"))) { + length = line_end - line_start - 1; + params[it] = string(pos, length); + it++; + line_start = line_end + 1; + pos = line_start; + if (line_start - file_data >= 256) + break; + if (it >= nb_params) + break; + } + if (read_size.value() != 256) + break; // End of file + + // Restart at beginning of last incomplete line + file_position -= (file_data + 256 - line_start); + } + } + } + + if (it < nb_params) { + /* bad number of params, signal defaults */ + input_file = "RECON"; + output_file = "RECON_RESULTS"; + recon_lock_duration = RECON_MIN_LOCK_DURATION; + recon_lock_nb_match = RECON_DEF_NB_MATCH; + recon_squelch_level = -14; + recon_match_mode = RECON_MATCH_CONTINUOUS; + wait = RECON_DEF_WAIT_DURATION; + volume = 40; + return false; + } + + if (it > 0) + input_file = params[0]; + else + input_file = "RECON"; + + if (it > 1) + output_file = params[1]; + else + output_file = "RECON_RESULTS"; + + if (it > 2) + recon_lock_duration = strtoll(params[2].c_str(), nullptr, 10); + else + recon_lock_duration = RECON_MIN_LOCK_DURATION; + + if (it > 3) + recon_lock_nb_match = strtoll(params[3].c_str(), nullptr, 10); + else + recon_lock_nb_match = RECON_DEF_NB_MATCH; + + if (it > 4) + recon_squelch_level = strtoll(params[4].c_str(), nullptr, 10); + else + recon_squelch_level = -14; + + if (it > 5) + recon_match_mode = strtoll(params[5].c_str(), nullptr, 10); + else + recon_match_mode = RECON_MATCH_CONTINUOUS; + + if (it > 6) + wait = strtoll(params[6].c_str(), nullptr, 10); + else + wait = RECON_DEF_WAIT_DURATION; + + if (it > 7) + volume = strtoll(params[7].c_str(), nullptr, 10); + else + volume = 40; + + return true; +} + +bool ReconView::ReconSetupSaveStrings(const std::string& dest, const std::string& input_file, const std::string& output_file, uint32_t recon_lock_duration, uint32_t recon_lock_nb_match, int32_t recon_squelch_level, uint32_t recon_match_mode, int32_t wait, int32_t volume) { + File settings_file; + + auto result = settings_file.create(dest); + if (result.is_valid()) + return false; + settings_file.write_line(input_file); + settings_file.write_line(output_file); + settings_file.write_line(to_string_dec_uint(recon_lock_duration)); + settings_file.write_line(to_string_dec_uint(recon_lock_nb_match)); + settings_file.write_line(to_string_dec_int(recon_squelch_level)); + settings_file.write_line(to_string_dec_uint(recon_match_mode)); + settings_file.write_line(to_string_dec_int(wait)); + settings_file.write_line(to_string_dec_int(volume)); + return true; +} + +void ReconView::audio_output_start() { + audio::output::start(); + this->on_headphone_volume_changed((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); +} + +bool ReconView::check_sd_card() { + return (sd_card::status() == sd_card::Status::Mounted) ? true : false; +} + +void ReconView::recon_redraw() { + if (last_rssi_min != rssi.get_min() || last_rssi_med != rssi.get_avg() || last_rssi_max != rssi.get_max()) { + last_rssi_min = rssi.get_min(); + last_rssi_med = rssi.get_avg(); + last_rssi_max = rssi.get_max(); + freq_stats.set("RSSI: " + to_string_dec_int(rssi.get_min()) + "/" + to_string_dec_int(rssi.get_avg()) + "/" + to_string_dec_int(rssi.get_max()) + " db"); + } + if (last_entry.frequency_a != freq) { + last_entry.frequency_a = freq; + big_display.set("FREQ: " + to_string_short_freq(freq) + " MHz"); + } + if (last_db != db || last_list_size != frequency_list.size() || last_freq_lock != freq_lock || last_nb_match != recon_lock_nb_match) { + last_freq_lock = freq; + last_list_size = frequency_list.size(); + last_db = db; + last_nb_match = recon_lock_nb_match; + text_max.set("/" + to_string_dec_uint(frequency_list.size()) + " " + to_string_dec_int(db) + " db " + to_string_dec_uint(freq_lock) + "/" + to_string_dec_uint(recon_lock_nb_match)); + if (freq_lock == 0) { + // NO FREQ LOCK, ONGOING STANDARD SCANNING + big_display.set_style(&style_white); + if (!userpause) + button_pause.set_text(""); + else + button_pause.set_text(""); + } else if (freq_lock == 1 && recon_lock_nb_match != 1) { + // STARTING LOCK FREQ + big_display.set_style(&style_yellow); + button_pause.set_text(""); + } else if (freq_lock >= recon_lock_nb_match) { + big_display.set_style(&style_green); + button_pause.set_text(""); + + // FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() + if ((!scanner_mode) && autosave && frequency_list.size() > 0) { + ReconSaveFreq(freq_file_path, current_index, false); + } + } + } +} + +void ReconView::handle_retune() { + if (last_freq != freq) { + last_freq = freq; + receiver_model.set_tuning_frequency(freq); // Retune + } + if (frequency_list.size() > 0) { + if (last_entry.modulation != frequency_list[current_index].modulation && frequency_list[current_index].modulation >= 0) { + last_entry.modulation = frequency_list[current_index].modulation; + field_mode.set_selected_index(frequency_list[current_index].modulation); + } + // Set bandwidth if any + if (last_entry.bandwidth != frequency_list[current_index].bandwidth && frequency_list[current_index].bandwidth >= 0) { + last_entry.bandwidth = frequency_list[current_index].bandwidth; + switch (frequency_list[current_index].modulation) { + case AM_MODULATION: + receiver_model.set_am_configuration(freq); + break; + case NFM_MODULATION: + receiver_model.set_nbfm_configuration(freq); + break; + case WFM_MODULATION: + receiver_model.set_wfm_configuration(freq); + default: + break; + } + field_bw.set_selected_index(freq); + } + if (last_entry.step != frequency_list[current_index].step && frequency_list[current_index].step >= 0) { + last_entry.step = frequency_list[current_index].step; + step = freqman_entry_get_step_value(last_entry.step); + step_mode.set_selected_index(step); + } + if (last_index != current_index) { + last_index = current_index; + if ((int32_t)frequency_list.size() && current_index < (int32_t)frequency_list.size() && frequency_list[current_index].type == RANGE) { + if (update_ranges) { + button_manual_start.set_text(to_string_short_freq(frequency_list[current_index].frequency_a)); + frequency_range.min = frequency_list[current_index].frequency_a; + if (frequency_list[current_index].frequency_b != 0) { + button_manual_end.set_text(to_string_short_freq(frequency_list[current_index].frequency_b)); + frequency_range.max = frequency_list[current_index].frequency_b; + } else { + button_manual_end.set_text(to_string_short_freq(frequency_list[current_index].frequency_a)); + frequency_range.max = frequency_list[current_index].frequency_a; + } + } + } + text_cycle.set_text(to_string_dec_uint(current_index + 1, 3)); + if (frequency_list[current_index].description.size() > 0) { + switch (frequency_list[current_index].type) { + case RANGE: + desc_cycle.set("R: " + frequency_list[current_index].description); // Show new description + break; + case HAMRADIO: + desc_cycle.set("H: " + frequency_list[current_index].description); // Show new description + break; + default: + case SINGLE: + desc_cycle.set("S: " + frequency_list[current_index].description); // Show new description + break; + } + } else { + desc_cycle.set("...no description..."); // Show new description + } + } + } +} + +void ReconView::focus() { + button_pause.focus(); +} + +ReconView::~ReconView() { + ReconSetupSaveStrings("RECON/RECON.CFG", input_file, output_file, recon_lock_duration, recon_lock_nb_match, squelch, recon_match_mode, wait, field_volume.value()); + + // save app settings + settings.save("recon", &app_settings); + + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); +} + +ReconView::ReconView(NavigationView& nav) + : nav_{nav} { + add_children({&labels, + &field_lna, + &field_vga, + &field_rf_amp, + &field_volume, + &field_bw, + &field_squelch, + &field_wait, + &field_lock_wait, + &button_recon_setup, + &button_scanner_mode, + &button_looking_glass, + &file_name, + &rssi, + &text_cycle, + &text_max, + &desc_cycle, + &big_display, + &freq_stats, + &text_timer, + &text_ctcss, + &button_manual_start, + &button_manual_end, + &button_manual_recon, + &field_mode, + &step_mode, + &button_pause, + &button_audio_app, + &button_add, + &button_dir, + &button_restart, + &button_mic_app, + &button_remove}); + + // Recon directory + if (check_sd_card()) { // Check to see if SD Card is mounted + make_new_directory(u"/RECON"); + sd_card_mounted = true; + } + def_step = 0; + // HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + receiver_model.set_tuning_frequency(stored_freq); + if (stored_freq - OneMHz > 0) + frequency_range.min = stored_freq - OneMHz; + else + frequency_range.min = 0; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + if (stored_freq + OneMHz < MAX_UFREQ) + frequency_range.max = stored_freq + OneMHz; + else + frequency_range.max = MAX_UFREQ; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + // Loading settings + autostart = persistent_memory::recon_autostart_recon(); + autosave = persistent_memory::recon_autosave_freqs(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_volume.set_value(volume); + if (sd_card_mounted) { + // load auto common app settings + auto rc = settings.load("recon", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + receiver_model.set_rf_amp(app_settings.rx_amp); + } + } + + button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + button_manual_end.on_select = [this, &nav](ButtonWithEncoder& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { + if (frequency_list.size() > 0) { + auto new_view = nav_.push(current_index); + new_view->on_changed = [this, &button](rf::Frequency f) { + f = f / OneMHz; + if (f >= 1 && f <= frequency_list.size()) { + index_stepper = f - 1 - current_index; + freq_lock = 0; + } + }; + } + }; + + button_manual_start.on_change = [this]() { + frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value(def_step); + if (frequency_range.min < 0) { + frequency_range.min = 0; + } + if (frequency_range.min > (MAX_UFREQ - freqman_entry_get_step_value(def_step))) { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value(def_step); + } + if (frequency_range.min > (frequency_range.max - freqman_entry_get_step_value(def_step))) { + frequency_range.max = frequency_range.min + freqman_entry_get_step_value(def_step); + if (frequency_range.max > MAX_UFREQ) { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value(def_step); + frequency_range.max = MAX_UFREQ; + } + } + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + button_manual_start.set_encoder_delta(0); + }; + + button_manual_end.on_change = [this]() { + frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value(def_step); + if (frequency_range.max < (freqman_entry_get_step_value(def_step) + 1)) { + frequency_range.max = (freqman_entry_get_step_value(def_step) + 1); + } + if (frequency_range.max > MAX_UFREQ) { + frequency_range.max = MAX_UFREQ; + } + if (frequency_range.max < (frequency_range.min + freqman_entry_get_step_value(def_step))) { + frequency_range.min = frequency_range.max - freqman_entry_get_step_value(def_step); + if (frequency_range.max < (freqman_entry_get_step_value(def_step) + 1)) { + frequency_range.min = 1; + frequency_range.max = (freqman_entry_get_step_value(def_step) + 1); + } + } + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + button_manual_end.set_encoder_delta(0); + }; + + text_cycle.on_change = [this]() { + on_index_delta(text_cycle.get_encoder_delta()); + text_cycle.set_encoder_delta(0); + }; + + button_pause.on_select = [this](ButtonWithEncoder&) { + if (frequency_list.size() > 0) { + if (freq_lock > 0) { + if (fwd) { + on_stepper_delta(1); + } else { + on_stepper_delta(-1); + } + button_pause.set_text(""); // Show button for non continuous stop + } else { + if (userpause) { + recon_resume(); + } else { + recon_pause(); + } + } + } + }; + + button_pause.on_change = [this]() { + on_stepper_delta(button_pause.get_encoder_delta()); + button_pause.set_encoder_delta(0); + }; + + button_audio_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_looking_glass.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + rssi.set_focusable(true); + rssi.set_peak(true, 500); + rssi.on_select = [this](RSSI&) { + nav_.pop(); + nav_.push(); + }; + + button_mic_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_remove.on_select = [this](ButtonWithEncoder&) { + if (frequency_list.size() > 0) { + if (scanner_mode) { + if (current_index >= (int32_t)frequency_list.size()) { + current_index = frequency_list.size() - 1; + } + frequency_list.erase(frequency_list.begin() + current_index); + if (current_index >= (int32_t)frequency_list.size()) { + current_index = frequency_list.size() - 1; + } + if (frequency_list.size() > 0) { + if (frequency_list[current_index].description.size() > 0) { + switch (frequency_list[current_index].type) { + case RANGE: + desc_cycle.set("R: " + frequency_list[current_index].description); + break; + case HAMRADIO: + desc_cycle.set("H: " + frequency_list[current_index].description); + break; + default: + case SINGLE: + desc_cycle.set("S: " + frequency_list[current_index].description); + break; + } + } else { + desc_cycle.set("...no description..."); + } + text_cycle.set_text(to_string_dec_uint(current_index + 1, 3)); + + File freqman_file; + + delete_file(freq_file_path); + + auto result = freqman_file.create(freq_file_path); + if (!result.is_valid()) { + for (size_t n = 0; n < frequency_list.size(); n++) { + std::string line; + get_freq_string(frequency_list[n], line); + freqman_file.write_line(line); + } + } + } + } else // RECON MODE / MANUAL, only remove matching from output + { + File recon_file; + File tmp_recon_file; + std::string tmp_freq_file_path = freq_file_path + ".TMP"; + std::string frequency_to_add; + + freqman_entry entry = frequency_list[current_index]; + entry.frequency_a = freq; + entry.frequency_b = 0; + entry.modulation = last_entry.modulation; + entry.bandwidth = last_entry.bandwidth; + entry.type = SINGLE; + + get_freq_string(entry, frequency_to_add); + + delete_file(tmp_freq_file_path); + auto result = tmp_recon_file.create(tmp_freq_file_path); // First recon if freq is already in txt + if (!result.is_valid()) { + bool found = false; + result = recon_file.open(freq_file_path); // First recon if freq is already in txt + if (!result.is_valid()) { + char one_char[1]; // Read it char by char + std::string line; // and put read line in here + for (size_t pointer = 0; pointer < recon_file.size(); pointer++) { + recon_file.seek(pointer); + recon_file.read(one_char, 1); + if ((int)one_char[0] > 31) { // ascii space upwards + line += one_char[0]; // add it to the textline + } else if (one_char[0] == '\n') { // new Line + if (line.compare(0, frequency_to_add.size(), frequency_to_add) == 0) { + found = true; + } else { + tmp_recon_file.write_line(frequency_to_add); + } + line.clear(); // ready for next textline + } + } + if (found) { + delete_file(freq_file_path); + rename_file(tmp_freq_file_path, freq_file_path); + } else { + delete_file(tmp_freq_file_path); + } + } + } + } + receiver_model.set_tuning_frequency(frequency_list[current_index].frequency_a); // retune + } + if (frequency_list.size() == 0) { + text_cycle.set_text(" "); + desc_cycle.set("no entries in list"); // Show new description + delete_file(freq_file_path); + } + timer = 0; + freq_lock = 0; + }; + + button_remove.on_change = [this]() { + on_stepper_delta(button_remove.get_encoder_delta()); + button_remove.set_encoder_delta(0); + }; + + button_manual_recon.on_select = [this](Button&) { + scanner_mode = false; + manual_mode = true; + recon_pause(); + + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + audio::output::stop(); + + frequency_list.clear(); + freqman_entry manual_freq_entry; + + def_step = step_mode.selected_index(); // max range val + + manual_freq_entry.type = RANGE; + manual_freq_entry.description = + "R " + to_string_short_freq(frequency_range.min) + ">" + to_string_short_freq(frequency_range.max) + " S" // current Manual range + + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0, 1); // euquiq: lame kludge to reduce spacing in step freq + manual_freq_entry.frequency_a = frequency_range.min; // min range val + manual_freq_entry.frequency_b = frequency_range.max; // max range val + manual_freq_entry.modulation = -1; + manual_freq_entry.bandwidth = -1; + manual_freq_entry.step = def_step; + + frequency_list.push_back(manual_freq_entry); + + big_display.set_style(&style_white); // Back to white color + + freq_stats.set_style(&style_white); + freq_stats.set("0/0/0"); + + text_cycle.set_text("1"); + text_max.set("/1"); + button_scanner_mode.set_style(&style_white); + button_scanner_mode.set_text("MSEARCH"); + file_name.set_style(&style_white); + file_name.set("MANUAL RANGE RECON"); + desc_cycle.set_style(&style_white); + desc_cycle.set("MANUAL RANGE RECON"); + + current_index = 0; + freq = manual_freq_entry.frequency_a; + handle_retune(); + recon_redraw(); + recon_resume(); + } + }; + + button_dir.on_select = [this](Button&) { + if (fwd) { + fwd = false; + button_dir.set_text(""); + } + timer = 0; + if (userpause) + recon_resume(); + }; + + button_restart.on_select = [this](Button&) { + if (frequency_list.size() > 0) { + def_step = step_mode.selected_index(); // Use def_step from manual selector + frequency_file_load(true); + if (fwd) { + button_dir.set_text("FW>"); + } else { + button_dir.set_text("(input_file, output_file, recon_lock_duration, recon_lock_nb_match, recon_match_mode); + open_view->on_changed = [this](std::vector result) { + input_file = result[0]; + output_file = result[1]; + freq_file_path = "/FREQMAN/" + output_file + ".TXT"; + recon_lock_duration = strtol(result[2].c_str(), nullptr, 10); + recon_lock_nb_match = strtol(result[3].c_str(), nullptr, 10); + recon_match_mode = strtol(result[4].c_str(), nullptr, 10); + + ReconSetupSaveStrings("RECON/RECON.CFG", input_file, output_file, recon_lock_duration, recon_lock_nb_match, squelch, recon_match_mode, wait, field_volume.value()); + + autosave = persistent_memory::recon_autosave_freqs(); + autostart = persistent_memory::recon_autostart_recon(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_lock_wait.set_value(recon_lock_duration); + + frequency_file_load(false); + if (autostart) { + recon_resume(); + } else { + recon_pause(); + } + if (userpause != true) { + recon_resume(); + } + }; + }; + + field_wait.on_change = [this](int32_t v) { + wait = v; + if (wait == 0) { + field_wait.set_style(&style_blue); + } else if (wait >= 500) { + field_wait.set_style(&style_white); + } else if (wait > -500 && wait < 500) { + field_wait.set_style(&style_red); + } else if (wait <= -500) { + field_wait.set_style(&style_green); + } + }; + + field_lock_wait.on_change = [this](uint32_t v) { + recon_lock_duration = v; + if (recon_match_mode == RECON_MATCH_CONTINUOUS) { + if ((v / STATS_UPDATE_INTERVAL) > recon_lock_nb_match) { + field_lock_wait.set_style(&style_white); + } else if ((v / STATS_UPDATE_INTERVAL) == recon_lock_nb_match) { + field_lock_wait.set_style(&style_yellow); + } + } else // RECON_MATCH_SPARSE + { + field_lock_wait.set_style(&style_white); + } + }; + + field_squelch.on_change = [this](int32_t v) { + squelch = v; + }; + + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + // PRE-CONFIGURATION: + button_scanner_mode.set_style(&style_blue); + button_scanner_mode.set_text("RECON"); + file_name.set("=>"); + + // Loading input and output file from settings + ReconSetupLoadStrings("RECON/RECON.CFG", input_file, output_file, recon_lock_duration, recon_lock_nb_match, squelch, recon_match_mode, wait, volume); + freq_file_path = "/FREQMAN/" + output_file + ".TXT"; + + field_squelch.set_value(squelch); + field_wait.set_value(wait); + field_lock_wait.set_value(recon_lock_duration); + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + // fill modulation and step options + freqman_set_modulation_option(field_mode); + freqman_set_step_option(step_mode); + + // set radio + change_mode(AM_MODULATION); // start on AM. + field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); // first tune + + if (filedelete) { + delete_file(freq_file_path); + } + + frequency_file_load(false); /* do not stop all at start */ + if (autostart) { + recon_resume(); + } else { + recon_pause(); + } + + recon_redraw(); +} + +void ReconView::frequency_file_load(bool stop_all_before) { + (void)(stop_all_before); + audio::output::stop(); + + def_step = step_mode.selected_index(); // use def_step from manual selector + frequency_list.clear(); // clear the existing frequency list (expected behavior) + std::string file_input = input_file; // default recon mode + if (scanner_mode) { + file_input = output_file; + file_name.set_style(&style_red); + button_scanner_mode.set_style(&style_red); + button_scanner_mode.set_text("SCANNER"); + } else { + file_name.set_style(&style_blue); + button_scanner_mode.set_style(&style_blue); + button_scanner_mode.set_text("RECON"); + } + file_name.set_style(&style_white); + desc_cycle.set_style(&style_white); + if (!load_freqman_file_ex(file_input, frequency_list, load_freqs, load_ranges, load_hamradios)) { + file_name.set_style(&style_red); + desc_cycle.set_style(&style_red); + desc_cycle.set(" NO " + file_input + ".TXT FILE ..."); + file_name.set("=> NO DATA"); + } else { + file_name.set("=> " + file_input); + if (frequency_list.size() == 0) { + file_name.set_style(&style_red); + desc_cycle.set_style(&style_red); + desc_cycle.set("/0 no entries in list"); + file_name.set("BadOrEmpty " + file_input); + } else { + if (frequency_list.size() > FREQMAN_MAX_PER_FILE) { + file_name.set_style(&style_yellow); + desc_cycle.set_style(&style_yellow); + } + } + } + + if (frequency_list[0].step >= 0) + step = freqman_entry_get_step_value(frequency_list[0].step); + else + step = freqman_entry_get_step_value(def_step); + + switch (frequency_list[0].type) { + case SINGLE: + freq = frequency_list[0].frequency_a; + break; + case RANGE: + minfreq = frequency_list[0].frequency_a; + maxfreq = frequency_list[0].frequency_b; + if (fwd) { + freq = minfreq; + } else { + freq = maxfreq; + } + if (frequency_list[0].step >= 0) + step = freqman_entry_get_step_value(frequency_list[0].step); + break; + case HAMRADIO: + minfreq = frequency_list[0].frequency_a; + maxfreq = frequency_list[0].frequency_b; + if (fwd) { + freq = minfreq; + } else { + freq = maxfreq; + } + break; + default: + break; + } + last_entry.modulation = -1; + last_entry.bandwidth = -1; + last_entry.step = -1; + + step_mode.set_selected_index(def_step); // Impose the default step into the manual step selector + receiver_model.enable(); + receiver_model.set_squelch_level(0); + std::string description = "...no description..."; + current_index = 0; + if (frequency_list.size() != 0) { + switch (frequency_list[current_index].type) { + case RANGE: + description = "R: " + frequency_list[current_index].description; + break; + case HAMRADIO: + description = "H: " + frequency_list[current_index].description; + break; + default: + case SINGLE: + description = "S: " + frequency_list[current_index].description; + break; + } + text_cycle.set_text(to_string_dec_uint(current_index + 1, 3)); + if (update_ranges) { + button_manual_start.set_text(to_string_short_freq(frequency_list[current_index].frequency_a)); + frequency_range.min = frequency_list[current_index].frequency_a; + if (frequency_list[current_index].frequency_b != 0) { + button_manual_end.set_text(to_string_short_freq(frequency_list[current_index].frequency_b)); + frequency_range.max = frequency_list[current_index].frequency_b; + } else { + button_manual_end.set_text(to_string_short_freq(frequency_list[current_index].frequency_a)); + frequency_range.max = frequency_list[current_index].frequency_a; + } + } + } else { + text_cycle.set_text(" "); + } + desc_cycle.set(description); + handle_retune(); +} + +void ReconView::on_statistics_update(const ChannelStatistics& statistics) { + db = statistics.max_db; + if (!userpause) { + if (!timer) { + status = 0; + continuous_lock = false; + freq_lock = 0; + timer = recon_lock_duration; + big_display.set_style(&style_white); + } + if (freq_lock < recon_lock_nb_match) // LOCKING + { + if (status != 1) { + status = 1; + if (wait != 0) { + audio::output::stop(); + } + } + if (db > squelch) // MATCHING LEVEL + { + continuous_lock = true; + freq_lock++; + } else { + // continuous, direct cut it if not consecutive match after 1 first match + if (recon_match_mode == RECON_MATCH_CONTINUOUS) { + if (freq_lock > 0) { + timer = 0; + continuous_lock = false; + } + } + } + } + if (freq_lock >= recon_lock_nb_match) // LOCKED + { + if (status != 2) { + continuous_lock = false; + status = 2; + if (wait != 0) { + audio_output_start(); + } + if (wait >= 0) { + timer = wait; + } + } + if (wait < 0) { + if (db > squelch) // MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY + { + timer = abs(wait); + } + } + } + } + if (last_timer != timer) { + last_timer = timer; + text_timer.set("TIMER: " + to_string_dec_int(timer)); + } + if (timer) { + if (!continuous_lock) + timer -= STATS_UPDATE_INTERVAL; + if (timer < 0) { + timer = 0; + } + } + if (recon || stepper != 0 || index_stepper != 0) { + if (!timer || stepper != 0 || index_stepper != 0) { + // IF THERE IS A FREQUENCY LIST ... + if (frequency_list.size() > 0) { + has_looped = false; + entry_has_changed = false; + + if (recon || stepper != 0 || index_stepper != 0) { + if (index_stepper == 0) { + /* we are doing a range */ + if (frequency_list[current_index].type == RANGE) { + if ((fwd && stepper == 0) || stepper > 0) { + // forward + freq += step; + // if bigger than range max + if (freq > maxfreq) { + // when going forward we already know that we can skip a whole range => two values in the list + current_index++; + entry_has_changed = true; + // looping + if ((uint32_t)current_index >= frequency_list.size()) { + has_looped = true; + current_index = 0; + } + } + } else if ((!fwd && stepper == 0) || stepper < 0) { + // reverse + freq -= step; + // if lower than range min + if (freq < minfreq) { + // when back we have to check one step at a time + current_index--; + entry_has_changed = true; + // looping + if (current_index < 0) { + has_looped = true; + current_index = frequency_list.size() - 1; + } + } + } + } else if (frequency_list[current_index].type == SINGLE) { + if ((fwd && stepper == 0) || stepper > 0) { // forward + current_index++; + entry_has_changed = true; + // looping + if ((uint32_t)current_index >= frequency_list.size()) { + has_looped = true; + current_index = 0; + } + } else if ((!fwd && stepper == 0) || stepper < 0) { + // reverse + current_index--; + entry_has_changed = true; + // if previous if under the list => go back from end + if (current_index < 0) { + has_looped = true; + current_index = frequency_list.size() - 1; + } + } + } else if (frequency_list[current_index].type == HAMRADIO) { + if ((fwd && stepper == 0) || stepper > 0) { // forward + if ((minfreq != maxfreq) && freq == minfreq) { + freq = maxfreq; + } else { + current_index++; + entry_has_changed = true; + // looping + if ((uint32_t)current_index >= frequency_list.size()) { + has_looped = true; + current_index = 0; + } + } + } else if ((!fwd && stepper == 0) || stepper < 0) { + // reverse + if ((minfreq != maxfreq) && freq == maxfreq) { + freq = minfreq; + } else { + current_index--; + entry_has_changed = true; + // if previous if under the list => go back from end + if (current_index < 0) { + has_looped = true; + current_index = frequency_list.size() - 1; + } + } + } + } + // set index to boundary if !continuous + if (has_looped && !continuous) { + entry_has_changed = true; + /* prepare values for the next run, when user will resume */ + if ((fwd && stepper == 0) || stepper > 0) { + current_index = 0; + } else if ((!fwd && stepper == 0) || stepper < 0) { + current_index = frequency_list.size() - 1; + } + } + } else { + current_index += index_stepper; + if (current_index < 0) + current_index += frequency_list.size(); + if ((unsigned)current_index >= frequency_list.size()) + current_index -= frequency_list.size(); + + entry_has_changed = true; + + if (!recon) // for some motive, audio output gets stopped. + audio_output_start(); + } + // reload entry if changed + if (entry_has_changed) { + timer = 0; + switch (frequency_list[current_index].type) { + case SINGLE: + freq = frequency_list[current_index].frequency_a; + break; + case RANGE: + minfreq = frequency_list[current_index].frequency_a; + maxfreq = frequency_list[current_index].frequency_b; + if ((fwd && !stepper && !index_stepper) || stepper > 0 || index_stepper > 0) { + freq = minfreq; + } else if ((!fwd && !stepper && !index_stepper) || stepper < 0 || index_stepper < 0) { + freq = maxfreq; + } + break; + case HAMRADIO: + minfreq = frequency_list[current_index].frequency_a; + maxfreq = frequency_list[current_index].frequency_b; + if ((fwd && !stepper && !index_stepper) || stepper > 0 || index_stepper > 0) { + freq = minfreq; + } else if ((!fwd && !stepper && !index_stepper) || stepper < 0 || index_stepper < 0) { + freq = maxfreq; + } + break; + default: + break; + } + } + // send a pause message with the right freq + if (has_looped && !continuous) { + recon_pause(); + } + index_stepper = 0; + if (stepper < 0) stepper++; + if (stepper > 0) stepper--; + } // if( recon || stepper != 0 || index_stepper != 0 ) + } // if (frequency_list.size() > 0 ) + } /* on_statistic_updates */ + } + handle_retune(); + recon_redraw(); +} + +void ReconView::recon_pause() { + timer = 0; + freq_lock = 0; + userpause = true; + continuous_lock = false; + recon = false; + + audio_output_start(); + + big_display.set_style(&style_white); + button_pause.set_text(""); // PAUSED, show resume +} + +void ReconView::recon_resume() { + timer = 0; + freq_lock = 0; + userpause = false; + continuous_lock = false; + recon = true; + + audio::output::stop(); + + big_display.set_style(&style_white); + button_pause.set_text(""); +} + +void ReconView::on_index_delta(int32_t v) { + if (v > 0) { + fwd = true; + button_dir.set_text("FW>"); + } else { + fwd = false; + button_dir.set_text(" 0) + index_stepper = v; + + freq_lock = 0; + timer = 0; +} + +void ReconView::on_stepper_delta(int32_t v) { + if (v > 0) { + fwd = true; + button_dir.set_text("FW>"); + } else { + fwd = false; + button_dir.set_text(" 0) + stepper = v; + + freq_lock = 0; + timer = 0; +} + +void ReconView::on_headphone_volume_changed(int32_t v) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); +} + +size_t ReconView::change_mode(freqman_index_t new_mod) { + field_mode.on_change = [this](size_t, OptionsField::value_t) {}; + field_bw.on_change = [this](size_t, OptionsField::value_t) {}; + + receiver_model.disable(); + baseband::shutdown(); + + switch (new_mod) { + case AM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw DSB (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + case NFM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw 16k (2) default + field_bw.set_selected_index(2); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + break; + case WFM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + // bw 200k (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + default: + break; + } + field_mode.set_selected_index(new_mod); + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (v != -1) { + change_mode(v); + } + }; + + if (!recon) // for some motive, audio output gets stopped. + audio::output::start(); // so if recon was stopped we resume audio + receiver_model.enable(); + + return freqman_entry_get_step_value(def_step); +} + +void ReconView::handle_coded_squelch(const uint32_t value) { + float diff, min_diff = value; + size_t min_idx{0}; + size_t c; + + if (field_mode.selected_index() != NFM_MODULATION) { + text_ctcss.set(" "); + return; + } + + // Find nearest match + for (c = 0; c < tone_keys.size(); c++) { + diff = abs(((float)value / 100.0) - tone_keys[c].second); + if (diff < min_diff) { + min_idx = c; + min_diff = diff; + } + } + + // Arbitrary confidence threshold + if (last_squelch_index < 0 || (unsigned)last_squelch_index != min_idx) { + last_squelch_index = min_idx; + if (min_diff < 40) + text_ctcss.set("T: " + tone_keys[min_idx].first); + else + text_ctcss.set(" "); + } +} } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon.hpp b/firmware/application/apps/ui_recon.hpp index af8c8da29..df4a91da4 100644 --- a/firmware/application/apps/ui_recon.hpp +++ b/firmware/application/apps/ui_recon.hpp @@ -42,329 +42,311 @@ namespace ui { - class ReconView : public View { - public: - ReconView(NavigationView& nav); - ~ReconView(); - - void focus() override; - - const Style style_grey { // recon - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - - const Style style_white { // recon - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::white(), - }; - - const Style style_yellow { //found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::yellow(), - }; - - const Style style_green { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - - const Style style_red { //erasing freq - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - const Style style_blue { // quick recon, wait == 0 - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::blue(), - }; - - std::string title() const override { return "Recon"; }; - - //void set_parent_rect(const Rect new_parent_rect) override; - - private: - NavigationView& nav_; - - void audio_output_start(); - bool check_sd_card(); - size_t change_mode( freqman_index_t mod_type); - void show_max( bool refresh_display = false ); - void recon_pause(); - void recon_resume(); - void frequency_file_load( bool stop_all_before = false); - void on_statistics_update(const ChannelStatistics& statistics); - void on_headphone_volume_changed(int32_t v); - void on_index_delta(int32_t v); - void on_stepper_delta(int32_t v); - void recon_redraw(); - void handle_retune(); - void handle_coded_squelch(const uint32_t value); - bool ReconSetupLoadStrings( const std::string &source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ); - bool ReconSetupSaveStrings( const std::string &dest, const std::string &input_file , const std::string &output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ); - bool ReconSaveFreq( const std::string &freq_file_path , size_t index , bool warn_if_exists ); - - jammer::jammer_range_t frequency_range { false, 0, MAX_UFREQ }; //perfect for manual recon task too... - int32_t squelch { 0 }; - int32_t db { 0 }; - int32_t timer { 0 }; - int32_t wait { RECON_DEF_WAIT_DURATION }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity - freqman_db frequency_list = { }; - int32_t current_index { 0 }; - bool userpause { false }; - bool continuous_lock { false }; - std::string input_file = { "RECON" }; - std::string output_file = { "RECON_RESULTS" }; - bool autosave = { true }; - bool autostart = { true }; - bool continuous = { true }; - bool filedelete = { true }; - bool load_freqs = { true }; - bool load_ranges = { true }; - bool load_hamradios = { true }; - bool update_ranges = { true }; - bool fwd = { true }; - bool recon = true ; - uint32_t recon_lock_nb_match = { 3 }; - uint32_t recon_lock_duration = { RECON_MIN_LOCK_DURATION }; - uint32_t recon_match_mode = { RECON_MATCH_CONTINUOUS }; - bool scanner_mode { false }; - bool manual_mode { false }; - bool sd_card_mounted = false ; - int32_t volume = 40 ; - int32_t stepper = 0 ; - int32_t index_stepper = 0 ; - int64_t freq = 0 ; - uint32_t step = 0 ; - freqman_index_t def_modulation = 0 ; - freqman_index_t def_bandwidth = 0 ; - freqman_index_t def_step = 0 ; - tone_index tone = 0 ; - freqman_entry last_entry = { } ; - bool entry_has_changed = false ; - uint32_t freq_lock { 0 }; - int64_t minfreq = 0 ; - int64_t maxfreq = 0 ; - bool has_looped = false ; - int8_t status = -1 ; // 0 recon , 1 locking , 2 locked - int32_t last_timer = -1 ; - int8_t last_db = -127 ; - uint16_t last_nb_match = 999 ; - uint16_t last_freq_lock = 999 ; - size_t last_list_size = 0 ; - int8_t last_rssi_min = -127 ; - int8_t last_rssi_med = -127 ; - int8_t last_rssi_max = -127 ; - int32_t last_index = -1 ; - int32_t last_squelch_index = -1 ; - int64_t last_freq = 0 ; - std::string freq_file_path = { } ; - - Labels labels - { - { { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() }, - { { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() }, - { { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() }, - { { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() }, - }; - - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; - - NumberField field_volume { - { 24 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - OptionsField field_bw { - { 3 * 8, 1 * 16 }, - 6, - { } - }; - - NumberField field_squelch { - { 12 * 8, 1 * 16 }, - 3, - { -90, 20 }, - 1, - ' ', - }; - - NumberField field_wait { - { 20 * 8, 1 * 16 }, - 5, - { -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, - STATS_UPDATE_INTERVAL, - ' ', - }; - - NumberField field_lock_wait { - { 26 * 8, 1 * 16 }, - 4, - { RECON_MIN_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, - STATS_UPDATE_INTERVAL, - ' ', - }; - - RSSI rssi { - { 0 * 16, 2 * 16, SCREEN_W - 8 * 8 + 4 , 16 }, - }; - - ButtonWithEncoder text_cycle { - { 0, 3 * 16, 4 * 8, 16 }, - "" - }; - - Text text_max { - { 4 * 8, 3 * 16, SCREEN_W - 7 * 8 - 4 * 8 , 16 }, - }; - - Text desc_cycle { - {0, 4 * 16, SCREEN_W , 16 }, - }; - - Text big_display { //Show frequency in text mode - { 0, 5 * 16 , 23 * 8, 16 }, - }; - - Text freq_stats { //Show frequency stats in text mode - { 0, 6 * 16 , 23 * 8, 16 }, - }; - - // TIMER: 9999 - Text text_timer { //Show frequency stats in text mode - { 0, 7 * 16 , 11 * 8, 16 }, - }; - - // T: Senn. 32.000k - Text text_ctcss { - { 12 * 8 + 4, 7 * 16 , 14 * 8, 1 * 8 }, - "" - }; - - Button button_recon_setup { - { SCREEN_W - 7 * 8 , 2 * 16 , 7 * 8, 28 }, - "CONFIG" - }; - - Button button_looking_glass { - { SCREEN_W - 7 * 8 , 5 * 16 , 7 * 8, 28 }, - "GLASS" - }; - - // Button can be RECON or SCANNER - Button button_scanner_mode { - { SCREEN_W - 7 * 8 , 8 * 16 , 7 * 8, 28 }, - "RECON" - }; - - Text file_name { //show file used - { 0 , 8 * 16 + 6 , SCREEN_W - 7 * 8, 16 }, - }; - - ButtonWithEncoder button_manual_start { - { 0 * 8, 11 * 16, 11 * 8, 28 }, - "" - }; - - ButtonWithEncoder button_manual_end { - { 12 * 8 - 6, 11 * 16, 11 * 8, 28 }, - "" - }; - - Button button_manual_recon { - { 23 * 8 - 3, 11 * 16, 7 * 8 , 28 }, - "SEARCH" - }; - - OptionsField field_mode { - { 5 * 8, (26 * 8) + 4 }, - 6, - { - } - }; - - OptionsField step_mode { - { 17 * 8, (26 * 8) + 4 }, - 12, - { - } - }; - - ButtonWithEncoder button_pause { - { 0, (15 * 16) - 4, 72, 28 }, - "PAUSE" - }; - - - Button button_audio_app { - { 84, (15 * 16) - 4, 72, 28 }, - "AUDIO" - }; - - ButtonWithEncoder button_add { - { 168, (15 * 16) - 4, 72, 28 }, - "" - }; - - Button button_dir { - { 0, (35 * 8) - 4, 34, 28 }, - "FW>" - }; - - Button button_restart { - { 38, (35 * 8) - 4, 34, 28 }, - "RST" - }; - - - Button button_mic_app { - { 84, (35 * 8) - 4, 72, 28 }, - "MIC TX" - }; - - ButtonWithEncoder button_remove { - { 168, (35 * 8) - 4, 72, 28 }, - "" - }; - - MessageHandlerRegistration message_handler_coded_squelch { - Message::ID::CodedSquelch, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_coded_squelch(message.value); - } - }; - - MessageHandlerRegistration message_handler_stats { - Message::ID::ChannelStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - }; +class ReconView : public View { + public: + ReconView(NavigationView& nav); + ~ReconView(); + + void focus() override; + + const Style style_grey{ + // recon + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + const Style style_white{ + // recon + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::white(), + }; + + const Style style_yellow{ + // found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::yellow(), + }; + + const Style style_green{ + // Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + const Style style_red{ + // erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + const Style style_blue{ + // quick recon, wait == 0 + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::blue(), + }; + + std::string title() const override { return "Recon"; }; + + // void set_parent_rect(const Rect new_parent_rect) override; + + private: + NavigationView& nav_; + + void audio_output_start(); + bool check_sd_card(); + size_t change_mode(freqman_index_t mod_type); + void show_max(bool refresh_display = false); + void recon_pause(); + void recon_resume(); + void frequency_file_load(bool stop_all_before = false); + void on_statistics_update(const ChannelStatistics& statistics); + void on_headphone_volume_changed(int32_t v); + void on_index_delta(int32_t v); + void on_stepper_delta(int32_t v); + void recon_redraw(); + void handle_retune(); + void handle_coded_squelch(const uint32_t value); + bool ReconSetupLoadStrings(const std::string& source, std::string& input_file, std::string& output_file, uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, int32_t& recon_squelch_level, uint32_t& recon_match_mode, int32_t& wait, int32_t& volume); + bool ReconSetupSaveStrings(const std::string& dest, const std::string& input_file, const std::string& output_file, uint32_t recon_lock_duration, uint32_t recon_lock_nb_match, int32_t recon_squelch_level, uint32_t recon_match_mode, int32_t wait, int32_t volume); + bool ReconSaveFreq(const std::string& freq_file_path, size_t index, bool warn_if_exists); + + jammer::jammer_range_t frequency_range{false, 0, MAX_UFREQ}; // perfect for manual recon task too... + int32_t squelch{0}; + int32_t db{0}; + int32_t timer{0}; + int32_t wait{RECON_DEF_WAIT_DURATION}; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity + freqman_db frequency_list = {}; + int32_t current_index{0}; + bool userpause{false}; + bool continuous_lock{false}; + std::string input_file = {"RECON"}; + std::string output_file = {"RECON_RESULTS"}; + bool autosave = {true}; + bool autostart = {true}; + bool continuous = {true}; + bool filedelete = {true}; + bool load_freqs = {true}; + bool load_ranges = {true}; + bool load_hamradios = {true}; + bool update_ranges = {true}; + bool fwd = {true}; + bool recon = true; + uint32_t recon_lock_nb_match = {3}; + uint32_t recon_lock_duration = {RECON_MIN_LOCK_DURATION}; + uint32_t recon_match_mode = {RECON_MATCH_CONTINUOUS}; + bool scanner_mode{false}; + bool manual_mode{false}; + bool sd_card_mounted = false; + int32_t volume = 40; + int32_t stepper = 0; + int32_t index_stepper = 0; + int64_t freq = 0; + uint32_t step = 0; + freqman_index_t def_modulation = 0; + freqman_index_t def_bandwidth = 0; + freqman_index_t def_step = 0; + tone_index tone = 0; + freqman_entry last_entry = {}; + bool entry_has_changed = false; + uint32_t freq_lock{0}; + int64_t minfreq = 0; + int64_t maxfreq = 0; + bool has_looped = false; + int8_t status = -1; // 0 recon , 1 locking , 2 locked + int32_t last_timer = -1; + int8_t last_db = -127; + uint16_t last_nb_match = 999; + uint16_t last_freq_lock = 999; + size_t last_list_size = 0; + int8_t last_rssi_min = -127; + int8_t last_rssi_med = -127; + int8_t last_rssi_max = -127; + int32_t last_index = -1; + int32_t last_squelch_index = -1; + int64_t last_freq = 0; + std::string freq_file_path = {}; + + Labels labels{ + {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()}, + {{0 * 8, 1 * 16}, "BW: SQ: W,L: , ", Color::light_grey()}, + {{3 * 8, 10 * 16}, "START END MANUAL", Color::light_grey()}, + {{0 * 8, (26 * 8) + 4}, "MODE:", Color::light_grey()}, + {{11 * 8, (26 * 8) + 4}, "STEP:", Color::light_grey()}, + }; + + LNAGainField field_lna{ + {4 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {11 * 8, 0 * 16}}; + + RFAmpField field_rf_amp{ + {18 * 8, 0 * 16}}; + + NumberField field_volume{ + {24 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + OptionsField field_bw{ + {3 * 8, 1 * 16}, + 6, + {}}; + + NumberField field_squelch{ + {12 * 8, 1 * 16}, + 3, + {-90, 20}, + 1, + ' ', + }; + + NumberField field_wait{ + {20 * 8, 1 * 16}, + 5, + {-RECON_MAX_LOCK_DURATION, RECON_MAX_LOCK_DURATION}, + STATS_UPDATE_INTERVAL, + ' ', + }; + + NumberField field_lock_wait{ + {26 * 8, 1 * 16}, + 4, + {RECON_MIN_LOCK_DURATION, RECON_MAX_LOCK_DURATION}, + STATS_UPDATE_INTERVAL, + ' ', + }; + + RSSI rssi{ + {0 * 16, 2 * 16, SCREEN_W - 8 * 8 + 4, 16}, + }; + + ButtonWithEncoder text_cycle{ + {0, 3 * 16, 4 * 8, 16}, + ""}; + + Text text_max{ + {4 * 8, 3 * 16, SCREEN_W - 7 * 8 - 4 * 8, 16}, + }; + + Text desc_cycle{ + {0, 4 * 16, SCREEN_W, 16}, + }; + + Text big_display{ + // Show frequency in text mode + {0, 5 * 16, 23 * 8, 16}, + }; + + Text freq_stats{ + // Show frequency stats in text mode + {0, 6 * 16, 23 * 8, 16}, + }; + + // TIMER: 9999 + Text text_timer{ + // Show frequency stats in text mode + {0, 7 * 16, 11 * 8, 16}, + }; + + // T: Senn. 32.000k + Text text_ctcss{ + {12 * 8 + 4, 7 * 16, 14 * 8, 1 * 8}, + ""}; + + Button button_recon_setup{ + {SCREEN_W - 7 * 8, 2 * 16, 7 * 8, 28}, + "CONFIG"}; + + Button button_looking_glass{ + {SCREEN_W - 7 * 8, 5 * 16, 7 * 8, 28}, + "GLASS"}; + + // Button can be RECON or SCANNER + Button button_scanner_mode{ + {SCREEN_W - 7 * 8, 8 * 16, 7 * 8, 28}, + "RECON"}; + + Text file_name{ + // show file used + {0, 8 * 16 + 6, SCREEN_W - 7 * 8, 16}, + }; + + ButtonWithEncoder button_manual_start{ + {0 * 8, 11 * 16, 11 * 8, 28}, + ""}; + + ButtonWithEncoder button_manual_end{ + {12 * 8 - 6, 11 * 16, 11 * 8, 28}, + ""}; + + Button button_manual_recon{ + {23 * 8 - 3, 11 * 16, 7 * 8, 28}, + "SEARCH"}; + + OptionsField field_mode{ + {5 * 8, (26 * 8) + 4}, + 6, + {}}; + + OptionsField step_mode{ + {17 * 8, (26 * 8) + 4}, + 12, + {}}; + + ButtonWithEncoder button_pause{ + {0, (15 * 16) - 4, 72, 28}, + "PAUSE"}; + + Button button_audio_app{ + {84, (15 * 16) - 4, 72, 28}, + "AUDIO"}; + + ButtonWithEncoder button_add{ + {168, (15 * 16) - 4, 72, 28}, + ""}; + + Button button_dir{ + {0, (35 * 8) - 4, 34, 28}, + "FW>"}; + + Button button_restart{ + {38, (35 * 8) - 4, 34, 28}, + "RST"}; + + Button button_mic_app{ + {84, (35 * 8) - 4, 72, 28}, + "MIC TX"}; + + ButtonWithEncoder button_remove{ + {168, (35 * 8) - 4, 72, 28}, + ""}; + + MessageHandlerRegistration message_handler_coded_squelch{ + Message::ID::CodedSquelch, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_coded_squelch(message.value); + }}; + + MessageHandlerRegistration message_handler_stats{ + Message::ID::ChannelStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; +}; } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon_settings.cpp b/firmware/application/apps/ui_recon_settings.cpp index 81a9d832a..fb57458cb 100644 --- a/firmware/application/apps/ui_recon_settings.cpp +++ b/firmware/application/apps/ui_recon_settings.cpp @@ -34,143 +34,144 @@ using namespace portapack; namespace ui { - ReconSetupViewMain::ReconSetupViewMain( NavigationView &nav , Rect parent_rect , std::string input_file , std::string output_file ) : View( parent_rect ) , _input_file { input_file } , _output_file { output_file } - { - hidden(true); - add_children({ - &button_load_freqs, - &text_input_file, - &button_save_freqs, - &button_output_file, - &checkbox_autosave_freqs, - &checkbox_autostart_recon, - &checkbox_continuous, - &checkbox_clear_output - }); - - checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() ); - checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() ); - checkbox_continuous.set_value( persistent_memory::recon_continuous() ); - checkbox_clear_output.set_value( persistent_memory::recon_clear_output() ); - - text_input_file.set( _input_file ); - button_output_file.set_text( _output_file ); - - button_load_freqs.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { - std::string dir_filter = "FREQMAN/"; - std::string str_file_path = new_file_path.string(); - if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder - // get the filename without txt extension so we can use load_freqman_file fcn - _input_file = new_file_path.stem().string(); - text_input_file.set( _input_file ); - } else { - nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); - } - }; - }; - - button_save_freqs.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { - std::string dir_filter = "FREQMAN/"; - std::string str_file_path = new_file_path.string(); - if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder - _output_file = new_file_path.stem().string(); - button_output_file.set_text( _output_file ); - } else { - nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); - } - }; - }; - - button_output_file.on_select =[this, &nav](Button&) { - text_prompt( nav, _output_file , 28 , - [this](std::string& buffer) { - _output_file = buffer ; - button_output_file.set_text( _output_file ); - } ); - }; - }; - - void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) { - persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value()); - persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value()); - persistent_memory::set_recon_continuous(checkbox_continuous.value()); - persistent_memory::set_recon_clear_output(checkbox_clear_output.value()); - input_file=_input_file ; - output_file=_output_file ; - }; - void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) { - persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value()); - persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value()); - persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value()); - persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value()); - recon_lock_duration = field_recon_lock_duration.value(); - recon_lock_nb_match = field_recon_lock_nb_match.value(); - recon_match_mode = field_recon_match_mode . selected_index_value() ; - }; - - void ReconSetupViewMain::focus() { - button_load_freqs.focus(); - } - - ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode } - { - (void)nav; - hidden(true); - - add_children({ - &checkbox_load_freqs, - &checkbox_load_ranges, - &checkbox_load_hamradios, - &checkbox_update_ranges_when_recon, - &text_recon_lock_duration, - &field_recon_lock_duration, - &text_recon_lock_nb, - &field_recon_lock_nb_match, - &field_recon_match_mode, - }); - - checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() ); - checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() ); - checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() ); - checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() ); - field_recon_lock_duration.set_value( _recon_lock_duration ); - field_recon_lock_nb_match.set_value( _recon_lock_nb_match ); - field_recon_match_mode.set_by_value( _recon_match_mode ); - }; - - void ReconSetupViewMore::focus() { - checkbox_load_freqs.focus(); - } - - void ReconSetupView::focus() { - viewMain.focus(); - } - - ReconSetupView::ReconSetupView( - NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode } - { - add_children({ - &tab_view, - &viewMain, - &viewMore, - &button_save - }); - - button_save.on_select = [this,&nav](Button&) { - viewMain.Save( input_file , output_file ); - viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode ); - std::vector messages ; - messages.push_back( input_file ); - messages.push_back( output_file ); - messages.push_back( to_string_dec_uint( recon_lock_duration ) ); - messages.push_back( to_string_dec_uint( recon_lock_nb_match ) ); - messages.push_back( to_string_dec_uint( recon_match_mode ) ); - on_changed( messages ); - nav.pop(); - }; - } +ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, std::string input_file, std::string output_file) + : View(parent_rect), _input_file{input_file}, _output_file{output_file} { + hidden(true); + add_children({&button_load_freqs, + &text_input_file, + &button_save_freqs, + &button_output_file, + &checkbox_autosave_freqs, + &checkbox_autostart_recon, + &checkbox_continuous, + &checkbox_clear_output}); + + checkbox_autosave_freqs.set_value(persistent_memory::recon_autosave_freqs()); + checkbox_autostart_recon.set_value(persistent_memory::recon_autostart_recon()); + checkbox_continuous.set_value(persistent_memory::recon_continuous()); + checkbox_clear_output.set_value(persistent_memory::recon_clear_output()); + + text_input_file.set(_input_file); + button_output_file.set_text(_output_file); + + button_load_freqs.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) { + std::string dir_filter = "FREQMAN/"; + std::string str_file_path = new_file_path.string(); + if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder + // get the filename without txt extension so we can use load_freqman_file fcn + _input_file = new_file_path.stem().string(); + text_input_file.set(_input_file); + } else { + nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); + } + }; + }; + + button_save_freqs.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) { + std::string dir_filter = "FREQMAN/"; + std::string str_file_path = new_file_path.string(); + if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder + _output_file = new_file_path.stem().string(); + button_output_file.set_text(_output_file); + } else { + nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); + } + }; + }; + + button_output_file.on_select = [this, &nav](Button&) { + text_prompt(nav, _output_file, 28, + [this](std::string& buffer) { + _output_file = buffer; + button_output_file.set_text(_output_file); + }); + }; +}; + +void ReconSetupViewMain::Save(std::string& input_file, std::string& output_file) { + persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value()); + persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value()); + persistent_memory::set_recon_continuous(checkbox_continuous.value()); + persistent_memory::set_recon_clear_output(checkbox_clear_output.value()); + input_file = _input_file; + output_file = _output_file; +}; +void ReconSetupViewMore::Save(uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, uint32_t& recon_match_mode) { + persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value()); + persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value()); + persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value()); + persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value()); + recon_lock_duration = field_recon_lock_duration.value(); + recon_lock_nb_match = field_recon_lock_nb_match.value(); + recon_match_mode = field_recon_match_mode.selected_index_value(); +}; + +void ReconSetupViewMain::focus() { + button_load_freqs.focus(); +} + +ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect, uint32_t recon_lock_duration, uint32_t recon_lock_nb_match, uint32_t recon_match_mode) + : View(parent_rect), _recon_lock_duration{recon_lock_duration}, _recon_lock_nb_match{recon_lock_nb_match}, _recon_match_mode{recon_match_mode} { + (void)nav; + hidden(true); + + add_children({ + &checkbox_load_freqs, + &checkbox_load_ranges, + &checkbox_load_hamradios, + &checkbox_update_ranges_when_recon, + &text_recon_lock_duration, + &field_recon_lock_duration, + &text_recon_lock_nb, + &field_recon_lock_nb_match, + &field_recon_match_mode, + }); + + checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs()); + checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges()); + checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios()); + checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon()); + field_recon_lock_duration.set_value(_recon_lock_duration); + field_recon_lock_nb_match.set_value(_recon_lock_nb_match); + field_recon_match_mode.set_by_value(_recon_match_mode); +}; + +void ReconSetupViewMore::focus() { + checkbox_load_freqs.focus(); +} + +void ReconSetupView::focus() { + viewMain.focus(); +} + +ReconSetupView::ReconSetupView( + NavigationView& nav, + std::string _input_file, + std::string _output_file, + uint32_t _recon_lock_duration, + uint32_t _recon_lock_nb_match, + uint32_t _recon_match_mode) + : nav_{nav}, input_file{_input_file}, output_file{_output_file}, recon_lock_duration{_recon_lock_duration}, recon_lock_nb_match{_recon_lock_nb_match}, recon_match_mode{_recon_match_mode} { + add_children({&tab_view, + &viewMain, + &viewMore, + &button_save}); + + button_save.on_select = [this, &nav](Button&) { + viewMain.Save(input_file, output_file); + viewMore.Save(recon_lock_duration, recon_lock_nb_match, recon_match_mode); + std::vector messages; + messages.push_back(input_file); + messages.push_back(output_file); + messages.push_back(to_string_dec_uint(recon_lock_duration)); + messages.push_back(to_string_dec_uint(recon_lock_nb_match)); + messages.push_back(to_string_dec_uint(recon_match_mode)); + on_changed(messages); + nav.pop(); + }; +} } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon_settings.hpp b/firmware/application/apps/ui_recon_settings.hpp index cd00be52b..c412f6e02 100644 --- a/firmware/application/apps/ui_recon_settings.hpp +++ b/firmware/application/apps/ui_recon_settings.hpp @@ -51,8 +51,8 @@ // default number of match to have a lock #define RECON_DEF_NB_MATCH 3 -#define RECON_MIN_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL -#define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps +#define RECON_MIN_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL +#define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps // screen size helper #define SCREEN_W 240 @@ -63,164 +63,142 @@ namespace ui { - class ReconSetupViewMain : public View { - public: - ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file ); - void Save( std::string &input_file , std::string &output_file ); - void focus() override; - - private: - std::string _input_file = { "RECON" }; - std::string _output_file = { "RECON_RESULTS" }; - - Button button_load_freqs { - { 1 * 8 , 12 , 18 * 8 , 22 }, - "select input file" - }; - Text text_input_file { - { 1 * 8 , 4 + 2 * 16, 18 * 8, 22 }, - "RECON" - }; - - Button button_save_freqs { - { 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 }, - "select output file" - }; - Button button_output_file { - { 1 * 8 , 5 * 16 - 2, 18 * 8, 22 }, - "RECON_RESULTS" - }; - - Checkbox checkbox_autosave_freqs { - { 1 * 8, 7 * 16 - 4 }, - 3, - "autosave freqs" - }; - - Checkbox checkbox_autostart_recon { - { 1 * 8, 9 * 16 - 4 }, - 3, - "autostart recon" - }; - - Checkbox checkbox_continuous { - { 1 * 8, 11 * 16 - 4 }, - 3, - "continuous" - }; - Checkbox checkbox_clear_output { - { 1 * 8, 13 * 16 - 4 }, - 3, - "clear output at start" - }; - }; - - class ReconSetupViewMore : public View { - public: - ReconSetupViewMore( NavigationView& nav, Rect parent_rect , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ); - - void Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ); - - void focus() override; - - private: - - uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL ; - uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH ; - uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS ; - - Checkbox checkbox_load_freqs { - { 1 * 8, 12 }, - 3, - "input: load freqs" - }; - - Checkbox checkbox_load_ranges { - { 1 * 8, 42 }, - 3, - "input: load ranges" - }; - - Checkbox checkbox_load_hamradios { - { 1 * 8, 72 }, - 3, - "input: load hamradios" - }; - - Checkbox checkbox_update_ranges_when_recon { - { 1 * 8, 102 }, - 3, - "auto update m-ranges" - }; - Text text_recon_lock_duration { - { 1 * 8 , 132 , 22 * 8 , 22 }, - " ms (lock duration)" - }; - NumberField field_recon_lock_duration { - { 1 * 8, 132 }, // position X , Y - 4, // number of displayed digits (even empty) - { -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, // range of number - STATS_UPDATE_INTERVAL, // rotary encoder increment - ' ', // filling character - false // can loop - }; - Text text_recon_lock_nb { - { 1 * 8 , 162 , 25 * 8 , 22 }, - " x (nb lock to match freq)" - }; - NumberField field_recon_lock_nb_match { - { 1 * 8, 162 }, - 4, - { 1, 99 }, - 1, - ' ', - false - }; - OptionsField field_recon_match_mode { - { 1 * 8, 192 }, - 20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE - { - { "SQL MATCH: CONTINOUS" , 0 }, - { "SQL MATCH: SPARSE" , 1 } - } - }; - }; - - - class ReconSetupView : public View { - public: - ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ); - - std::function messages )> on_changed { }; - - void focus() override; - - std::string title() const override { return "Recon setup"; }; - - private: - - NavigationView& nav_ ; - - std::string input_file = { "RECON" }; - std::string output_file = { "RECON_RESULTS" }; - uint32_t recon_lock_duration = STATS_UPDATE_INTERVAL ; - uint32_t recon_lock_nb_match = RECON_DEF_NB_MATCH ; - uint32_t recon_match_mode = RECON_MATCH_CONTINUOUS ; - - Rect view_rect = { 0, 3 * 8, SCREEN_W, 230 }; - - ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file }; - ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode }; - - TabView tab_view { - { "Main", Color::cyan() , &viewMain }, - { "More", Color::green(), &viewMore } - }; - Button button_save { - { 9 * 8, 255, 14 * 8 , 40 }, - "SAVE" - }; - }; +class ReconSetupViewMain : public View { + public: + ReconSetupViewMain(NavigationView& nav, Rect parent_rect, std::string input_file, std::string output_file); + void Save(std::string& input_file, std::string& output_file); + void focus() override; + + private: + std::string _input_file = {"RECON"}; + std::string _output_file = {"RECON_RESULTS"}; + + Button button_load_freqs{ + {1 * 8, 12, 18 * 8, 22}, + "select input file"}; + Text text_input_file{ + {1 * 8, 4 + 2 * 16, 18 * 8, 22}, + "RECON"}; + + Button button_save_freqs{ + {1 * 8, 4 * 16 - 8, 18 * 8, 22}, + "select output file"}; + Button button_output_file{ + {1 * 8, 5 * 16 - 2, 18 * 8, 22}, + "RECON_RESULTS"}; + + Checkbox checkbox_autosave_freqs{ + {1 * 8, 7 * 16 - 4}, + 3, + "autosave freqs"}; + + Checkbox checkbox_autostart_recon{ + {1 * 8, 9 * 16 - 4}, + 3, + "autostart recon"}; + + Checkbox checkbox_continuous{ + {1 * 8, 11 * 16 - 4}, + 3, + "continuous"}; + Checkbox checkbox_clear_output{ + {1 * 8, 13 * 16 - 4}, + 3, + "clear output at start"}; +}; + +class ReconSetupViewMore : public View { + public: + ReconSetupViewMore(NavigationView& nav, Rect parent_rect, uint32_t _recon_lock_duration, uint32_t _recon_lock_nb_match, uint32_t _recon_match_mode); + + void Save(uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, uint32_t& recon_match_mode); + + void focus() override; + + private: + uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL; + uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH; + uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS; + + Checkbox checkbox_load_freqs{ + {1 * 8, 12}, + 3, + "input: load freqs"}; + + Checkbox checkbox_load_ranges{ + {1 * 8, 42}, + 3, + "input: load ranges"}; + + Checkbox checkbox_load_hamradios{ + {1 * 8, 72}, + 3, + "input: load hamradios"}; + + Checkbox checkbox_update_ranges_when_recon{ + {1 * 8, 102}, + 3, + "auto update m-ranges"}; + Text text_recon_lock_duration{ + {1 * 8, 132, 22 * 8, 22}, + " ms (lock duration)"}; + NumberField field_recon_lock_duration{ + {1 * 8, 132}, // position X , Y + 4, // number of displayed digits (even empty) + {-RECON_MAX_LOCK_DURATION, RECON_MAX_LOCK_DURATION}, // range of number + STATS_UPDATE_INTERVAL, // rotary encoder increment + ' ', // filling character + false // can loop + }; + Text text_recon_lock_nb{ + {1 * 8, 162, 25 * 8, 22}, + " x (nb lock to match freq)"}; + NumberField field_recon_lock_nb_match{ + {1 * 8, 162}, + 4, + {1, 99}, + 1, + ' ', + false}; + OptionsField field_recon_match_mode{ + {1 * 8, 192}, + 20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE + { + {"SQL MATCH: CONTINOUS", 0}, + {"SQL MATCH: SPARSE", 1}}}; +}; + +class ReconSetupView : public View { + public: + ReconSetupView(NavigationView& nav, std::string _input_file, std::string _output_file, uint32_t _recon_lock_duration, uint32_t _recon_lock_nb_match, uint32_t _recon_match_mode); + + std::function messages)> on_changed{}; + + void focus() override; + + std::string title() const override { return "Recon setup"; }; + + private: + NavigationView& nav_; + + std::string input_file = {"RECON"}; + std::string output_file = {"RECON_RESULTS"}; + uint32_t recon_lock_duration = STATS_UPDATE_INTERVAL; + uint32_t recon_lock_nb_match = RECON_DEF_NB_MATCH; + uint32_t recon_match_mode = RECON_MATCH_CONTINUOUS; + + Rect view_rect = {0, 3 * 8, SCREEN_W, 230}; + + ReconSetupViewMain viewMain{nav_, view_rect, input_file, output_file}; + ReconSetupViewMore viewMore{nav_, view_rect, recon_lock_duration, recon_lock_nb_match, recon_match_mode}; + + TabView tab_view{ + {"Main", Color::cyan(), &viewMain}, + {"More", Color::green(), &viewMore}}; + Button button_save{ + {9 * 8, 255, 14 * 8, 40}, + "SAVE"}; +}; } /* namespace ui */ diff --git a/firmware/application/apps/ui_remote.cpp b/firmware/application/apps/ui_remote.cpp index e7742924c..7572285e7 100644 --- a/firmware/application/apps/ui_remote.cpp +++ b/firmware/application/apps/ui_remote.cpp @@ -30,26 +30,22 @@ using namespace portapack; namespace ui { void RemoteView::focus() { - button.focus(); + button.focus(); } RemoteView::~RemoteView() { - //transmitter_model.disable(); - //baseband::shutdown(); + // transmitter_model.disable(); + // baseband::shutdown(); } - RemoteView::RemoteView( - NavigationView& nav -) { - add_children({ - &labels, - &button - }); - - button.on_select = [this, &nav](Button&) { - nav.pop(); - }; + NavigationView& nav) { + add_children({&labels, + &button}); + + button.on_select = [this, &nav](Button&) { + nav.pop(); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_remote.hpp b/firmware/application/apps/ui_remote.hpp index 6f69df4d1..0a990c897 100644 --- a/firmware/application/apps/ui_remote.hpp +++ b/firmware/application/apps/ui_remote.hpp @@ -27,38 +27,36 @@ namespace ui { class RemoteView : public View { -public: - RemoteView(NavigationView& nav); - ~RemoteView(); - - void focus() override; - - std::string title() const override { return "Custom remote"; }; + public: + RemoteView(NavigationView& nav); + ~RemoteView(); -private: - /*enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - tx_modes tx_mode = IDLE; + void focus() override; - struct remote_layout_t { - Point position; - std::string text; - }; - - const std::array remote_layout { };*/ - - Labels labels { - { { 1 * 8, 0 }, "Work in progress...", Color::light_grey() } - }; - - Button button { - { 60, 64, 120, 32 }, - "Exit" - }; + std::string title() const override { return "Custom remote"; }; + + private: + /*enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + tx_modes tx_mode = IDLE; + + struct remote_layout_t { + Point position; + std::string text; + }; + + const std::array remote_layout { };*/ + + Labels labels{ + {{1 * 8, 0}, "Work in progress...", Color::light_grey()}}; + + Button button{ + {60, 64, 120, 32}, + "Exit"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 08e736e5e..436dad7bb 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -27,809 +27,795 @@ using namespace portapack; namespace ui { -ScannerThread::ScannerThread(std::vector frequency_list) : frequency_list_ { std::move(frequency_list) } -{ - _manual_search = false; - create_thread(); +ScannerThread::ScannerThread(std::vector frequency_list) + : frequency_list_{std::move(frequency_list)} { + _manual_search = false; + create_thread(); } -ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz) : frequency_range_(frequency_range), def_step_hz_(def_step_hz) -{ - _manual_search = true; - create_thread(); +ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz) + : frequency_range_(frequency_range), def_step_hz_(def_step_hz) { + _manual_search = true; + create_thread(); } ScannerThread::~ScannerThread() { - stop(); + stop(); } -void ScannerThread::create_thread() -{ - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this); +void ScannerThread::create_thread() { + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this); } void ScannerThread::stop() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } + if (thread) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } } -//Set by "userpause" +// Set by "userpause" void ScannerThread::set_scanning(const bool v) { - _scanning = v; + _scanning = v; } bool ScannerThread::is_scanning() { - return _scanning; + return _scanning; } void ScannerThread::set_freq_lock(const uint32_t v) { - _freq_lock = v; + _freq_lock = v; } uint32_t ScannerThread::is_freq_lock() { - return _freq_lock; + return _freq_lock; } -//Delete an entry from frequency list -//Caller must pause scan_thread AND can't delete a second one until this field is cleared +// Delete an entry from frequency list +// Caller must pause scan_thread AND can't delete a second one until this field is cleared void ScannerThread::set_freq_del(const rf::Frequency v) { - _freq_del = v; + _freq_del = v; } -//Force a one-time forward or reverse frequency index change; OK to do this without pausing scan thread +// Force a one-time forward or reverse frequency index change; OK to do this without pausing scan thread //(used when rotary encoder is turned) void ScannerThread::set_index_stepper(const int32_t v) { - _index_stepper = v; + _index_stepper = v; } -//Set scanning direction; OK to do this without pausing scan_thread +// Set scanning direction; OK to do this without pausing scan_thread void ScannerThread::set_scanning_direction(bool fwd) { - int32_t new_stepper = fwd? 1 : -1; + int32_t new_stepper = fwd ? 1 : -1; - if (_stepper != new_stepper) { - _stepper = new_stepper; - chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction - } + if (_stepper != new_stepper) { + _stepper = new_stepper; + chThdSleepMilliseconds(300); // Give some pause after reversing scanning direction + } } msg_t ScannerThread::static_fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); - return 0; + auto obj = static_cast(arg); + obj->run(); + return 0; } void ScannerThread::run() { - RetuneMessage message { }; - - if (!_manual_search && frequency_list_.size()) { //IF NOT MANUAL MODE AND THERE IS A FREQUENCY LIST ... - int32_t size = frequency_list_.size(); - int32_t frequency_index = (_stepper > 0)? size : 0; //Forcing wraparound to starting frequency on 1st pass - - while( !chThdShouldTerminate() ) { - bool force_one_step = (_index_stepper != 0); - int32_t step = force_one_step? _index_stepper : _stepper; //_index_stepper direction takes priority - - if (_scanning || force_one_step) { //Scanning, or paused and using rotary encoder - if ((_freq_lock == 0) || force_one_step) { //normal scanning (not performing freq_lock) - frequency_index += step; - if (frequency_index >= size) //Wrap - frequency_index = 0; - else if (frequency_index < 0) - frequency_index = size-1; - - if (force_one_step) - _index_stepper = 0; - - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune - } - message.freq = frequency_list_[frequency_index]; - message.range = frequency_index; //Inform freq (for coloring purposes also!) - EventDispatcher::send_message(message); - } - else if (_freq_del != 0) { //There is a frequency to delete - for (int32_t i = 0; i < size; i++) { //Search for the freq to delete - if (frequency_list_[i] == _freq_del) - { //found: Erase it - frequency_list_.erase(frequency_list_.begin() + i); - size = frequency_list_.size(); - break; - } - } - _freq_del = 0; //deleted. - } - - chThdSleepMilliseconds(SCANNER_SLEEP_MS); //Needed to (eventually) stabilize the receiver into new freq - } - }else if(_manual_search && (def_step_hz_ > 0)) // manual search range mode - { - int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_hz_; - int64_t frequency_index = (_stepper > 0)? size : 0; //Forcing wraparound to starting frequency on 1st pass - - while( !chThdShouldTerminate() ) { - bool force_one_step = (_index_stepper != 0); - int32_t step = force_one_step? _index_stepper : _stepper; //_index_stepper direction takes priority - - if (_scanning || force_one_step) { //Scanning, or paused and using rotary encoder - if ((_freq_lock == 0) || force_one_step) { //normal scanning (not performing freq_lock) - frequency_index += step; - if (frequency_index >= size) //Wrap - frequency_index = 0; - else if (frequency_index < 0) - frequency_index = size-1; - - if (force_one_step) - _index_stepper = 0; - - receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_hz_); // Retune - } - message.freq = frequency_range_.min + frequency_index * def_step_hz_; - message.range = 0; //Inform freq (for coloring purposes also!) - EventDispatcher::send_message(message); - } - - chThdSleepMilliseconds(SCANNER_SLEEP_MS); //Needed to (eventually) stabilize the receiver into new freq - } - } + RetuneMessage message{}; + + if (!_manual_search && frequency_list_.size()) { // IF NOT MANUAL MODE AND THERE IS A FREQUENCY LIST ... + int32_t size = frequency_list_.size(); + int32_t frequency_index = (_stepper > 0) ? size : 0; // Forcing wraparound to starting frequency on 1st pass + + while (!chThdShouldTerminate()) { + bool force_one_step = (_index_stepper != 0); + int32_t step = force_one_step ? _index_stepper : _stepper; //_index_stepper direction takes priority + + if (_scanning || force_one_step) { // Scanning, or paused and using rotary encoder + if ((_freq_lock == 0) || force_one_step) { // normal scanning (not performing freq_lock) + frequency_index += step; + if (frequency_index >= size) // Wrap + frequency_index = 0; + else if (frequency_index < 0) + frequency_index = size - 1; + + if (force_one_step) + _index_stepper = 0; + + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune + } + message.freq = frequency_list_[frequency_index]; + message.range = frequency_index; // Inform freq (for coloring purposes also!) + EventDispatcher::send_message(message); + } else if (_freq_del != 0) { // There is a frequency to delete + for (int32_t i = 0; i < size; i++) { // Search for the freq to delete + if (frequency_list_[i] == _freq_del) { // found: Erase it + frequency_list_.erase(frequency_list_.begin() + i); + size = frequency_list_.size(); + break; + } + } + _freq_del = 0; // deleted. + } + + chThdSleepMilliseconds(SCANNER_SLEEP_MS); // Needed to (eventually) stabilize the receiver into new freq + } + } else if (_manual_search && (def_step_hz_ > 0)) // manual search range mode + { + int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_hz_; + int64_t frequency_index = (_stepper > 0) ? size : 0; // Forcing wraparound to starting frequency on 1st pass + + while (!chThdShouldTerminate()) { + bool force_one_step = (_index_stepper != 0); + int32_t step = force_one_step ? _index_stepper : _stepper; //_index_stepper direction takes priority + + if (_scanning || force_one_step) { // Scanning, or paused and using rotary encoder + if ((_freq_lock == 0) || force_one_step) { // normal scanning (not performing freq_lock) + frequency_index += step; + if (frequency_index >= size) // Wrap + frequency_index = 0; + else if (frequency_index < 0) + frequency_index = size - 1; + + if (force_one_step) + _index_stepper = 0; + + receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_hz_); // Retune + } + message.freq = frequency_range_.min + frequency_index * def_step_hz_; + message.range = 0; // Inform freq (for coloring purposes also!) + EventDispatcher::send_message(message); + } + + chThdSleepMilliseconds(SCANNER_SLEEP_MS); // Needed to (eventually) stabilize the receiver into new freq + } + } } void ScannerView::bigdisplay_update(int32_t v) { - if (v != bigdisplay_current_color) { - if (v!=-1) - bigdisplay_current_color = v; // -1 means refresh display but keep current color - - switch (bigdisplay_current_color) { - case BDC_GREY: big_display.set_style(&style_grey); break; - case BDC_YELLOW:big_display.set_style(&style_yellow); break; - case BDC_GREEN: big_display.set_style(&style_green); break; - case BDC_RED: big_display.set_style(&style_red); break; - default: break; - } - - // update frequency display - bigdisplay_current_frequency = current_frequency; - big_display.set(bigdisplay_current_frequency); - } else { - - // no style change, but update frequency display if it's changed - if (current_frequency != bigdisplay_current_frequency) { - bigdisplay_current_frequency = current_frequency; - big_display.set(bigdisplay_current_frequency); - } - } + if (v != bigdisplay_current_color) { + if (v != -1) + bigdisplay_current_color = v; // -1 means refresh display but keep current color + + switch (bigdisplay_current_color) { + case BDC_GREY: + big_display.set_style(&style_grey); + break; + case BDC_YELLOW: + big_display.set_style(&style_yellow); + break; + case BDC_GREEN: + big_display.set_style(&style_green); + break; + case BDC_RED: + big_display.set_style(&style_red); + break; + default: + break; + } + + // update frequency display + bigdisplay_current_frequency = current_frequency; + big_display.set(bigdisplay_current_frequency); + } else { + // no style change, but update frequency display if it's changed + if (current_frequency != bigdisplay_current_frequency) { + bigdisplay_current_frequency = current_frequency; + big_display.set(bigdisplay_current_frequency); + } + } } void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) { - current_index = freq_idx; //since it is an ongoing scan, this is a new index - current_frequency = freq; - - if (scan_thread) { - switch (scan_thread->is_freq_lock()) - { - case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING - bigdisplay_update(BDC_GREY); - break; - case 1: //STARTING LOCK FREQ - bigdisplay_update(BDC_YELLOW); - break; - case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update() - bigdisplay_update(BDC_GREEN); - break; - default: //freq lock is checking the signal, do not update display - return; - } - } - - if (!manual_search) { - if (frequency_list.size() > 0) { - text_current_index.set( to_string_dec_uint(freq_idx + 1, 3) ); - } - - if (freq_idx < description_list.size() && description_list[freq_idx].size() > 1) - desc_current_index.set( description_list[freq_idx] ); //Show description from file - else - desc_current_index.set(desc_freq_list_scan); //Show Scan file name (no description in file) - } + current_index = freq_idx; // since it is an ongoing scan, this is a new index + current_frequency = freq; + + if (scan_thread) { + switch (scan_thread->is_freq_lock()) { + case 0: // NO FREQ LOCK, ONGOING STANDARD SCANNING + bigdisplay_update(BDC_GREY); + break; + case 1: // STARTING LOCK FREQ + bigdisplay_update(BDC_YELLOW); + break; + case MAX_FREQ_LOCK: // FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update() + bigdisplay_update(BDC_GREEN); + break; + default: // freq lock is checking the signal, do not update display + return; + } + } + + if (!manual_search) { + if (frequency_list.size() > 0) { + text_current_index.set(to_string_dec_uint(freq_idx + 1, 3)); + } + + if (freq_idx < description_list.size() && description_list[freq_idx].size() > 1) + desc_current_index.set(description_list[freq_idx]); // Show description from file + else + desc_current_index.set(desc_freq_list_scan); // Show Scan file name (no description in file) + } } void ScannerView::focus() { - field_mode.focus(); + field_mode.focus(); } ScannerView::~ScannerView() { - // make sure to stop the thread before shutting down the receiver - scan_thread.reset(); - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); + // make sure to stop the thread before shutting down the receiver + scan_thread.reset(); + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); } -void ScannerView::show_max_index() { //show total number of freqs to scan - text_current_index.set("---"); - - if (frequency_list.size() == FREQMAN_MAX_PER_FILE ) { - text_max_index.set_style(&style_red); - text_max_index.set( "/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE ) + " (DB MAX!)"); - } - else { - text_max_index.set_style(&style_grey); - text_max_index.set( "/ " + to_string_dec_uint(frequency_list.size())); - } +void ScannerView::show_max_index() { // show total number of freqs to scan + text_current_index.set("---"); + + if (frequency_list.size() == FREQMAN_MAX_PER_FILE) { + text_max_index.set_style(&style_red); + text_max_index.set("/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE) + " (DB MAX!)"); + } else { + text_max_index.set_style(&style_grey); + text_max_index.set("/ " + to_string_dec_uint(frequency_list.size())); + } } ScannerView::ScannerView( - NavigationView& nav - ) : nav_ { nav } , loaded_file_name { "SCANNER" } -{ - add_children({ - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &field_volume, - &field_bw, - &field_squelch, - &field_browse_wait, - &field_lock_wait, - &button_load, - &button_clear, - &rssi, - &text_current_index, - &text_max_index, - &desc_current_index, - &big_display, - &button_manual_start, - &button_manual_end, - &field_mode, - &field_step, - &button_manual_search, - &button_pause, - &button_dir, - &button_audio_app, - &button_mic_app, - &button_add, - &button_remove - - }); - - //Populate option text for these fields - freqman_set_modulation_option( field_mode ); - freqman_set_step_option( field_step ); - - // Default starting modulation (these may be overridden in SCANNER.TXT) - change_mode(AM_MODULATION); //Default modulation - field_mode.set_by_value(AM_MODULATION); //Reflect the mode into the manual selector - field_step.set_by_value(9000); //Default step interval (Hz) - - //FUTURE: perhaps additional settings should be stored in persistent memory vs using defaults - //HELPER: Pre-setting a manual range, based on stored frequency - rf::Frequency stored_freq = persistent_memory::tuned_frequency(); - frequency_range.min = stored_freq - 1000000; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - frequency_range.max = stored_freq + 1000000; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - - // Button to load txt files from the FREQMAN folder - button_load.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this](std::filesystem::path new_file_path) { - - std::string dir_filter = "FREQMAN/"; - std::string str_file_path = new_file_path.string(); - - if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder - scan_pause(); - // get the filename without txt extension so we can use load_freqman_file fcn - std::string str_file_name = new_file_path.stem().string(); - frequency_file_load(str_file_name, true); - } else { - nav_.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); - } - }; - }; - - // Button to clear in-memory frequency list - button_clear.on_select = [this, &nav](Button&) { - if (scan_thread && frequency_list.size()) { - scan_thread->stop(); //STOP SCANNER THREAD - frequency_list.clear(); - description_list.clear(); - - show_max_index(); //UPDATE new list size on screen - text_current_index.set(""); - desc_current_index.set(desc_freq_list_scan); - scan_thread->set_freq_lock(0); //Reset the scanner lock - - //FUTURE: Consider switching to manual search mode automatically after clear (but would need to validate freq range) - } - }; - - // Button to configure starting frequency for a manual range search - button_manual_start.on_select = [this, &nav](Button& button) { - auto new_view = nav_.push(frequency_range.min); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.min = f; - button_manual_start.set_text(to_string_short_freq(f)); - }; - }; - - // Button to configure ending frequency for a manual range search - button_manual_end.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(frequency_range.max); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.max = f; - button_manual_end.set_text(to_string_short_freq(f)); - }; - }; - - // Button to pause/resume scan (note that some other buttons will trigger resume also) - button_pause.on_select = [this](ButtonWithEncoder&) { - if ( userpause ) - user_resume(); - else { - scan_pause(); - button_pause.set_text(""); //PAUSED, show resume - userpause=true; - } - }; - - // Encoder dial causes frequency change when focus is on pause button - button_pause.on_change = [this]() { - int32_t encoder_delta { (button_pause.get_encoder_delta()>0)? 1 : -1 }; - - if (scan_thread) - scan_thread->set_index_stepper(encoder_delta); - - //Restart browse timer when frequency changes - if (browse_timer != 0) - browse_timer = 1; - - button_pause.set_encoder_delta( 0 ); - }; - - // Button to switch to Audio app - button_audio_app.on_select = [this](Button&) { - if (scan_thread) - scan_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - // Button to switch to Mic app - button_mic_app.on_select = [this](Button&) { - if (scan_thread) - scan_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - // Button to delete current frequency from scan Freq List - button_remove.on_select = [this](Button&) { - if (scan_thread && (frequency_list.size() > current_index)) { - scan_thread->set_scanning(false); //PAUSE Scanning if necessary - - // Remove frequency from the Freq List in memory (it is not removed from the file) - scan_thread->set_freq_del(frequency_list[current_index]); - description_list.erase(description_list.begin() + current_index); - frequency_list.erase(frequency_list.begin() + current_index); - - show_max_index(); //UPDATE new list size on screen - desc_current_index.set(""); //Clean up description (cosmetic detail) - scan_thread->set_freq_lock(0); //Reset the scanner lock - } - }; - - // Button to toggle between Manual Search and Freq List Scan modes - button_manual_search.on_select = [this](Button&) { - if (!manual_search) { - if (!frequency_range.min || !frequency_range.max) { - nav_.display_modal("Error", "Both START and END freqs\nneed a value"); - } else if (frequency_range.min > frequency_range.max) { - nav_.display_modal("Error", "END freq\nis lower than START"); - } else { - manual_search = true; //Switch to Manual Search mode - } - } else { - manual_search = false; // Switch to List Scan mode - } - - audio::output::stop(); - - if (scan_thread) - scan_thread->stop(); //STOP SCANNER THREAD - - if ( userpause ) //If user-paused, resume - user_resume(); - - start_scan_thread(); //RESTART SCANNER THREAD in selected mode - }; - - // Mode field was changed (AM/NFM/WFM) - field_mode.on_change = [this](size_t, OptionsField::value_t v) { - receiver_model.disable(); - baseband::shutdown(); - change_mode((freqman_index_t)v); - if ( scan_thread && !scan_thread->is_scanning() ) //for some motive, audio output gets stopped. - audio::output::start(); //So if scan was stopped we resume audio - receiver_model.enable(); - }; - - // Step field was changed (Hz) -- only affects manual Search mode - field_step.on_change = [this](size_t, OptionsField::value_t v) { - (void)v; // prevent compiler Unused warning - - if ( manual_search && scan_thread ) { - //Restart scan thread with new step value - scan_thread->stop(); //STOP SCANNER THREAD - - //Resuming pause automatically - //FUTURE: Consider whether we should stay in Pause mode... - if ( userpause ) //If user-paused, resume - user_resume(); - - start_scan_thread(); //RESTART SCANNER THREAD in Manual Search mode - } - }; - - // Button to toggle Forward/Reverse - button_dir.on_select = [this](Button&) { - fwd = !fwd; - if (scan_thread) - scan_thread->set_scanning_direction(fwd); - if ( userpause ) //If user-paused, resume - user_resume(); - button_dir.set_text(fwd? "REVERSE":"FORWARD"); - bigdisplay_update(BDC_GREY); //Back to grey color - }; - - // Button to add current frequency (found during Search) to the Scan Frequency List - button_add.on_select = [this](Button&) { - File scanner_file; - const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT"; - auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt - - if (!result.is_valid()) { - const std::string frequency_to_add = "f=" - + to_string_dec_uint(current_frequency / 1000) - + to_string_dec_uint(current_frequency % 1000UL, 3, '0'); - - bool found=false; - constexpr size_t buffer_size = 1024; - char buffer[buffer_size]; - - for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) { - size_t adjusted_buffer_size; - if (pointer + buffer_size >= scanner_file.size()) { - memset(buffer, 0, sizeof(buffer)); - adjusted_buffer_size = scanner_file.size() - pointer; - } - else { - adjusted_buffer_size = buffer_size; - } - - scanner_file.seek(pointer); - scanner_file.read(buffer, adjusted_buffer_size); - - for (size_t i = 0; i < adjusted_buffer_size; ++i) { - if (buffer[i] == frequency_to_add.data()[freq_str_idx]) { - ++freq_str_idx; - if (freq_str_idx >= frequency_to_add.size()) { - found = true; - break; - } - } - else { - freq_str_idx = 0; - } - } - - if (found) { - break; - } - } - - if (found) { - nav_.display_modal("Error", "Frequency already exists"); - bigdisplay_update(-1); //After showing an error - } - else { - scanner_file.append(freq_file_path); //Second: append if it is not there - scanner_file.write_line(frequency_to_add); - - // Add to frequency_list in memory too, since we can now switch back from manual mode - // Note that we are allowing freqs to be added to file (code above) that exceed the max count we can load into memory - if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { - frequency_list.push_back(current_frequency); - description_list.push_back(""); - - show_max_index(); //Display updated frequency list size - } - } - } else - { - nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq."); - bigdisplay_update(-1); //After showing an error - } - }; - - //PRE-CONFIGURATION: - field_browse_wait.on_change = [this](int32_t v) { browse_wait = v; }; - field_browse_wait.set_value(5); - - field_lock_wait.on_change = [this](int32_t v) { lock_wait = v; }; - field_lock_wait.set_value(2); - - field_squelch.on_change = [this](int32_t v) { squelch = v; }; - field_squelch.set_value(-10); - - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; - - // LEARN FREQUENCIES - std::string scanner_txt = "SCANNER"; - frequency_file_load(scanner_txt); + NavigationView& nav) + : nav_{nav}, loaded_file_name{"SCANNER"} { + add_children({&labels, + &field_lna, + &field_vga, + &field_rf_amp, + &field_volume, + &field_bw, + &field_squelch, + &field_browse_wait, + &field_lock_wait, + &button_load, + &button_clear, + &rssi, + &text_current_index, + &text_max_index, + &desc_current_index, + &big_display, + &button_manual_start, + &button_manual_end, + &field_mode, + &field_step, + &button_manual_search, + &button_pause, + &button_dir, + &button_audio_app, + &button_mic_app, + &button_add, + &button_remove + + }); + + // Populate option text for these fields + freqman_set_modulation_option(field_mode); + freqman_set_step_option(field_step); + + // Default starting modulation (these may be overridden in SCANNER.TXT) + change_mode(AM_MODULATION); // Default modulation + field_mode.set_by_value(AM_MODULATION); // Reflect the mode into the manual selector + field_step.set_by_value(9000); // Default step interval (Hz) + + // FUTURE: perhaps additional settings should be stored in persistent memory vs using defaults + // HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + frequency_range.min = stored_freq - 1000000; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + frequency_range.max = stored_freq + 1000000; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + + // Button to load txt files from the FREQMAN folder + button_load.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this](std::filesystem::path new_file_path) { + std::string dir_filter = "FREQMAN/"; + std::string str_file_path = new_file_path.string(); + + if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder + scan_pause(); + // get the filename without txt extension so we can use load_freqman_file fcn + std::string str_file_name = new_file_path.stem().string(); + frequency_file_load(str_file_name, true); + } else { + nav_.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); + } + }; + }; + + // Button to clear in-memory frequency list + button_clear.on_select = [this, &nav](Button&) { + if (scan_thread && frequency_list.size()) { + scan_thread->stop(); // STOP SCANNER THREAD + frequency_list.clear(); + description_list.clear(); + + show_max_index(); // UPDATE new list size on screen + text_current_index.set(""); + desc_current_index.set(desc_freq_list_scan); + scan_thread->set_freq_lock(0); // Reset the scanner lock + + // FUTURE: Consider switching to manual search mode automatically after clear (but would need to validate freq range) + } + }; + + // Button to configure starting frequency for a manual range search + button_manual_start.on_select = [this, &nav](Button& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + // Button to configure ending frequency for a manual range search + button_manual_end.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + // Button to pause/resume scan (note that some other buttons will trigger resume also) + button_pause.on_select = [this](ButtonWithEncoder&) { + if (userpause) + user_resume(); + else { + scan_pause(); + button_pause.set_text(""); // PAUSED, show resume + userpause = true; + } + }; + + // Encoder dial causes frequency change when focus is on pause button + button_pause.on_change = [this]() { + int32_t encoder_delta{(button_pause.get_encoder_delta() > 0) ? 1 : -1}; + + if (scan_thread) + scan_thread->set_index_stepper(encoder_delta); + + // Restart browse timer when frequency changes + if (browse_timer != 0) + browse_timer = 1; + + button_pause.set_encoder_delta(0); + }; + + // Button to switch to Audio app + button_audio_app.on_select = [this](Button&) { + if (scan_thread) + scan_thread->stop(); + nav_.pop(); + nav_.push(); + }; + + // Button to switch to Mic app + button_mic_app.on_select = [this](Button&) { + if (scan_thread) + scan_thread->stop(); + nav_.pop(); + nav_.push(); + }; + + // Button to delete current frequency from scan Freq List + button_remove.on_select = [this](Button&) { + if (scan_thread && (frequency_list.size() > current_index)) { + scan_thread->set_scanning(false); // PAUSE Scanning if necessary + + // Remove frequency from the Freq List in memory (it is not removed from the file) + scan_thread->set_freq_del(frequency_list[current_index]); + description_list.erase(description_list.begin() + current_index); + frequency_list.erase(frequency_list.begin() + current_index); + + show_max_index(); // UPDATE new list size on screen + desc_current_index.set(""); // Clean up description (cosmetic detail) + scan_thread->set_freq_lock(0); // Reset the scanner lock + } + }; + + // Button to toggle between Manual Search and Freq List Scan modes + button_manual_search.on_select = [this](Button&) { + if (!manual_search) { + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + manual_search = true; // Switch to Manual Search mode + } + } else { + manual_search = false; // Switch to List Scan mode + } + + audio::output::stop(); + + if (scan_thread) + scan_thread->stop(); // STOP SCANNER THREAD + + if (userpause) // If user-paused, resume + user_resume(); + + start_scan_thread(); // RESTART SCANNER THREAD in selected mode + }; + + // Mode field was changed (AM/NFM/WFM) + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + receiver_model.disable(); + baseband::shutdown(); + change_mode((freqman_index_t)v); + if (scan_thread && !scan_thread->is_scanning()) // for some motive, audio output gets stopped. + audio::output::start(); // So if scan was stopped we resume audio + receiver_model.enable(); + }; + + // Step field was changed (Hz) -- only affects manual Search mode + field_step.on_change = [this](size_t, OptionsField::value_t v) { + (void)v; // prevent compiler Unused warning + + if (manual_search && scan_thread) { + // Restart scan thread with new step value + scan_thread->stop(); // STOP SCANNER THREAD + + // Resuming pause automatically + // FUTURE: Consider whether we should stay in Pause mode... + if (userpause) // If user-paused, resume + user_resume(); + + start_scan_thread(); // RESTART SCANNER THREAD in Manual Search mode + } + }; + + // Button to toggle Forward/Reverse + button_dir.on_select = [this](Button&) { + fwd = !fwd; + if (scan_thread) + scan_thread->set_scanning_direction(fwd); + if (userpause) // If user-paused, resume + user_resume(); + button_dir.set_text(fwd ? "REVERSE" : "FORWARD"); + bigdisplay_update(BDC_GREY); // Back to grey color + }; + + // Button to add current frequency (found during Search) to the Scan Frequency List + button_add.on_select = [this](Button&) { + File scanner_file; + const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT"; + auto result = scanner_file.open(freq_file_path); // First search if freq is already in txt + + if (!result.is_valid()) { + const std::string frequency_to_add = "f=" + to_string_dec_uint(current_frequency / 1000) + to_string_dec_uint(current_frequency % 1000UL, 3, '0'); + + bool found = false; + constexpr size_t buffer_size = 1024; + char buffer[buffer_size]; + + for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) { + size_t adjusted_buffer_size; + if (pointer + buffer_size >= scanner_file.size()) { + memset(buffer, 0, sizeof(buffer)); + adjusted_buffer_size = scanner_file.size() - pointer; + } else { + adjusted_buffer_size = buffer_size; + } + + scanner_file.seek(pointer); + scanner_file.read(buffer, adjusted_buffer_size); + + for (size_t i = 0; i < adjusted_buffer_size; ++i) { + if (buffer[i] == frequency_to_add.data()[freq_str_idx]) { + ++freq_str_idx; + if (freq_str_idx >= frequency_to_add.size()) { + found = true; + break; + } + } else { + freq_str_idx = 0; + } + } + + if (found) { + break; + } + } + + if (found) { + nav_.display_modal("Error", "Frequency already exists"); + bigdisplay_update(-1); // After showing an error + } else { + scanner_file.append(freq_file_path); // Second: append if it is not there + scanner_file.write_line(frequency_to_add); + + // Add to frequency_list in memory too, since we can now switch back from manual mode + // Note that we are allowing freqs to be added to file (code above) that exceed the max count we can load into memory + if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { + frequency_list.push_back(current_frequency); + description_list.push_back(""); + + show_max_index(); // Display updated frequency list size + } + } + } else { + nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq."); + bigdisplay_update(-1); // After showing an error + } + }; + + // PRE-CONFIGURATION: + field_browse_wait.on_change = [this](int32_t v) { browse_wait = v; }; + field_browse_wait.set_value(5); + + field_lock_wait.on_change = [this](int32_t v) { lock_wait = v; }; + field_lock_wait.set_value(2); + + field_squelch.on_change = [this](int32_t v) { squelch = v; }; + field_squelch.set_value(-10); + + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + + // LEARN FREQUENCIES + std::string scanner_txt = "SCANNER"; + frequency_file_load(scanner_txt); } void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) { - - bool found_range { false }; - bool found_single { false }; - freqman_index_t def_mod_index { -1 }; - freqman_index_t def_bw_index { -1 }; - freqman_index_t def_step_index { -1 }; - - // stop everything running now if required - if (stop_all_before) { - scan_thread->stop(); - frequency_list.clear(); // clear the existing frequency list (expected behavior) - description_list.clear(); - } - - if ( load_freqman_file(file_name, database) ) { - loaded_file_name = file_name; // keep loaded filename in memory - for(auto& entry : database) { // READ LINE PER LINE - if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { //We got space! - // - // Get modulation & bw & step from file if specified - // Note these values could be different for each line in the file, but we only look at the first one - // - // Note that freqman requires a very specific string for these parameters, - // so check syntax in frequency file if specified value isn't being loaded - // - if (def_mod_index == -1) - def_mod_index = entry.modulation; - - if (def_bw_index == -1) - def_bw_index = entry.bandwidth; - - if (def_step_index == -1) - def_step_index = entry.step; - - // Get frequency - if (entry.type == RANGE) { - if (!found_range) { - // Set Start & End Search Range instead of populating the small in-memory frequency table - // NOTE: There may be multiple single frequencies in file, but only one search range is supported. - found_range = true; - frequency_range.min = entry.frequency_a; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - frequency_range.max = entry.frequency_b; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - } - } - else if ( entry.type == SINGLE ) { - found_single = true; - frequency_list.push_back(entry.frequency_a); - description_list.push_back(entry.description); - } - else if ( entry.type == HAMRADIO ) { - // For HAM repeaters, add both receive & transmit frequencies to scan list and modify description - // (FUTURE fw versions might handle these differently) - found_single = true; - frequency_list.push_back(entry.frequency_a); - description_list.push_back("R:" + entry.description); - - if ( (entry.frequency_a != entry.frequency_b) && - (frequency_list.size() < FREQMAN_MAX_PER_FILE) ) { - frequency_list.push_back(entry.frequency_b); - description_list.push_back("T:" + entry.description); - } - } - } - else - { - break; //No more space: Stop reading the txt file ! - } - } - } - else - { - loaded_file_name = "SCANNER"; // back to the default frequency file - desc_current_index.set(" NO " + file_name + ".TXT FILE ..." ); - } - - desc_freq_list_scan = loaded_file_name+".TXT"; - if (desc_freq_list_scan.length() > 23) { // Truncate description and add ellipses if long file name - desc_freq_list_scan.resize(20); - desc_freq_list_scan = desc_freq_list_scan + "..."; - } - - if ((def_mod_index != -1) && (def_mod_index != (freqman_index_t)field_mode.selected_index_value())) - field_mode.set_by_value( def_mod_index ); //Update mode (also triggers a change callback that disables & reenables RF background) - - if (def_bw_index != -1) //Update BW if specified in file - field_bw.set_selected_index( def_bw_index ); - - if (def_step_index != -1) //Update step if specified in file - field_step.set_selected_index( def_step_index ); - - audio::output::stop(); - - if ( userpause ) //If user-paused, resume - user_resume(); - - // Scan list if we found one, otherwise do manual range search - manual_search = !found_single; - - start_scan_thread(); + bool found_range{false}; + bool found_single{false}; + freqman_index_t def_mod_index{-1}; + freqman_index_t def_bw_index{-1}; + freqman_index_t def_step_index{-1}; + + // stop everything running now if required + if (stop_all_before) { + scan_thread->stop(); + frequency_list.clear(); // clear the existing frequency list (expected behavior) + description_list.clear(); + } + + if (load_freqman_file(file_name, database)) { + loaded_file_name = file_name; // keep loaded filename in memory + for (auto& entry : database) { // READ LINE PER LINE + if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { // We got space! + // + // Get modulation & bw & step from file if specified + // Note these values could be different for each line in the file, but we only look at the first one + // + // Note that freqman requires a very specific string for these parameters, + // so check syntax in frequency file if specified value isn't being loaded + // + if (def_mod_index == -1) + def_mod_index = entry.modulation; + + if (def_bw_index == -1) + def_bw_index = entry.bandwidth; + + if (def_step_index == -1) + def_step_index = entry.step; + + // Get frequency + if (entry.type == RANGE) { + if (!found_range) { + // Set Start & End Search Range instead of populating the small in-memory frequency table + // NOTE: There may be multiple single frequencies in file, but only one search range is supported. + found_range = true; + frequency_range.min = entry.frequency_a; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + frequency_range.max = entry.frequency_b; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + } + } else if (entry.type == SINGLE) { + found_single = true; + frequency_list.push_back(entry.frequency_a); + description_list.push_back(entry.description); + } else if (entry.type == HAMRADIO) { + // For HAM repeaters, add both receive & transmit frequencies to scan list and modify description + // (FUTURE fw versions might handle these differently) + found_single = true; + frequency_list.push_back(entry.frequency_a); + description_list.push_back("R:" + entry.description); + + if ((entry.frequency_a != entry.frequency_b) && + (frequency_list.size() < FREQMAN_MAX_PER_FILE)) { + frequency_list.push_back(entry.frequency_b); + description_list.push_back("T:" + entry.description); + } + } + } else { + break; // No more space: Stop reading the txt file ! + } + } + } else { + loaded_file_name = "SCANNER"; // back to the default frequency file + desc_current_index.set(" NO " + file_name + ".TXT FILE ..."); + } + + desc_freq_list_scan = loaded_file_name + ".TXT"; + if (desc_freq_list_scan.length() > 23) { // Truncate description and add ellipses if long file name + desc_freq_list_scan.resize(20); + desc_freq_list_scan = desc_freq_list_scan + "..."; + } + + if ((def_mod_index != -1) && (def_mod_index != (freqman_index_t)field_mode.selected_index_value())) + field_mode.set_by_value(def_mod_index); // Update mode (also triggers a change callback that disables & reenables RF background) + + if (def_bw_index != -1) // Update BW if specified in file + field_bw.set_selected_index(def_bw_index); + + if (def_step_index != -1) // Update step if specified in file + field_step.set_selected_index(def_step_index); + + audio::output::stop(); + + if (userpause) // If user-paused, resume + user_resume(); + + // Scan list if we found one, otherwise do manual range search + manual_search = !found_single; + + start_scan_thread(); } void ScannerView::update_squelch_while_paused(int32_t max_db) { - //Update audio & color based on signal level even if paused - if (++color_timer > 2) { //Counter to reduce color toggling when weak signal - if (max_db > squelch) { - audio::output::start(); //Re-enable audio when signal goes above squelch - on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause - bigdisplay_update(BDC_GREEN); - } else { - audio::output::stop(); //Silence audio when signal drops below squelch - bigdisplay_update(BDC_GREY); //Back to grey color - } - - color_timer = 0; - } + // Update audio & color based on signal level even if paused + if (++color_timer > 2) { // Counter to reduce color toggling when weak signal + if (max_db > squelch) { + audio::output::start(); // Re-enable audio when signal goes above squelch + on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause + bigdisplay_update(BDC_GREEN); + } else { + audio::output::stop(); // Silence audio when signal drops below squelch + bigdisplay_update(BDC_GREY); // Back to grey color + } + + color_timer = 0; + } } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - - if (userpause) { - update_squelch_while_paused(statistics.max_db); - } - else if ( scan_thread ) //Scanning not user-paused - { - //Resume regardless of signal strength if browse time reached - if ((browse_wait != 0) && (browse_timer >= (browse_wait * STATISTICS_UPDATES_PER_SEC)) ) { - browse_timer = 0; - scan_resume(); //Resume scanning - } - else - { - if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch) - if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //Pause scanning when signal checking time reached - if (!browse_timer) //Don't bother pausing if already paused - scan_pause(); - browse_timer++; // browse_timer!=0 is also an indication that we've paused the scan - update_squelch_while_paused(statistics.max_db); - } else { - scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal - if (browse_timer) //Continue incrementing browse timer while paused - browse_timer++; - } - lock_timer = 0; //Keep resetting lock timer while signal remains - } else { //There is NOTHING on the air - if (!browse_timer) { - // Signal lost and scan was never paused - if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ? - bigdisplay_update(BDC_GREY); //Back to grey color - scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no sig - } - } else { - //Signal lost and scan is still paused - lock_timer++; //Bump paused time - if (lock_timer >= (lock_wait * STATISTICS_UPDATES_PER_SEC)) { //Stay on freq until lock_wait time elapses - browse_timer = 0; - scan_resume(); - } else { - browse_timer++; //Bump browse time too (may hit that limit before lock_timer reached) - update_squelch_while_paused(statistics.max_db); - } - } - } - } - } + if (userpause) { + update_squelch_while_paused(statistics.max_db); + } else if (scan_thread) // Scanning not user-paused + { + // Resume regardless of signal strength if browse time reached + if ((browse_wait != 0) && (browse_timer >= (browse_wait * STATISTICS_UPDATES_PER_SEC))) { + browse_timer = 0; + scan_resume(); // Resume scanning + } else { + if (statistics.max_db > squelch) { // There is something on the air...(statistics.max_db > -squelch) + if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { // Pause scanning when signal checking time reached + if (!browse_timer) // Don't bother pausing if already paused + scan_pause(); + browse_timer++; // browse_timer!=0 is also an indication that we've paused the scan + update_squelch_while_paused(statistics.max_db); + } else { + scan_thread->set_freq_lock(scan_thread->is_freq_lock() + 1); // in lock period, still analyzing the signal + if (browse_timer) // Continue incrementing browse timer while paused + browse_timer++; + } + lock_timer = 0; // Keep resetting lock timer while signal remains + } else { // There is NOTHING on the air + if (!browse_timer) { + // Signal lost and scan was never paused + if (scan_thread->is_freq_lock() > 0) { // But are we already in freq_lock ? + bigdisplay_update(BDC_GREY); // Back to grey color + scan_thread->set_freq_lock(0); // Reset the scanner lock, since there is no sig + } + } else { + // Signal lost and scan is still paused + lock_timer++; // Bump paused time + if (lock_timer >= (lock_wait * STATISTICS_UPDATES_PER_SEC)) { // Stay on freq until lock_wait time elapses + browse_timer = 0; + scan_resume(); + } else { + browse_timer++; // Bump browse time too (may hit that limit before lock_timer reached) + update_squelch_while_paused(statistics.max_db); + } + } + } + } + } } void ScannerView::scan_pause() { - if (scan_thread && scan_thread->is_scanning()) { - scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan - scan_thread->set_scanning(false); // WE STOP SCANNING - } - audio::output::start(); - on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause + if (scan_thread && scan_thread->is_scanning()) { + scan_thread->set_freq_lock(0); // Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan + scan_thread->set_scanning(false); // WE STOP SCANNING + } + audio::output::start(); + on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause } void ScannerView::scan_resume() { - audio::output::stop(); - bigdisplay_update(BDC_GREY); //Back to grey color + audio::output::stop(); + bigdisplay_update(BDC_GREY); // Back to grey color - if (scan_thread) { - scan_thread->set_index_stepper(fwd? 1 : -1); - scan_thread->set_scanning(true); // RESUME! - } + if (scan_thread) { + scan_thread->set_index_stepper(fwd ? 1 : -1); + scan_thread->set_scanning(true); // RESUME! + } } void ScannerView::user_resume() { - browse_timer = browse_wait * STATISTICS_UPDATES_PER_SEC; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq. - button_pause.set_text(""); //Show button for pause, arrows indicate rotary encoder enabled for freq change - userpause=false; //Resume scanning + browse_timer = browse_wait * STATISTICS_UPDATES_PER_SEC; // Will trigger a scan_resume() on_statistics_update, also advancing to next freq. + button_pause.set_text(""); // Show button for pause, arrows indicate rotary encoder enabled for freq change + userpause = false; // Resume scanning } void ScannerView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); } -void ScannerView::change_mode(freqman_index_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread() - using option_t = std::pair; - using options_t = std::vector; - options_t bw; - field_bw.on_change = [this](size_t n, OptionsField::value_t) { - (void)n; //avoid unused warning - }; - - switch (new_mod) { - case AM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - field_bw.set_selected_index(0); - receiver_model.set_am_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - break; - case NFM_MODULATION: //bw 16k (2) default - freqman_set_bandwidth_option( new_mod , field_bw ); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - field_bw.set_selected_index(2); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - break; - case WFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - field_bw.set_selected_index(0); - receiver_model.set_wfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000); - break; - default: - break; - } +void ScannerView::change_mode(freqman_index_t new_mod) { // Before this, do a scan_thread->stop(); After this do a start_scan_thread() + using option_t = std::pair; + using options_t = std::vector; + options_t bw; + field_bw.on_change = [this](size_t n, OptionsField::value_t) { + (void)n; // avoid unused warning + }; + + switch (new_mod) { + case AM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + field_bw.set_selected_index(0); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + break; + case NFM_MODULATION: // bw 16k (2) default + freqman_set_bandwidth_option(new_mod, field_bw); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + field_bw.set_selected_index(2); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + break; + case WFM_MODULATION: + freqman_set_bandwidth_option(new_mod, field_bw); + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + field_bw.set_selected_index(0); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(2000000); + break; + default: + break; + } } void ScannerView::start_scan_thread() { - receiver_model.enable(); - receiver_model.set_squelch_level(0); - show_max_index(); - - //Start Scanner Thread - //FUTURE: Consider passing additional control flags (fwd,userpause,etc) to scanner thread at start (perhaps in a data structure) - if (manual_search) { - button_manual_search.set_text("SCAN"); //Update meaning of Manual Scan button - desc_current_index.set(desc_freq_range_search); - scan_thread = std::make_unique(frequency_range, field_step.selected_index_value() ); - } else { - button_manual_search.set_text("SRCH"); //Update meaning of Manual Scan button - desc_current_index.set(desc_freq_list_scan); - scan_thread = std::make_unique(frequency_list); - } - - scan_thread->set_scanning_direction(fwd); + receiver_model.enable(); + receiver_model.set_squelch_level(0); + show_max_index(); + + // Start Scanner Thread + // FUTURE: Consider passing additional control flags (fwd,userpause,etc) to scanner thread at start (perhaps in a data structure) + if (manual_search) { + button_manual_search.set_text("SCAN"); // Update meaning of Manual Scan button + desc_current_index.set(desc_freq_range_search); + scan_thread = std::make_unique(frequency_range, field_step.selected_index_value()); + } else { + button_manual_search.set_text("SRCH"); // Update meaning of Manual Scan button + desc_current_index.set(desc_freq_list_scan); + scan_thread = std::make_unique(frequency_list); + } + + scan_thread->set_scanning_direction(fwd); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 4d17843cd..cfe86bb36 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -33,296 +33,284 @@ #include "string_format.hpp" #include "file.hpp" -#define SCANNER_SLEEP_MS 50 //ms that Scanner Thread sleeps per loop -#define STATISTICS_UPDATES_PER_SEC 10 -#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous +#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop +#define STATISTICS_UPDATES_PER_SEC 10 +#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous namespace ui { class ScannerThread { -public: - ScannerThread(std::vector frequency_list); - ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz); - ~ScannerThread(); - - void set_scanning(const bool v); - bool is_scanning(); - - void set_freq_lock(const uint32_t v); - uint32_t is_freq_lock(); - - void set_freq_del(const rf::Frequency v); - void set_index_stepper(const int32_t v); - void set_scanning_direction(bool fwd); - - void stop(); - - ScannerThread(const ScannerThread&) = delete; - ScannerThread(ScannerThread&&) = delete; - ScannerThread& operator=(const ScannerThread&) = delete; - ScannerThread& operator=(ScannerThread&&) = delete; - -private: - std::vector frequency_list_ { }; - jammer::jammer_range_t frequency_range_ {false, 0, 0}; - size_t def_step_hz_ { 0 }; - Thread* thread { nullptr }; - - bool _scanning { true }; - bool _manual_search { false }; - uint32_t _freq_lock { 0 }; - rf::Frequency _freq_del { 0 }; - uint32_t _freq_idx { 0 }; - int32_t _stepper { 1 }; - int32_t _index_stepper { 0 }; - static msg_t static_fn(void* arg); - void run(); - void create_thread(); + public: + ScannerThread(std::vector frequency_list); + ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz); + ~ScannerThread(); + + void set_scanning(const bool v); + bool is_scanning(); + + void set_freq_lock(const uint32_t v); + uint32_t is_freq_lock(); + + void set_freq_del(const rf::Frequency v); + void set_index_stepper(const int32_t v); + void set_scanning_direction(bool fwd); + + void stop(); + + ScannerThread(const ScannerThread&) = delete; + ScannerThread(ScannerThread&&) = delete; + ScannerThread& operator=(const ScannerThread&) = delete; + ScannerThread& operator=(ScannerThread&&) = delete; + + private: + std::vector frequency_list_{}; + jammer::jammer_range_t frequency_range_{false, 0, 0}; + size_t def_step_hz_{0}; + Thread* thread{nullptr}; + + bool _scanning{true}; + bool _manual_search{false}; + uint32_t _freq_lock{0}; + rf::Frequency _freq_del{0}; + uint32_t _freq_idx{0}; + int32_t _stepper{1}; + int32_t _index_stepper{0}; + static msg_t static_fn(void* arg); + void run(); + void create_thread(); }; class ScannerView : public View { -public: - ScannerView(NavigationView& nav); - ~ScannerView(); - - void focus() override; - - const Style style_grey { // scanning - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - - const Style style_yellow { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::dark_yellow(), - }; - - const Style style_green { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - - const Style style_red { //erasing freq - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - std::string title() const override { return "Scanner"; }; - std::vector frequency_list{ }; - std::vector description_list { }; - -//void set_parent_rect(const Rect new_parent_rect) override; - -private: - NavigationView& nav_; - - void start_scan_thread(); - void change_mode(freqman_index_t mod_type); - void show_max_index(); - void scan_pause(); - void scan_resume(); - void user_resume(); - void frequency_file_load(std::string file_name, bool stop_all_before = false); - void bigdisplay_update(int32_t); - void update_squelch_while_paused(int32_t max_db); - void on_statistics_update(const ChannelStatistics& statistics); - void on_headphone_volume_changed(int32_t v); - void handle_retune(int64_t freq, uint32_t freq_idx); - - jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too... - int32_t squelch { 0 }; - uint32_t browse_timer { 0 }; - uint32_t lock_timer { 0 }; - uint32_t color_timer { 0 }; - int32_t bigdisplay_current_color { -2 }; - rf::Frequency bigdisplay_current_frequency { 0 }; - uint32_t browse_wait { 0 }; - uint32_t lock_wait { 0 }; - freqman_db database { }; - std::string loaded_file_name; - uint32_t current_index { 0 }; - rf::Frequency current_frequency { 0 }; - bool userpause { false }; - bool manual_search { false }; - bool fwd { true }; //to preserve direction setting even if scan_thread restarted - - enum bigdisplay_color_type { - BDC_GREY, - BDC_YELLOW, - BDC_GREEN, - BDC_RED - }; - - std::string desc_freq_range_search = "SEARCHING..."; - std::string desc_freq_list_scan = ""; - - Labels labels { - { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, - { { 0 * 8, 1 * 16 }, "BW: SQ: Wsa: Wsl:", Color::light_grey() }, - { { 0 * 8, 10 * 16 }, "SRCH START SEARCH END SWITCH", Color::light_grey() }, - - { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() }, - { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() }, - }; - - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; - - NumberField field_volume { - { 24 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - OptionsField field_bw { - { 3 * 8, 1 * 16 }, - 6, - { } - }; - - NumberField field_squelch { - { 13 * 8, 1 * 16 }, - 3, - { -90, 20 }, - 1, - ' ', - }; - - NumberField field_browse_wait { //Signal-Active wait timer - time to wait before moving on even when signal locked - { 21 * 8, 1 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - NumberField field_lock_wait { //Signal-Lost wait timer - time to wait before moving on after losing signal lock - { 28 * 8, 1 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - RSSI rssi { - { 0 * 16, 2 * 16, 15 * 16, 8 }, - }; - - Text text_current_index { - { 0, 3 * 16, 3 * 8, 16 }, - }; - - Text text_max_index { - { 4 * 8, 3 * 16, 18 * 8, 16 }, - }; - - Text desc_current_index { - {0, 4 * 16, 240 - 6 * 8, 16 }, - }; - - BigFrequency big_display { //Show frequency in glamour - { 4, 6 * 16, 28 * 8, 52 }, - 0 - }; - - Button button_manual_start { - { 0 * 8, 11 * 16, 11 * 8, 28 }, - "" - }; - - Button button_manual_end { - { 12 * 8, 11 * 16, 11 * 8, 28 }, - "" - }; - - Button button_manual_search { - { 24 * 8, 11 * 16, 6 * 8, 28 }, - "" - }; - - OptionsField field_mode { - { 5 * 8, (26 * 8) + 4 }, - 6, - { } //Text strings get filled by freqman_set_modulation_option() - }; - - OptionsField field_step { - { 17 * 8, (26 * 8) + 4 }, - 12, - { } //Text strings get filled by freqman_set_step_option() - }; - - ButtonWithEncoder button_pause { - { 0, (15 * 16) - 4, 72, 28 }, - "" - }; - - Button button_dir { - { 0, (35 * 8) - 4, 72, 28 }, - "REVERSE" - }; - - Button button_audio_app { - { 84, (15 * 16) - 4, 72, 28 }, - "AUDIO" - }; - - Button button_mic_app { - { 84, (35 * 8) - 4, 72, 28 }, - "MIC TX" - }; - - Button button_add { - { 168, (15 * 16) - 4, 72, 28 }, - "ADD FQ" - }; - - Button button_load { - { 24 * 8, 3 * 16 - 10, 6 * 8, 22 }, - "LOAD" - }; - - Button button_clear { - { 24 * 8, 4 * 16, 6 * 8, 22 }, - "MCLR" - }; - - Button button_remove { - { 168, (35 * 8) - 4, 72, 28 }, - "DEL FQ" - }; - - std::unique_ptr scan_thread { }; - - MessageHandlerRegistration message_handler_retune { - Message::ID::Retune, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_retune(message.freq, message.range); - } - }; - - MessageHandlerRegistration message_handler_stats { - Message::ID::ChannelStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; + public: + ScannerView(NavigationView& nav); + ~ScannerView(); + + void focus() override; + + const Style style_grey{ + // scanning + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + const Style style_yellow{ + // Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::dark_yellow(), + }; + + const Style style_green{ + // Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + const Style style_red{ + // erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + std::string title() const override { return "Scanner"; }; + std::vector frequency_list{}; + std::vector description_list{}; + + // void set_parent_rect(const Rect new_parent_rect) override; + + private: + NavigationView& nav_; + + void start_scan_thread(); + void change_mode(freqman_index_t mod_type); + void show_max_index(); + void scan_pause(); + void scan_resume(); + void user_resume(); + void frequency_file_load(std::string file_name, bool stop_all_before = false); + void bigdisplay_update(int32_t); + void update_squelch_while_paused(int32_t max_db); + void on_statistics_update(const ChannelStatistics& statistics); + void on_headphone_volume_changed(int32_t v); + void handle_retune(int64_t freq, uint32_t freq_idx); + + jammer::jammer_range_t frequency_range{false, 0, 0}; // perfect for manual scan task too... + int32_t squelch{0}; + uint32_t browse_timer{0}; + uint32_t lock_timer{0}; + uint32_t color_timer{0}; + int32_t bigdisplay_current_color{-2}; + rf::Frequency bigdisplay_current_frequency{0}; + uint32_t browse_wait{0}; + uint32_t lock_wait{0}; + freqman_db database{}; + std::string loaded_file_name; + uint32_t current_index{0}; + rf::Frequency current_frequency{0}; + bool userpause{false}; + bool manual_search{false}; + bool fwd{true}; // to preserve direction setting even if scan_thread restarted + + enum bigdisplay_color_type { + BDC_GREY, + BDC_YELLOW, + BDC_GREEN, + BDC_RED + }; + + std::string desc_freq_range_search = "SEARCHING..."; + std::string desc_freq_list_scan = ""; + + Labels labels{ + {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL:", Color::light_grey()}, + {{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Color::light_grey()}, + {{0 * 8, 10 * 16}, "SRCH START SEARCH END SWITCH", Color::light_grey()}, + + {{0 * 8, (26 * 8) + 4}, "MODE:", Color::light_grey()}, + {{11 * 8, (26 * 8) + 4}, "STEP:", Color::light_grey()}, + }; + + LNAGainField field_lna{ + {4 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {11 * 8, 0 * 16}}; + + RFAmpField field_rf_amp{ + {18 * 8, 0 * 16}}; + + NumberField field_volume{ + {24 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + OptionsField field_bw{ + {3 * 8, 1 * 16}, + 6, + {}}; + + NumberField field_squelch{ + {13 * 8, 1 * 16}, + 3, + {-90, 20}, + 1, + ' ', + }; + + NumberField field_browse_wait{ + // Signal-Active wait timer - time to wait before moving on even when signal locked + {21 * 8, 1 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + NumberField field_lock_wait{ + // Signal-Lost wait timer - time to wait before moving on after losing signal lock + {28 * 8, 1 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + RSSI rssi{ + {0 * 16, 2 * 16, 15 * 16, 8}, + }; + + Text text_current_index{ + {0, 3 * 16, 3 * 8, 16}, + }; + + Text text_max_index{ + {4 * 8, 3 * 16, 18 * 8, 16}, + }; + + Text desc_current_index{ + {0, 4 * 16, 240 - 6 * 8, 16}, + }; + + BigFrequency big_display{// Show frequency in glamour + {4, 6 * 16, 28 * 8, 52}, + 0}; + + Button button_manual_start{ + {0 * 8, 11 * 16, 11 * 8, 28}, + ""}; + + Button button_manual_end{ + {12 * 8, 11 * 16, 11 * 8, 28}, + ""}; + + Button button_manual_search{ + {24 * 8, 11 * 16, 6 * 8, 28}, + ""}; + + OptionsField field_mode{ + {5 * 8, (26 * 8) + 4}, + 6, + {} // Text strings get filled by freqman_set_modulation_option() + }; + + OptionsField field_step{ + {17 * 8, (26 * 8) + 4}, + 12, + {} // Text strings get filled by freqman_set_step_option() + }; + + ButtonWithEncoder button_pause{ + {0, (15 * 16) - 4, 72, 28}, + ""}; + + Button button_dir{ + {0, (35 * 8) - 4, 72, 28}, + "REVERSE"}; + + Button button_audio_app{ + {84, (15 * 16) - 4, 72, 28}, + "AUDIO"}; + + Button button_mic_app{ + {84, (35 * 8) - 4, 72, 28}, + "MIC TX"}; + + Button button_add{ + {168, (15 * 16) - 4, 72, 28}, + "ADD FQ"}; + + Button button_load{ + {24 * 8, 3 * 16 - 10, 6 * 8, 22}, + "LOAD"}; + + Button button_clear{ + {24 * 8, 4 * 16, 6 * 8, 22}, + "MCLR"}; + + Button button_remove{ + {168, (35 * 8) - 4, 72, 28}, + "DEL FQ"}; + + std::unique_ptr scan_thread{}; + + MessageHandlerRegistration message_handler_retune{ + Message::ID::Retune, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_retune(message.freq, message.range); + }}; + + MessageHandlerRegistration message_handler_stats{ + Message::ID::ChannelStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_script.cpp b/firmware/application/apps/ui_script.cpp index 702882a67..a76da3df3 100644 --- a/firmware/application/apps/ui_script.cpp +++ b/firmware/application/apps/ui_script.cpp @@ -32,80 +32,75 @@ using namespace portapack; namespace ui { void ScriptView::on_frequency_select() { - //button_edit_freq.focus(); + // button_edit_freq.focus(); } void ScriptView::on_edit_freq(rf::Frequency f) { - (void)f; - //frequencies[menu_view.highlighted()].value = f; - setup_list(); + (void)f; + // frequencies[menu_view.highlighted()].value = f; + setup_list(); } void ScriptView::on_edit_desc(NavigationView& nav) { - (void)nav; + (void)nav; } void ScriptView::on_delete() { - //frequencies.erase(frequencies.begin() + menu_view.highlighted()); - setup_list(); + // frequencies.erase(frequencies.begin() + menu_view.highlighted()); + setup_list(); } void ScriptView::setup_list() { - //size_t n; - - menu_view.clear(); - - /*for (n = 0; n < frequencies.size(); n++) { - menu_view.add_item({ freqman_item_string(frequencies[n]), ui::Color::white(), nullptr, [this](){ on_frequency_select(); } }); - }*/ - - menu_view.set_parent_rect({ 0, 0, 240, 168 }); - menu_view.set_highlighted(menu_view.highlighted()); // Refresh + // size_t n; + + menu_view.clear(); + + /*for (n = 0; n < frequencies.size(); n++) { + menu_view.add_item({ freqman_item_string(frequencies[n]), ui::Color::white(), nullptr, [this](){ on_frequency_select(); } }); + }*/ + + menu_view.set_parent_rect({0, 0, 240, 168}); + menu_view.set_highlighted(menu_view.highlighted()); // Refresh } void ScriptView::focus() { - menu_view.focus(); + menu_view.focus(); } ScriptView::ScriptView( - NavigationView& nav -) { - - add_children({ - &menu_view, - &text_edit, - &button_edit_freq, - &button_edit_desc, - &button_del, - &button_exit - }); - - setup_list(); - - button_edit_freq.on_select = [this, &nav](Button&) { - /*auto new_view = nav.push(frequencies[menu_view.highlighted()].value); - new_view->on_changed = [this](rf::Frequency f) { - on_edit_freq(f); - };*/ - }; - - button_edit_desc.on_select = [this, &nav](Button&) { - on_edit_desc(nav); - }; - - button_del.on_select = [this, &nav](Button&) { - nav.push("Confirm", "Are you sure?", YESNO, - [this](bool choice) { - if (choice) { - on_delete(); - } - } - ); - }; - - button_exit.on_select = [this, &nav](Button&) { - nav.pop(); - }; -} + NavigationView& nav) { + add_children({&menu_view, + &text_edit, + &button_edit_freq, + &button_edit_desc, + &button_del, + &button_exit}); + + setup_list(); + button_edit_freq.on_select = [this, &nav](Button&) { + /*auto new_view = nav.push(frequencies[menu_view.highlighted()].value); + new_view->on_changed = [this](rf::Frequency f) { + on_edit_freq(f); + };*/ + }; + + button_edit_desc.on_select = [this, &nav](Button&) { + on_edit_desc(nav); + }; + + button_del.on_select = [this, &nav](Button&) { + nav.push("Confirm", "Are you sure?", YESNO, + [this](bool choice) { + if (choice) { + on_delete(); + } + }); + }; + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; } + +} // namespace ui diff --git a/firmware/application/apps/ui_script.hpp b/firmware/application/apps/ui_script.hpp index 9bd3f8aac..0add5de81 100644 --- a/firmware/application/apps/ui_script.hpp +++ b/firmware/application/apps/ui_script.hpp @@ -30,65 +30,59 @@ #include "rtc_time.hpp" namespace ui { - + enum script_keyword { - STOP = 0, - WAIT_N, - WAIT_RTC, - IF, - LOOP, - END, - TX, - RX + STOP = 0, + WAIT_N, + WAIT_RTC, + IF, + LOOP, + END, + TX, + RX }; struct script_line { - script_keyword keyword; + script_keyword keyword; }; class ScriptView : public View { -public: - ScriptView(NavigationView& nav); - - void focus() override; + public: + ScriptView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Script editor"; }; + + private: + void on_frequency_select(); + void on_edit_freq(rf::Frequency f); + void on_edit_desc(NavigationView& nav); + void on_delete(); + void setup_list(); - std::string title() const override { return "Script editor"; }; + std::vector script{}; -private: - void on_frequency_select(); - void on_edit_freq(rf::Frequency f); - void on_edit_desc(NavigationView& nav); - void on_delete(); - void setup_list(); - - std::vector script { }; + MenuView menu_view{ + {0, 0, 240, 168}, + true}; - MenuView menu_view { - { 0, 0, 240, 168 }, - true - }; + Text text_edit{ + {16, 194, 5 * 8, 16}, + "Edit:"}; + Button button_edit_freq{ + {16, 194 + 16, 88, 32}, + "Frequency"}; + Button button_edit_desc{ + {16, 194 + 16 + 34, 88, 32}, + "Description"}; + Button button_del{ + {160, 192, 72, 64}, + "Delete"}; - Text text_edit { - { 16, 194, 5 * 8, 16 }, - "Edit:" - }; - Button button_edit_freq { - { 16, 194 + 16, 88, 32 }, - "Frequency" - }; - Button button_edit_desc { - { 16, 194 + 16 + 34, 88, 32 }, - "Description" - }; - Button button_del { - { 160, 192, 72, 64 }, - "Delete" - }; - - Button button_exit { - { 160, 264, 72, 32 }, - "Exit" - }; + Button button_exit{ + {160, 264, 72, 32}, + "Exit"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_sd_over_usb.cpp b/firmware/application/apps/ui_sd_over_usb.cpp index 82da9d345..f21e7590e 100644 --- a/firmware/application/apps/ui_sd_over_usb.cpp +++ b/firmware/application/apps/ui_sd_over_usb.cpp @@ -25,38 +25,35 @@ namespace ui { -SdOverUsbView::SdOverUsbView(NavigationView& nav) : nav_ (nav) { - add_children({ - &labels, - &button_run - }); - - button_run.on_select = [this](Button&) { - ui::Painter painter; - painter.fill_rectangle( - { 0, 0, portapack::display.width(), portapack::display.height() }, - ui::Color::black() - ); - - painter.draw_bitmap( - { portapack::display.width()/2-8, portapack::display.height()/2-8 }, - bitmap_icon_hackrf, - ui::Color::yellow(), - ui::Color::black() - ); - - sdcDisconnect(&SDCD1); - sdcStop(&SDCD1); - - portapack::shutdown(true); - m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false); - m0_halt(); - /* will not return*/ - }; +SdOverUsbView::SdOverUsbView(NavigationView& nav) + : nav_(nav) { + add_children({&labels, + &button_run}); + + button_run.on_select = [this](Button&) { + ui::Painter painter; + painter.fill_rectangle( + {0, 0, portapack::display.width(), portapack::display.height()}, + ui::Color::black()); + + painter.draw_bitmap( + {portapack::display.width() / 2 - 8, portapack::display.height() / 2 - 8}, + bitmap_icon_hackrf, + ui::Color::yellow(), + ui::Color::black()); + + sdcDisconnect(&SDCD1); + sdcStop(&SDCD1); + + portapack::shutdown(true); + m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false); + m0_halt(); + /* will not return*/ + }; } void SdOverUsbView::focus() { - button_run.focus(); + button_run.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_sd_over_usb.hpp b/firmware/application/apps/ui_sd_over_usb.hpp index 1801ce0d8..1fa8e39d8 100644 --- a/firmware/application/apps/ui_sd_over_usb.hpp +++ b/firmware/application/apps/ui_sd_over_usb.hpp @@ -35,28 +35,27 @@ namespace ui { class SdOverUsbView : public View { -public: - SdOverUsbView(NavigationView& nav); + public: + SdOverUsbView(NavigationView& nav); - void focus() override; + void focus() override; - std::string title() const override { return "SD over USB"; }; + std::string title() const override { return "SD over USB"; }; -private: - NavigationView& nav_; - - Labels labels { - { { 3 * 8, 2 * 16 }, "Click Run to start the", Color::white() }, - { { 3 * 8, 3 * 16 }, "USB Mass Storage Mode.", Color::white() }, - { { 3 * 8, 5 * 16 }, "It can take up to 20s", Color::white() }, - { { 3 * 8, 6 * 16 }, "for the drive to be", Color::white() }, - { { 3 * 8, 7 * 16 }, "available.", Color::white() }, - }; + private: + NavigationView& nav_; - Button button_run { - { 9 * 8, 15 * 16, 12 * 8, 3 * 16 }, - "Run" - }; + Labels labels{ + {{3 * 8, 2 * 16}, "Click Run to start the", Color::white()}, + {{3 * 8, 3 * 16}, "USB Mass Storage Mode.", Color::white()}, + {{3 * 8, 5 * 16}, "It can take up to 20s", Color::white()}, + {{3 * 8, 6 * 16}, "for the drive to be", Color::white()}, + {{3 * 8, 7 * 16}, "available.", Color::white()}, + }; + + Button button_run{ + {9 * 8, 15 * 16, 12 * 8, 3 * 16}, + "Run"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_sd_wipe.cpp b/firmware/application/apps/ui_sd_wipe.cpp index f17e76ae2..3139b4d62 100644 --- a/firmware/application/apps/ui_sd_wipe.cpp +++ b/firmware/application/apps/ui_sd_wipe.cpp @@ -24,39 +24,37 @@ namespace ui { -Thread* WipeSDView::thread { nullptr }; - -WipeSDView::WipeSDView(NavigationView& nav) : nav_ (nav) { - add_children({ - &text_info, - &progress, - &dummy - }); +Thread* WipeSDView::thread{nullptr}; + +WipeSDView::WipeSDView(NavigationView& nav) + : nav_(nav) { + add_children({&text_info, + &progress, + &dummy}); } WipeSDView::~WipeSDView() { - if (thread) - chThdTerminate(thread); + if (thread) + chThdTerminate(thread); } void WipeSDView::focus() { - BlockDeviceInfo block_device_info; - - dummy.focus(); - - if (!confirmed) { - nav_.push("Warning !", "Wipe FAT of SD card?", YESCANCEL, [this](bool choice) { - if (choice) - confirmed = true; - } - ); - } else { - if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) { - thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO, WipeSDView::static_fn, this); - } else { - nav_.pop(); // Just silently abort for now - } - } + BlockDeviceInfo block_device_info; + + dummy.focus(); + + if (!confirmed) { + nav_.push("Warning !", "Wipe FAT of SD card?", YESCANCEL, [this](bool choice) { + if (choice) + confirmed = true; + }); + } else { + if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) { + thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO, WipeSDView::static_fn, this); + } else { + nav_.pop(); // Just silently abort for now + } + } } } /* namespace ui */ diff --git a/firmware/application/apps/ui_sd_wipe.hpp b/firmware/application/apps/ui_sd_wipe.hpp index 1e70058db..8f8eebe8b 100644 --- a/firmware/application/apps/ui_sd_wipe.hpp +++ b/firmware/application/apps/ui_sd_wipe.hpp @@ -33,63 +33,60 @@ namespace ui { class WipeSDView : public View { -public: - WipeSDView(NavigationView& nav); - ~WipeSDView(); - void focus() override; - - std::string title() const override { return "Wipe sdcard"; }; - -private: - NavigationView& nav_; - - bool confirmed = false; - static Thread* thread; - - static msg_t static_fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); - return 0; - } - - void run() { - lfsr_word_t v = 1; - //DIR d; - const auto buffer = std::make_unique>(); - - //f_opendir(&d, (TCHAR*)u""); - - uint32_t count = 512; //sd_card::fs.n_fats * sd_card::fs.fsize; - progress.set_max(count); - - for (uint32_t c = 0; c < count; c++) { - progress.set_value(c); - - lfsr_fill(v, - reinterpret_cast(buffer->data()), - sizeof(*buffer.get()) / sizeof(lfsr_word_t)); - - if (disk_write(sd_card::fs.drv, buffer->data(), sd_card::fs.fatbase + c, 1) != RES_OK) - break; - } - nav_.pop(); - } - - Text text_info { - { 10 * 8, 16 * 8, 10 * 8, 16 }, - "Working..." - }; - - ProgressBar progress { - { 2 * 8, 19 * 8, 26 * 8, 24 } - }; - - Button dummy { - { 240, 0, 0, 0 }, - "" - }; + public: + WipeSDView(NavigationView& nav); + ~WipeSDView(); + void focus() override; + + std::string title() const override { return "Wipe sdcard"; }; + + private: + NavigationView& nav_; + + bool confirmed = false; + static Thread* thread; + + static msg_t static_fn(void* arg) { + auto obj = static_cast(arg); + obj->run(); + return 0; + } + + void run() { + lfsr_word_t v = 1; + // DIR d; + const auto buffer = std::make_unique>(); + + // f_opendir(&d, (TCHAR*)u""); + + uint32_t count = 512; // sd_card::fs.n_fats * sd_card::fs.fsize; + progress.set_max(count); + + for (uint32_t c = 0; c < count; c++) { + progress.set_value(c); + + lfsr_fill(v, + reinterpret_cast(buffer->data()), + sizeof(*buffer.get()) / sizeof(lfsr_word_t)); + + if (disk_write(sd_card::fs.drv, buffer->data(), sd_card::fs.fatbase + c, 1) != RES_OK) + break; + } + nav_.pop(); + } + + Text text_info{ + {10 * 8, 16 * 8, 10 * 8, 16}, + "Working..."}; + + ProgressBar progress{ + {2 * 8, 19 * 8, 26 * 8, 24}}; + + Button dummy{ + {240, 0, 0, 0}, + ""}; }; } /* namespace ui */ -#endif/*__UI_SD_WIPE_H__*/ +#endif /*__UI_SD_WIPE_H__*/ diff --git a/firmware/application/apps/ui_search.cpp b/firmware/application/apps/ui_search.cpp index 30e5278db..c5e82641d 100644 --- a/firmware/application/apps/ui_search.cpp +++ b/firmware/application/apps/ui_search.cpp @@ -29,405 +29,395 @@ using namespace portapack; namespace ui { -template<> +template <> void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style -) { - std::string str_duration = ""; - - if (entry.duration < 600) - str_duration = to_string_dec_uint(entry.duration / 10) + "." + to_string_dec_uint(entry.duration % 10) + "s"; - else - str_duration = to_string_dec_uint(entry.duration / 600) + "m" + to_string_dec_uint((entry.duration / 10) % 60) + "s"; - - str_duration.resize(target_rect.width() / 8, ' '); - - painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration); + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + std::string str_duration = ""; + + if (entry.duration < 600) + str_duration = to_string_dec_uint(entry.duration / 10) + "." + to_string_dec_uint(entry.duration % 10) + "s"; + else + str_duration = to_string_dec_uint(entry.duration / 600) + "m" + to_string_dec_uint((entry.duration / 10) % 60) + "s"; + + str_duration.resize(target_rect.width() / 8, ' '); + + painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration); } void SearchView::focus() { - field_frequency_min.focus(); + field_frequency_min.focus(); } SearchView::~SearchView() { - receiver_model.disable(); - baseband::shutdown(); + receiver_model.disable(); + baseband::shutdown(); } void SearchView::do_detection() { - uint8_t power_max = 0; - int32_t bin_max = -1; - uint32_t slice_max = 0; - uint32_t snap_value; - uint8_t power; - rtc::RTC datetime; - std::string str_approx, str_timestamp; - - // Display spectrum - bin_skip_acc = 0; - pixel_index = 0; - display.draw_pixels( - { { 0, 88 }, { (Dim)spectrum_row.size(), 1 } }, - spectrum_row - ); - - mean_power = mean_acc / (SEARCH_BIN_NB_NO_DC * slices_nb); - mean_acc = 0; - - overall_power_max = 0; - - // Find max power over threshold for all slices - for (size_t slice = 0; slice < slices_nb; slice++) { - power = slices[slice].max_power; - if (power > overall_power_max) - overall_power_max = power; - - if ((power >= mean_power + power_threshold) && (power > power_max)) { - power_max = power; - bin_max = slices[slice].max_index; - slice_max = slice; - } - } - - // Lock / release - if ((bin_max >= last_bin - 2) && (bin_max <= last_bin + 2) && (bin_max > -1) && (slice_max == last_slice)) { - - // Staying around the same bin - if (detect_timer >= DETECT_DELAY) { - if ((bin_max != locked_bin) || (!locked)) { - - if (!locked) { - resolved_frequency = slices[slice_max].center_frequency + (SEARCH_BIN_WIDTH * (bin_max - 128)); - - if (check_snap.value()) { - snap_value = options_snap.selected_index_value(); - resolved_frequency = round(resolved_frequency / snap_value) * snap_value; - } - - // Check range - if ((resolved_frequency >= f_min) && (resolved_frequency <= f_max)) { - - duration = 0; - - auto& entry = ::on_packet(recent, resolved_frequency); - - rtcGetTime(&RTCD1, &datetime); - str_timestamp = to_string_dec_uint(datetime.hour(), 2, '0') + ":" + - to_string_dec_uint(datetime.minute(), 2, '0') + ":" + - to_string_dec_uint(datetime.second(), 2, '0'); - entry.set_time(str_timestamp); - recent_entries_view.set_dirty(); - - text_infos.set("Locked ! "); - big_display.set_style(&style_locked); - - locked = true; - locked_bin = bin_max; - - // TODO - /*nav_.pop(); - receiver_model.disable(); - baseband::shutdown(); - nav_.pop();*/ - - /*if (options_goto.selected_index() == 1) - nav_.push(false); - else if (options_goto.selected_index() == 2) - nav_.push(); - */ - } else - text_infos.set("Out of range"); - } - - big_display.set(resolved_frequency); - } - } - release_timer = 0; - } else { - detect_timer = 0; - if (locked) { - if (release_timer >= RELEASE_DELAY) { - locked = false; - - auto& entry = ::on_packet(recent, resolved_frequency); - entry.set_duration(duration); - recent_entries_view.set_dirty(); - - text_infos.set("Listening"); - big_display.set_style(&style_grey); - } - } - } - - last_bin = bin_max; - last_slice = slice_max; - search_counter++; - - // Refresh red tick - portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::black()); - if (bin_max > -1) { - last_tick_pos = (Coord)(bin_max / slices_nb); - portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::red()); - } + uint8_t power_max = 0; + int32_t bin_max = -1; + uint32_t slice_max = 0; + uint32_t snap_value; + uint8_t power; + rtc::RTC datetime; + std::string str_approx, str_timestamp; + + // Display spectrum + bin_skip_acc = 0; + pixel_index = 0; + display.draw_pixels( + {{0, 88}, {(Dim)spectrum_row.size(), 1}}, + spectrum_row); + + mean_power = mean_acc / (SEARCH_BIN_NB_NO_DC * slices_nb); + mean_acc = 0; + + overall_power_max = 0; + + // Find max power over threshold for all slices + for (size_t slice = 0; slice < slices_nb; slice++) { + power = slices[slice].max_power; + if (power > overall_power_max) + overall_power_max = power; + + if ((power >= mean_power + power_threshold) && (power > power_max)) { + power_max = power; + bin_max = slices[slice].max_index; + slice_max = slice; + } + } + + // Lock / release + if ((bin_max >= last_bin - 2) && (bin_max <= last_bin + 2) && (bin_max > -1) && (slice_max == last_slice)) { + // Staying around the same bin + if (detect_timer >= DETECT_DELAY) { + if ((bin_max != locked_bin) || (!locked)) { + if (!locked) { + resolved_frequency = slices[slice_max].center_frequency + (SEARCH_BIN_WIDTH * (bin_max - 128)); + + if (check_snap.value()) { + snap_value = options_snap.selected_index_value(); + resolved_frequency = round(resolved_frequency / snap_value) * snap_value; + } + + // Check range + if ((resolved_frequency >= f_min) && (resolved_frequency <= f_max)) { + duration = 0; + + auto& entry = ::on_packet(recent, resolved_frequency); + + rtcGetTime(&RTCD1, &datetime); + str_timestamp = to_string_dec_uint(datetime.hour(), 2, '0') + ":" + + to_string_dec_uint(datetime.minute(), 2, '0') + ":" + + to_string_dec_uint(datetime.second(), 2, '0'); + entry.set_time(str_timestamp); + recent_entries_view.set_dirty(); + + text_infos.set("Locked ! "); + big_display.set_style(&style_locked); + + locked = true; + locked_bin = bin_max; + + // TODO + /*nav_.pop(); + receiver_model.disable(); + baseband::shutdown(); + nav_.pop();*/ + + /*if (options_goto.selected_index() == 1) + nav_.push(false); + else if (options_goto.selected_index() == 2) + nav_.push(); + */ + } else + text_infos.set("Out of range"); + } + + big_display.set(resolved_frequency); + } + } + release_timer = 0; + } else { + detect_timer = 0; + if (locked) { + if (release_timer >= RELEASE_DELAY) { + locked = false; + + auto& entry = ::on_packet(recent, resolved_frequency); + entry.set_duration(duration); + recent_entries_view.set_dirty(); + + text_infos.set("Listening"); + big_display.set_style(&style_grey); + } + } + } + + last_bin = bin_max; + last_slice = slice_max; + search_counter++; + + // Refresh red tick + portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::black()); + if (bin_max > -1) { + last_tick_pos = (Coord)(bin_max / slices_nb); + portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::red()); + } } void SearchView::add_spectrum_pixel(Color color) { - // Is avoiding floats really necessary ? - bin_skip_acc += bin_skip_frac; - if (bin_skip_acc < 0x10000) - return; - - bin_skip_acc -= 0x10000; - - if (pixel_index < 240) - spectrum_row[pixel_index++] = color; + // Is avoiding floats really necessary ? + bin_skip_acc += bin_skip_frac; + if (bin_skip_acc < 0x10000) + return; + + bin_skip_acc -= 0x10000; + + if (pixel_index < 240) + spectrum_row[pixel_index++] = color; } void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) { - uint8_t max_power = 0; - int16_t max_bin = 0; - uint8_t power; - size_t bin; - - baseband::spectrum_streaming_stop(); - - // Add pixels to spectrum display and find max power for this slice - // Center 12 bins are ignored (DC spike is blanked) - // Leftmost and rightmost 2 bins are ignored - for (bin = 0; bin < 256; bin++) { - - if ((bin < 2) || (bin > 253) || ((bin >= 122) && (bin < 134))) { - power = 0; - } else { - if (bin < 128) - power = spectrum.db[128 + bin]; - else - power = spectrum.db[bin - 128]; - } - - add_spectrum_pixel(spectrum_rgb3_lut[power]); - - mean_acc += power; - if (power > max_power) { - max_power = power; - max_bin = bin; - } - } - - slices[slice_counter].max_power = max_power; - slices[slice_counter].max_index = max_bin; - - if (slices_nb > 1) { - // Slice sequence - if (slice_counter >= slices_nb) { - do_detection(); - slice_counter = 0; - } else - slice_counter++; - receiver_model.set_tuning_frequency(slices[slice_counter].center_frequency); - baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Clear - } else { - // Unique slice - do_detection(); - } - - baseband::spectrum_streaming_start(); + uint8_t max_power = 0; + int16_t max_bin = 0; + uint8_t power; + size_t bin; + + baseband::spectrum_streaming_stop(); + + // Add pixels to spectrum display and find max power for this slice + // Center 12 bins are ignored (DC spike is blanked) + // Leftmost and rightmost 2 bins are ignored + for (bin = 0; bin < 256; bin++) { + if ((bin < 2) || (bin > 253) || ((bin >= 122) && (bin < 134))) { + power = 0; + } else { + if (bin < 128) + power = spectrum.db[128 + bin]; + else + power = spectrum.db[bin - 128]; + } + + add_spectrum_pixel(spectrum_rgb3_lut[power]); + + mean_acc += power; + if (power > max_power) { + max_power = power; + max_bin = bin; + } + } + + slices[slice_counter].max_power = max_power; + slices[slice_counter].max_index = max_bin; + + if (slices_nb > 1) { + // Slice sequence + if (slice_counter >= slices_nb) { + do_detection(); + slice_counter = 0; + } else + slice_counter++; + receiver_model.set_tuning_frequency(slices[slice_counter].center_frequency); + baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Clear + } else { + // Unique slice + do_detection(); + } + + baseband::spectrum_streaming_start(); } void SearchView::on_show() { - baseband::spectrum_streaming_start(); + baseband::spectrum_streaming_start(); } void SearchView::on_hide() { - baseband::spectrum_streaming_stop(); + baseband::spectrum_streaming_stop(); } void SearchView::on_range_changed() { - rf::Frequency slices_span, center_frequency; - int64_t offset; - size_t slice; - - f_min = field_frequency_min.value(); - f_max = field_frequency_max.value(); - search_span = abs(f_max - f_min); - - if (search_span > SEARCH_SLICE_WIDTH) { - // ex: 100M~115M (15M span): - // slices_nb = (115M-100M)/2.5M = 6 - slices_nb = (search_span + SEARCH_SLICE_WIDTH - 1) / SEARCH_SLICE_WIDTH; - if (slices_nb > 32) { - text_slices.set("!!"); - slices_nb = 32; - } else { - text_slices.set(to_string_dec_uint(slices_nb, 2, ' ')); - } - // slices_span = 6 * 2.5M = 15M - slices_span = slices_nb * SEARCH_SLICE_WIDTH; - // offset = 0 + 2.5/2 = 1.25M - offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2); - // slice_start = 100M + 1.25M = 101.25M - center_frequency = std::min(f_min, f_max) + offset; - - for (slice = 0; slice < slices_nb; slice++) { - slices[slice].center_frequency = center_frequency; - center_frequency += SEARCH_SLICE_WIDTH; - } - } else { - slices[0].center_frequency = (f_max + f_min) / 2; - receiver_model.set_tuning_frequency(slices[0].center_frequency); - - slices_nb = 1; - text_slices.set(" 1"); - } - - bin_skip_frac = 0xF000 / slices_nb; - - slice_counter = 0; + rf::Frequency slices_span, center_frequency; + int64_t offset; + size_t slice; + + f_min = field_frequency_min.value(); + f_max = field_frequency_max.value(); + search_span = abs(f_max - f_min); + + if (search_span > SEARCH_SLICE_WIDTH) { + // ex: 100M~115M (15M span): + // slices_nb = (115M-100M)/2.5M = 6 + slices_nb = (search_span + SEARCH_SLICE_WIDTH - 1) / SEARCH_SLICE_WIDTH; + if (slices_nb > 32) { + text_slices.set("!!"); + slices_nb = 32; + } else { + text_slices.set(to_string_dec_uint(slices_nb, 2, ' ')); + } + // slices_span = 6 * 2.5M = 15M + slices_span = slices_nb * SEARCH_SLICE_WIDTH; + // offset = 0 + 2.5/2 = 1.25M + offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2); + // slice_start = 100M + 1.25M = 101.25M + center_frequency = std::min(f_min, f_max) + offset; + + for (slice = 0; slice < slices_nb; slice++) { + slices[slice].center_frequency = center_frequency; + center_frequency += SEARCH_SLICE_WIDTH; + } + } else { + slices[0].center_frequency = (f_max + f_min) / 2; + receiver_model.set_tuning_frequency(slices[0].center_frequency); + + slices_nb = 1; + text_slices.set(" 1"); + } + + bin_skip_frac = 0xF000 / slices_nb; + + slice_counter = 0; } void SearchView::on_lna_changed(int32_t v_db) { - receiver_model.set_lna(v_db); + receiver_model.set_lna(v_db); } void SearchView::on_vga_changed(int32_t v_db) { - receiver_model.set_vga(v_db); + receiver_model.set_vga(v_db); } void SearchView::do_timers() { - - if (timing_div >= 60) { - // ~1Hz - - timing_div = 0; - - // Update scan rate - text_rate.set(to_string_dec_uint(search_counter, 3)); - search_counter = 0; - } - - if (timing_div % 12 == 0) { - // ~5Hz - - // Update power levels - text_mean.set(to_string_dec_uint(mean_power, 3)); - - vu_max.set_value(overall_power_max); - vu_max.set_mark(mean_power + power_threshold); - } - - if (timing_div % 6 == 0) { - // ~10Hz - - // Update timing indicator - if (locked) { - progress_timers.set_max(RELEASE_DELAY); - progress_timers.set_value(RELEASE_DELAY - release_timer); - } else { - progress_timers.set_max(DETECT_DELAY); - progress_timers.set_value(detect_timer); - } - - // Increment timers - if (detect_timer < DETECT_DELAY) detect_timer++; - if (release_timer < RELEASE_DELAY) release_timer++; - - if (locked) duration++; - } - - timing_div++; + if (timing_div >= 60) { + // ~1Hz + + timing_div = 0; + + // Update scan rate + text_rate.set(to_string_dec_uint(search_counter, 3)); + search_counter = 0; + } + + if (timing_div % 12 == 0) { + // ~5Hz + + // Update power levels + text_mean.set(to_string_dec_uint(mean_power, 3)); + + vu_max.set_value(overall_power_max); + vu_max.set_mark(mean_power + power_threshold); + } + + if (timing_div % 6 == 0) { + // ~10Hz + + // Update timing indicator + if (locked) { + progress_timers.set_max(RELEASE_DELAY); + progress_timers.set_value(RELEASE_DELAY - release_timer); + } else { + progress_timers.set_max(DETECT_DELAY); + progress_timers.set_value(detect_timer); + } + + // Increment timers + if (detect_timer < DETECT_DELAY) detect_timer++; + if (release_timer < RELEASE_DELAY) release_timer++; + + if (locked) duration++; + } + + timing_div++; } SearchView::SearchView( - NavigationView& nav -) : nav_ (nav) -{ - baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); - - add_children({ - &labels, - &field_frequency_min, - &field_frequency_max, - &field_lna, - &field_vga, - &field_threshold, - &text_mean, - &text_slices, - &text_rate, - &text_infos, - &vu_max, - &progress_timers, - &check_snap, - &options_snap, - &big_display, - &recent_entries_view - }); - - baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); - - recent_entries_view.set_parent_rect({ 0, 28 * 8, 240, 12 * 8 }); - recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) { - nav.push(entry.frequency); - }; - - text_mean.set_style(&style_grey); - text_slices.set_style(&style_grey); - text_rate.set_style(&style_grey); - progress_timers.set_style(&style_grey); - big_display.set_style(&style_grey); - - check_snap.set_value(true); - options_snap.set_selected_index(1); // 12.5kHz - - field_threshold.set_value(80); - field_threshold.on_change = [this](int32_t value) { - power_threshold = value; - }; - - field_frequency_min.set_value(receiver_model.tuning_frequency() - 1000000); - field_frequency_min.set_step(100000); - field_frequency_min.on_change = [this](rf::Frequency) { - this->on_range_changed(); - }; - field_frequency_min.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->field_frequency_min.set_value(f); - }; - }; - - field_frequency_max.set_value(receiver_model.tuning_frequency() + 1000000); - field_frequency_max.set_step(100000); - field_frequency_max.on_change = [this](rf::Frequency) { - this->on_range_changed(); - }; - field_frequency_max.on_edit = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->field_frequency_max.set_value(f); - }; - }; - - field_lna.set_value(receiver_model.lna()); - field_lna.on_change = [this](int32_t v) { - this->on_lna_changed(v); - }; - - field_vga.set_value(receiver_model.vga()); - field_vga.on_change = [this](int32_t v_db) { - this->on_vga_changed(v_db); - }; - - progress_timers.set_max(DETECT_DELAY); - - on_range_changed(); - - receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); - receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH); - receiver_model.set_baseband_bandwidth(2500000); - receiver_model.enable(); + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); + + add_children({&labels, + &field_frequency_min, + &field_frequency_max, + &field_lna, + &field_vga, + &field_threshold, + &text_mean, + &text_slices, + &text_rate, + &text_infos, + &vu_max, + &progress_timers, + &check_snap, + &options_snap, + &big_display, + &recent_entries_view}); + + baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); + + recent_entries_view.set_parent_rect({0, 28 * 8, 240, 12 * 8}); + recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) { + nav.push(entry.frequency); + }; + + text_mean.set_style(&style_grey); + text_slices.set_style(&style_grey); + text_rate.set_style(&style_grey); + progress_timers.set_style(&style_grey); + big_display.set_style(&style_grey); + + check_snap.set_value(true); + options_snap.set_selected_index(1); // 12.5kHz + + field_threshold.set_value(80); + field_threshold.on_change = [this](int32_t value) { + power_threshold = value; + }; + + field_frequency_min.set_value(receiver_model.tuning_frequency() - 1000000); + field_frequency_min.set_step(100000); + field_frequency_min.on_change = [this](rf::Frequency) { + this->on_range_changed(); + }; + field_frequency_min.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->field_frequency_min.set_value(f); + }; + }; + + field_frequency_max.set_value(receiver_model.tuning_frequency() + 1000000); + field_frequency_max.set_step(100000); + field_frequency_max.on_change = [this](rf::Frequency) { + this->on_range_changed(); + }; + field_frequency_max.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->field_frequency_max.set_value(f); + }; + }; + + field_lna.set_value(receiver_model.lna()); + field_lna.on_change = [this](int32_t v) { + this->on_lna_changed(v); + }; + + field_vga.set_value(receiver_model.vga()); + field_vga.on_change = [this](int32_t v_db) { + this->on_vga_changed(v_db); + }; + + progress_timers.set_max(DETECT_DELAY); + + on_range_changed(); + + receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); + receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH); + receiver_model.set_baseband_bandwidth(2500000); + receiver_model.enable(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_search.hpp b/firmware/application/apps/ui_search.hpp index 8dc6f48ef..c2952162f 100644 --- a/firmware/application/apps/ui_search.hpp +++ b/firmware/application/apps/ui_search.hpp @@ -30,223 +30,204 @@ namespace ui { -#define SEARCH_SLICE_WIDTH 2500000 // Search slice bandwidth -#define SEARCH_BIN_NB 256 // FFT power bins -#define SEARCH_BIN_NB_NO_DC (SEARCH_BIN_NB - 16) // Bins after trimming -#define SEARCH_BIN_WIDTH (SEARCH_SLICE_WIDTH / SEARCH_BIN_NB) +#define SEARCH_SLICE_WIDTH 2500000 // Search slice bandwidth +#define SEARCH_BIN_NB 256 // FFT power bins +#define SEARCH_BIN_NB_NO_DC (SEARCH_BIN_NB - 16) // Bins after trimming +#define SEARCH_BIN_WIDTH (SEARCH_SLICE_WIDTH / SEARCH_BIN_NB) -#define DETECT_DELAY 5 // In 100ms units -#define RELEASE_DELAY 6 +#define DETECT_DELAY 5 // In 100ms units +#define RELEASE_DELAY 6 struct SearchRecentEntry { - using Key = rf::Frequency; - - static constexpr Key invalid_key = 0xffffffff; - - rf::Frequency frequency; - uint32_t duration { 0 }; // In 100ms units - std::string time { "" }; - - SearchRecentEntry( - ) : SearchRecentEntry { 0 } - { - } - - SearchRecentEntry( - const rf::Frequency frequency - ) : frequency { frequency } - { - } - - Key key() const { - return frequency; - } - - void set_time(std::string& new_time) { - time = new_time; - } - - void set_duration(uint32_t new_duration) { - duration = new_duration; - } + using Key = rf::Frequency; + + static constexpr Key invalid_key = 0xffffffff; + + rf::Frequency frequency; + uint32_t duration{0}; // In 100ms units + std::string time{""}; + + SearchRecentEntry() + : SearchRecentEntry{0} { + } + + SearchRecentEntry( + const rf::Frequency frequency) + : frequency{frequency} { + } + + Key key() const { + return frequency; + } + + void set_time(std::string& new_time) { + time = new_time; + } + + void set_duration(uint32_t new_duration) { + duration = new_duration; + } }; using SearchRecentEntries = RecentEntries; class SearchView : public View { -public: - SearchView(NavigationView& nav); - ~SearchView(); - - SearchView(const SearchView&) = delete; - SearchView(SearchView&&) = delete; - SearchView& operator=(const SearchView&) = delete; - SearchView& operator=(SearchView&&) = delete; - - void on_show() override; - void on_hide() override; - void focus() override; - - std::string title() const override { return "Search"; }; - -private: - NavigationView& nav_; - - const Style style_grey { // For informations and lost signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - - const Style style_locked { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - - struct slice_t { - rf::Frequency center_frequency; - uint8_t max_power; - int16_t max_index; - uint8_t power; - int16_t index; - } slices[32]; - - uint32_t bin_skip_acc { 0 }, bin_skip_frac { }; - uint32_t pixel_index { 0 }; - std::array spectrum_row = { 0 }; - ChannelSpectrumFIFO* fifo { nullptr }; - rf::Frequency f_min { 0 }, f_max { 0 }; - uint8_t detect_timer { 0 }, release_timer { 0 }, timing_div { 0 }; - uint8_t overall_power_max { 0 }; - uint32_t mean_power { 0 }, mean_acc { 0 }; - uint32_t duration { 0 }; - uint32_t power_threshold { 80 }; // Todo: Put this in persistent / settings - rf::Frequency slice_start { 0 }; - uint8_t slices_nb { 0 }; - uint8_t slice_counter { 0 }; - int16_t last_bin { 0 }; - uint32_t last_slice { 0 }; - Coord last_tick_pos { 0 }; - rf::Frequency search_span { 0 }, resolved_frequency { 0 }; - uint16_t locked_bin { 0 }; - uint8_t search_counter { 0 }; - bool locked { false }; - - void on_channel_spectrum(const ChannelSpectrum& spectrum); - void on_range_changed(); - void do_detection(); - void on_lna_changed(int32_t v_db); - void on_vga_changed(int32_t v_db); - void do_timers(); - void add_spectrum_pixel(Color color); - - const RecentEntriesColumns columns { { - { "Frequency", 9 }, - { "Time", 8 }, - { "Duration", 11 } - } }; - SearchRecentEntries recent { }; - RecentEntriesView> recent_entries_view { columns, recent }; - - Labels labels { - { { 1 * 8, 0 }, "Min: Max: LNA VGA", Color::light_grey() }, - { { 1 * 8, 4 * 8 }, "Trig: /255 Mean: /255", Color::light_grey() }, - { { 1 * 8, 6 * 8 }, "Slices: /32 Rate: Hz", Color::light_grey() }, - { { 6 * 8, 10 * 8 }, "Timer Status", Color::light_grey() }, - { { 1 * 8, 25 * 8 }, "Accuracy +/-4.9kHz", Color::light_grey() }, - { { 26 * 8, 25 * 8 }, "MHz", Color::light_grey() } - }; - - FrequencyField field_frequency_min { - { 1 * 8, 1 * 16 }, - }; - FrequencyField field_frequency_max { - { 11 * 8, 1 * 16 }, - }; - LNAGainField field_lna { - { 22 * 8, 1 * 16 } - }; - VGAGainField field_vga { - { 26 * 8, 1 * 16 } - }; - - NumberField field_threshold { - { 6 * 8, 2 * 16 }, - 3, - { 5, 255 }, - 5, - ' ' - }; - Text text_mean { - { 22 * 8, 2 * 16, 3 * 8, 16 }, - "---" - }; - Text text_slices { - { 8 * 8, 3 * 16, 2 * 8, 16 }, - "--" - }; - Text text_rate { - { 24 * 8, 3 * 16, 3 * 8, 16 }, - "---" - }; - - VuMeter vu_max { - { 1 * 8, 11 * 8 - 4, 3 * 8, 48 }, - 18, - false - }; - - ProgressBar progress_timers { - { 6 * 8, 12 * 8, 6 * 8, 16 } - }; - Text text_infos { - { 13 * 8, 12 * 8, 15 * 8, 16 }, - "Listening" - }; - - Checkbox check_snap { - { 6 * 8, 15 * 8 }, - 7, - "Snap to:", - true - }; - OptionsField options_snap { - { 17 * 8, 15 * 8 }, // Position - 7, // Length - { // Options - { "25kHz ", 25000 }, - { "12.5kHz", 12500 }, - { "8.33kHz", 8333 }, - { "2.5kHz", 2500 }, - { "500Hz", 500 } - } - }; - - BigFrequency big_display { - { 4, 9 * 16, 28 * 8, 52 }, - 0 - }; - - MessageHandlerRegistration message_handler_spectrum_config { - Message::ID::ChannelSpectrumConfig, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->fifo = message.fifo; - } - }; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->fifo ) { - ChannelSpectrum channel_spectrum; - while( fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - this->do_timers(); - } - }; + public: + SearchView(NavigationView& nav); + ~SearchView(); + + SearchView(const SearchView&) = delete; + SearchView(SearchView&&) = delete; + SearchView& operator=(const SearchView&) = delete; + SearchView& operator=(SearchView&&) = delete; + + void on_show() override; + void on_hide() override; + void focus() override; + + std::string title() const override { return "Search"; }; + + private: + NavigationView& nav_; + + const Style style_grey{ + // For informations and lost signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + const Style style_locked{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + struct slice_t { + rf::Frequency center_frequency; + uint8_t max_power; + int16_t max_index; + uint8_t power; + int16_t index; + } slices[32]; + + uint32_t bin_skip_acc{0}, bin_skip_frac{}; + uint32_t pixel_index{0}; + std::array spectrum_row = {0}; + ChannelSpectrumFIFO* fifo{nullptr}; + rf::Frequency f_min{0}, f_max{0}; + uint8_t detect_timer{0}, release_timer{0}, timing_div{0}; + uint8_t overall_power_max{0}; + uint32_t mean_power{0}, mean_acc{0}; + uint32_t duration{0}; + uint32_t power_threshold{80}; // Todo: Put this in persistent / settings + rf::Frequency slice_start{0}; + uint8_t slices_nb{0}; + uint8_t slice_counter{0}; + int16_t last_bin{0}; + uint32_t last_slice{0}; + Coord last_tick_pos{0}; + rf::Frequency search_span{0}, resolved_frequency{0}; + uint16_t locked_bin{0}; + uint8_t search_counter{0}; + bool locked{false}; + + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_range_changed(); + void do_detection(); + void on_lna_changed(int32_t v_db); + void on_vga_changed(int32_t v_db); + void do_timers(); + void add_spectrum_pixel(Color color); + + const RecentEntriesColumns columns{{{"Frequency", 9}, + {"Time", 8}, + {"Duration", 11}}}; + SearchRecentEntries recent{}; + RecentEntriesView> recent_entries_view{columns, recent}; + + Labels labels{ + {{1 * 8, 0}, "Min: Max: LNA VGA", Color::light_grey()}, + {{1 * 8, 4 * 8}, "Trig: /255 Mean: /255", Color::light_grey()}, + {{1 * 8, 6 * 8}, "Slices: /32 Rate: Hz", Color::light_grey()}, + {{6 * 8, 10 * 8}, "Timer Status", Color::light_grey()}, + {{1 * 8, 25 * 8}, "Accuracy +/-4.9kHz", Color::light_grey()}, + {{26 * 8, 25 * 8}, "MHz", Color::light_grey()}}; + + FrequencyField field_frequency_min{ + {1 * 8, 1 * 16}, + }; + FrequencyField field_frequency_max{ + {11 * 8, 1 * 16}, + }; + LNAGainField field_lna{ + {22 * 8, 1 * 16}}; + VGAGainField field_vga{ + {26 * 8, 1 * 16}}; + + NumberField field_threshold{ + {6 * 8, 2 * 16}, + 3, + {5, 255}, + 5, + ' '}; + Text text_mean{ + {22 * 8, 2 * 16, 3 * 8, 16}, + "---"}; + Text text_slices{ + {8 * 8, 3 * 16, 2 * 8, 16}, + "--"}; + Text text_rate{ + {24 * 8, 3 * 16, 3 * 8, 16}, + "---"}; + + VuMeter vu_max{ + {1 * 8, 11 * 8 - 4, 3 * 8, 48}, + 18, + false}; + + ProgressBar progress_timers{ + {6 * 8, 12 * 8, 6 * 8, 16}}; + Text text_infos{ + {13 * 8, 12 * 8, 15 * 8, 16}, + "Listening"}; + + Checkbox check_snap{ + {6 * 8, 15 * 8}, + 7, + "Snap to:", + true}; + OptionsField options_snap{ + {17 * 8, 15 * 8}, // Position + 7, // Length + { // Options + {"25kHz ", 25000}, + {"12.5kHz", 12500}, + {"8.33kHz", 8333}, + {"2.5kHz", 2500}, + {"500Hz", 500}}}; + + BigFrequency big_display{ + {4, 9 * 16, 28 * 8, 52}, + 0}; + + MessageHandlerRegistration message_handler_spectrum_config{ + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->fifo = message.fifo; + }}; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if (this->fifo) { + ChannelSpectrum channel_spectrum; + while (fifo->out(channel_spectrum)) { + this->on_channel_spectrum(channel_spectrum); + } + } + this->do_timers(); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index ce7782c8f..0575ed1f5 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -41,539 +41,497 @@ using namespace portapack; #include "freqman.hpp" namespace ui { - SetDateTimeView::SetDateTimeView( - NavigationView& nav - ) { - button_save.on_select = [&nav, this](Button&){ - const auto model = this->form_collect(); - const rtc::RTC new_datetime { - model.year, model.month, model.day, - model.hour, model.minute, model.second - }; - rtcSetTime(&RTCD1, &new_datetime); - nav.pop(); - }, - - button_cancel.on_select = [&nav](Button&){ - nav.pop(); - }, - - add_children({ - &labels, - &field_year, - &field_month, - &field_day, - &field_hour, - &field_minute, - &field_second, - &button_save, - &button_cancel, - }); - - rtc::RTC datetime; - rtcGetTime(&RTCD1, &datetime); - SetDateTimeModel model { - datetime.year(), - datetime.month(), - datetime.day(), - datetime.hour(), - datetime.minute(), - datetime.second() - }; - - form_init(model); - } - - void SetDateTimeView::focus() { - button_cancel.focus(); - } - - void SetDateTimeView::form_init(const SetDateTimeModel& model) { - field_year.set_value(model.year); - field_month.set_value(model.month); - field_day.set_value(model.day); - field_hour.set_value(model.hour); - field_minute.set_value(model.minute); - field_second.set_value(model.second); - } - - SetDateTimeModel SetDateTimeView::form_collect() { - return { - .year = static_cast(field_year.value()), - .month = static_cast(field_month.value()), - .day = static_cast(field_day.value()), - .hour = static_cast(field_hour.value()), - .minute = static_cast(field_minute.value()), - .second = static_cast(field_second.value()) - }; - } - - SetRadioView::SetRadioView( - NavigationView& nav - ) { - button_cancel.on_select = [&nav](Button&){ - nav.pop(); - }; - - const auto reference = portapack::clock_manager.get_reference(); - - std::string source_name("---"); - switch(reference.source) { - case ClockManager::ReferenceSource::Xtal: source_name = "HackRF"; break; - case ClockManager::ReferenceSource::PortaPack: source_name = "PortaPack"; break; - case ClockManager::ReferenceSource::External: source_name = "External"; break; - } - - value_source.set(source_name); - value_source_frequency.set(to_string_dec_uint(reference.frequency / 1000000, 2) + "." + to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz"); - - label_source.set_style(&style_text); - value_source.set_style(&style_text); - value_source_frequency.set_style(&style_text); - - add_children({ - &label_source, - &value_source, - &value_source_frequency, - }); - - if( reference.source == ClockManager::ReferenceSource::Xtal ) { - add_children({ - &labels_correction, - &field_ppm, - }); - } - - add_children({ - &check_clkout, - &field_clkout_freq, - &labels_clkout_khz, - &value_freq_step, - &labels_bias, - &check_bias, - &button_save, - &button_cancel - }); - - SetFrequencyCorrectionModel model { - static_cast(portapack::persistent_memory::correction_ppb() / 1000) , 0 - }; - - form_init(model); - - check_clkout.set_value(portapack::persistent_memory::clkout_enabled()); - check_clkout.on_select = [this](Checkbox&, bool v) { - clock_manager.enable_clock_output(v); - portapack::persistent_memory::set_clkout_enabled(v); - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - }; - - field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq()); - value_freq_step.set_style(&style_text); - - field_clkout_freq.on_select = [this](NumberField&) { - freq_step_khz++; - if(freq_step_khz > 3) { - freq_step_khz = 0; - } - switch(freq_step_khz) { - case 0: - value_freq_step.set(" |"); - break; - case 1: - value_freq_step.set(" | "); - break; - case 2: - value_freq_step.set(" | "); - break; - case 3: - value_freq_step.set("| "); - break; - } - field_clkout_freq.set_step(pow(10, freq_step_khz)); - }; - - check_bias.set_value(portapack::get_antenna_bias()); - check_bias.on_select = [this](Checkbox&, bool v) { - portapack::set_antenna_bias(v); - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - }; - - button_save.on_select = [this, &nav](Button&){ - const auto model = this->form_collect(); - portapack::persistent_memory::set_correction_ppb(model.ppm * 1000); - portapack::persistent_memory::set_clkout_freq(model.freq); - clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled()); - nav.pop(); - }; - } - - void SetRadioView::focus() { - button_save.focus(); - } - - void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) { - field_ppm.set_value(model.ppm); - } - - SetFrequencyCorrectionModel SetRadioView::form_collect() { - return { - .ppm = static_cast(field_ppm.value()), - .freq = static_cast(field_clkout_freq.value()), - }; - } - - - SetUIView::SetUIView(NavigationView& nav) { - add_children({ - &checkbox_disable_touchscreen, - &checkbox_speaker, - &checkbox_bloff, - &options_bloff, - &checkbox_showsplash, - &checkbox_showclock, - &options_clockformat, - &checkbox_guireturnflag, - &button_save, - &button_cancel - }); - - checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen()); - checkbox_speaker.set_value(persistent_memory::config_speaker()); - checkbox_showsplash.set_value(persistent_memory::config_splash()); - checkbox_showclock.set_value(!persistent_memory::hide_clock()); - checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon()); - - const auto backlight_config = persistent_memory::config_backlight_timer(); - checkbox_bloff.set_value(backlight_config.timeout_enabled()); - options_bloff.set_by_value(backlight_config.timeout_enum()); - - if (persistent_memory::clock_with_date()) { - options_clockformat.set_selected_index(1); - } else { - options_clockformat.set_selected_index(0); - } - - - button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_config_backlight_timer({ - (persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(), - checkbox_bloff.value() - }); - - if (checkbox_showclock.value()){ - if (options_clockformat.selected_index() == 1) - persistent_memory::set_clock_with_date(true); - else - persistent_memory::set_clock_with_date(false); - } - - if (checkbox_speaker.value()) audio::output::speaker_mute(); //Just mute audio if speaker is disabled - persistent_memory::set_config_speaker(checkbox_speaker.value()); //Store Speaker status - StatusRefreshMessage message { }; //Refresh status bar with/out speaker - EventDispatcher::send_message(message); - - persistent_memory::set_config_splash(checkbox_showsplash.value()); - persistent_memory::set_clock_hidden(!checkbox_showclock.value()); - persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value()); - persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value()); - nav.pop(); - }; - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - } - - void SetUIView::focus() { - button_save.focus(); - } - - - // --------------------------------------------------------- - // Appl. Settings - // --------------------------------------------------------- - SetAppSettingsView::SetAppSettingsView(NavigationView& nav) { - add_children({ - &checkbox_load_app_settings, - &checkbox_save_app_settings, - &button_save, - &button_cancel - }); - - checkbox_load_app_settings.set_value(persistent_memory::load_app_settings()); - checkbox_save_app_settings.set_value(persistent_memory::save_app_settings()); - - button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_load_app_settings(checkbox_load_app_settings.value()); - persistent_memory::set_save_app_settings(checkbox_save_app_settings.value()); - nav.pop(); - }; - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - } - - void SetAppSettingsView::focus() { - button_save.focus(); - } - - // --------------------------------------------------------- - // Converter Settings - // --------------------------------------------------------- - SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) { - add_children({ - &check_show_converter, - &check_converter, - &converter_mode, - &button_converter_freq, - &button_save, - &button_cancel - }); - - check_show_converter.set_value(!portapack::persistent_memory::config_hide_converter()); - check_show_converter.on_select = [this](Checkbox&, bool v) { - portapack::persistent_memory::set_config_hide_converter(!v); - if( !v ) - { - check_converter.set_value(false); - } - // Retune to take converter change in account - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); - //Refresh status bar with/out converter - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - }; - - check_converter.set_value(portapack::persistent_memory::config_converter()); - check_converter.on_select = [this](Checkbox&, bool v) { - if( v ) - { - check_show_converter.set_value(true); - portapack::persistent_memory::set_config_hide_converter(false); - } - portapack::persistent_memory::set_config_converter(v); - // Retune to take converter change in account - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); - //Refresh status bar with/out converter - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - }; - - converter_mode.set_by_value( portapack::persistent_memory::config_updown_converter() ); - converter_mode.on_change = [this](size_t, OptionsField::value_t v) { - portapack::persistent_memory::set_config_updown_converter( v ); - //Refresh status bar with icon up or down - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - }; - - button_converter_freq.set_text( to_string_short_freq( portapack::persistent_memory::config_converter_freq() ) + "MHz"); - button_converter_freq.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(portapack::persistent_memory::config_converter_freq() ); - new_view->on_changed = [this, &button](rf::Frequency f) { - portapack::persistent_memory::set_config_converter_freq( f ); - // Retune to take converter change in account - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); - button_converter_freq.set_text( "<" + to_string_short_freq( f ) + " MHz>" ); - }; - }; - - button_save.on_select = [&nav, this](Button&) { - nav.pop(); - }; - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - } - - void SetConverterSettingsView::focus() { - button_save.focus(); - } - - // --------------------------------------------------------- - // Persistent Memory Settings - // --------------------------------------------------------- - SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { - add_children({ - &text_pmem_about, - &text_pmem_informations, - &text_pmem_status, - &check_load_mem_at_startup, - &button_save_mem_to_file, - &button_load_mem_from_file, - &button_load_mem_defaults, - &button_return - }); - - bool load_mem_at_startup = false ; - File pmem_flag_file_handle ; - - std::string folder = "SETTINGS"; - make_new_directory(folder); - - std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ; - auto result = pmem_flag_file_handle.open(pmem_flag_file); - if(!result.is_valid()) - { - load_mem_at_startup = true ; - } - check_load_mem_at_startup.set_value(load_mem_at_startup); - check_load_mem_at_startup.on_select = [this](Checkbox&, bool v) { - File pmem_flag_file_handle ; - std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ; - if( v ) - { - auto result = pmem_flag_file_handle.open(pmem_flag_file); - if(result.is_valid()) - { - auto result = pmem_flag_file_handle.create(pmem_flag_file); //third: create if it is not there - if( !result.is_valid() ) - { - text_pmem_status.set("pmem flag file created"); - } - else - { - text_pmem_status.set("!err. creating pmem flagfile!"); - } - } - else - { - text_pmem_status.set("pmem flag already present"); - } - } - else - { - auto result = delete_file( pmem_flag_file ); - if( result.code() != FR_OK ) - { - text_pmem_status.set("!err. deleting pmem flagfile!"); - } - else - { - text_pmem_status.set("pmem flag file deleted"); - } - } - }; - - button_save_mem_to_file.on_select = [&nav, this](Button&) { - if( !portapack::persistent_memory::save_persistent_settings_to_file("SETTINGS/pmem_settings") ) - { - text_pmem_status.set("!problem saving settings!"); - } - else - { - text_pmem_status.set("settings saved"); - } - }; - - button_load_mem_from_file.on_select = [&nav, this](Button&) { - if( !portapack::persistent_memory::load_persistent_settings_from_file("SETTINGS/pmem_settings") ) - { - text_pmem_status.set("!problem loading settings!"); - } - else - { - text_pmem_status.set("settings loaded"); - //Refresh status bar with icon up or down - StatusRefreshMessage message { }; - EventDispatcher::send_message(message); - } - }; - - button_load_mem_defaults.on_select = [&nav, this](Button&) { - nav.push( - "Warning!", - "This will reset the p.mem\nand set the default settings", - YESNO, - [this](bool choice) { - if (choice) { - portapack::persistent_memory::cache::defaults(); - } - } ); - }; - - button_return.on_select = [&nav, this](Button&) { - nav.pop(); - }; - } - - void SetPersistentMemoryView::focus() { - button_return.focus(); - } - - // - // Audio settings - // - SetAudioView::SetAudioView(NavigationView& nav) { - add_children({ - &labels, - &field_tone_mix, - &button_save, - &button_cancel - }); - - field_tone_mix.set_value(persistent_memory::tone_mix()); - - button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_tone_mix(field_tone_mix.value()); - nav.pop(); - }; - - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - } - - void SetAudioView::focus() { - button_save.focus(); - } - - SetQRCodeView::SetQRCodeView(NavigationView& nav) { - add_children({ - &checkbox_bigger_qr, - &button_save, - &button_cancel - }); - - checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code()); - - button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value()); - nav.pop(); - }; - - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - - } - - void SetQRCodeView::focus() { - button_save.focus(); - } - - // --------------------------------------------------------- - // Settings main menu - // --------------------------------------------------------- - SettingsMenuView::SettingsMenuView(NavigationView& nav) { - if( portapack::persistent_memory::show_gui_return_icon() ) - { - add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } ); - } - add_items({ - { "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, - { "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push(); } }, - { "User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push(); } }, - { "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push(); } }, - { "Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push(); } }, - { "App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav](){ nav.push(); } }, - { "Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push(); } }, - { "QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav](){ nav.push(); } }, - { "P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav](){ nav.push(); } }, - }); - set_max_rows(2); // allow wider buttons - } +SetDateTimeView::SetDateTimeView( + NavigationView& nav) { + button_save.on_select = [&nav, this](Button&) { + const auto model = this->form_collect(); + const rtc::RTC new_datetime{ + model.year, model.month, model.day, + model.hour, model.minute, model.second}; + rtcSetTime(&RTCD1, &new_datetime); + nav.pop(); + }, + + button_cancel.on_select = [&nav](Button&) { + nav.pop(); + }, + + add_children({ + &labels, + &field_year, + &field_month, + &field_day, + &field_hour, + &field_minute, + &field_second, + &button_save, + &button_cancel, + }); + + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + SetDateTimeModel model{ + datetime.year(), + datetime.month(), + datetime.day(), + datetime.hour(), + datetime.minute(), + datetime.second()}; + + form_init(model); +} + +void SetDateTimeView::focus() { + button_cancel.focus(); +} + +void SetDateTimeView::form_init(const SetDateTimeModel& model) { + field_year.set_value(model.year); + field_month.set_value(model.month); + field_day.set_value(model.day); + field_hour.set_value(model.hour); + field_minute.set_value(model.minute); + field_second.set_value(model.second); +} + +SetDateTimeModel SetDateTimeView::form_collect() { + return { + .year = static_cast(field_year.value()), + .month = static_cast(field_month.value()), + .day = static_cast(field_day.value()), + .hour = static_cast(field_hour.value()), + .minute = static_cast(field_minute.value()), + .second = static_cast(field_second.value())}; +} + +SetRadioView::SetRadioView( + NavigationView& nav) { + button_cancel.on_select = [&nav](Button&) { + nav.pop(); + }; + + const auto reference = portapack::clock_manager.get_reference(); + + std::string source_name("---"); + switch (reference.source) { + case ClockManager::ReferenceSource::Xtal: + source_name = "HackRF"; + break; + case ClockManager::ReferenceSource::PortaPack: + source_name = "PortaPack"; + break; + case ClockManager::ReferenceSource::External: + source_name = "External"; + break; + } + + value_source.set(source_name); + value_source_frequency.set(to_string_dec_uint(reference.frequency / 1000000, 2) + "." + to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz"); + + label_source.set_style(&style_text); + value_source.set_style(&style_text); + value_source_frequency.set_style(&style_text); + + add_children({ + &label_source, + &value_source, + &value_source_frequency, + }); + + if (reference.source == ClockManager::ReferenceSource::Xtal) { + add_children({ + &labels_correction, + &field_ppm, + }); + } + + add_children({&check_clkout, + &field_clkout_freq, + &labels_clkout_khz, + &value_freq_step, + &labels_bias, + &check_bias, + &button_save, + &button_cancel}); + + SetFrequencyCorrectionModel model{ + static_cast(portapack::persistent_memory::correction_ppb() / 1000), 0}; + + form_init(model); + + check_clkout.set_value(portapack::persistent_memory::clkout_enabled()); + check_clkout.on_select = [this](Checkbox&, bool v) { + clock_manager.enable_clock_output(v); + portapack::persistent_memory::set_clkout_enabled(v); + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + }; + + field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq()); + value_freq_step.set_style(&style_text); + + field_clkout_freq.on_select = [this](NumberField&) { + freq_step_khz++; + if (freq_step_khz > 3) { + freq_step_khz = 0; + } + switch (freq_step_khz) { + case 0: + value_freq_step.set(" |"); + break; + case 1: + value_freq_step.set(" | "); + break; + case 2: + value_freq_step.set(" | "); + break; + case 3: + value_freq_step.set("| "); + break; + } + field_clkout_freq.set_step(pow(10, freq_step_khz)); + }; + + check_bias.set_value(portapack::get_antenna_bias()); + check_bias.on_select = [this](Checkbox&, bool v) { + portapack::set_antenna_bias(v); + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + }; + + button_save.on_select = [this, &nav](Button&) { + const auto model = this->form_collect(); + portapack::persistent_memory::set_correction_ppb(model.ppm * 1000); + portapack::persistent_memory::set_clkout_freq(model.freq); + clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled()); + nav.pop(); + }; +} + +void SetRadioView::focus() { + button_save.focus(); +} + +void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) { + field_ppm.set_value(model.ppm); +} + +SetFrequencyCorrectionModel SetRadioView::form_collect() { + return { + .ppm = static_cast(field_ppm.value()), + .freq = static_cast(field_clkout_freq.value()), + }; +} + +SetUIView::SetUIView(NavigationView& nav) { + add_children({&checkbox_disable_touchscreen, + &checkbox_speaker, + &checkbox_bloff, + &options_bloff, + &checkbox_showsplash, + &checkbox_showclock, + &options_clockformat, + &checkbox_guireturnflag, + &button_save, + &button_cancel}); + + checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen()); + checkbox_speaker.set_value(persistent_memory::config_speaker()); + checkbox_showsplash.set_value(persistent_memory::config_splash()); + checkbox_showclock.set_value(!persistent_memory::hide_clock()); + checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon()); + + const auto backlight_config = persistent_memory::config_backlight_timer(); + checkbox_bloff.set_value(backlight_config.timeout_enabled()); + options_bloff.set_by_value(backlight_config.timeout_enum()); + + if (persistent_memory::clock_with_date()) { + options_clockformat.set_selected_index(1); + } else { + options_clockformat.set_selected_index(0); + } + + button_save.on_select = [&nav, this](Button&) { + persistent_memory::set_config_backlight_timer({(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(), + checkbox_bloff.value()}); + + if (checkbox_showclock.value()) { + if (options_clockformat.selected_index() == 1) + persistent_memory::set_clock_with_date(true); + else + persistent_memory::set_clock_with_date(false); + } + + if (checkbox_speaker.value()) audio::output::speaker_mute(); // Just mute audio if speaker is disabled + persistent_memory::set_config_speaker(checkbox_speaker.value()); // Store Speaker status + StatusRefreshMessage message{}; // Refresh status bar with/out speaker + EventDispatcher::send_message(message); + + persistent_memory::set_config_splash(checkbox_showsplash.value()); + persistent_memory::set_clock_hidden(!checkbox_showclock.value()); + persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value()); + persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value()); + nav.pop(); + }; + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetUIView::focus() { + button_save.focus(); +} + +// --------------------------------------------------------- +// Appl. Settings +// --------------------------------------------------------- +SetAppSettingsView::SetAppSettingsView(NavigationView& nav) { + add_children({&checkbox_load_app_settings, + &checkbox_save_app_settings, + &button_save, + &button_cancel}); + + checkbox_load_app_settings.set_value(persistent_memory::load_app_settings()); + checkbox_save_app_settings.set_value(persistent_memory::save_app_settings()); + + button_save.on_select = [&nav, this](Button&) { + persistent_memory::set_load_app_settings(checkbox_load_app_settings.value()); + persistent_memory::set_save_app_settings(checkbox_save_app_settings.value()); + nav.pop(); + }; + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetAppSettingsView::focus() { + button_save.focus(); +} + +// --------------------------------------------------------- +// Converter Settings +// --------------------------------------------------------- +SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) { + add_children({&check_show_converter, + &check_converter, + &converter_mode, + &button_converter_freq, + &button_save, + &button_cancel}); + + check_show_converter.set_value(!portapack::persistent_memory::config_hide_converter()); + check_show_converter.on_select = [this](Checkbox&, bool v) { + portapack::persistent_memory::set_config_hide_converter(!v); + if (!v) { + check_converter.set_value(false); + } + // Retune to take converter change in account + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + // Refresh status bar with/out converter + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + }; + + check_converter.set_value(portapack::persistent_memory::config_converter()); + check_converter.on_select = [this](Checkbox&, bool v) { + if (v) { + check_show_converter.set_value(true); + portapack::persistent_memory::set_config_hide_converter(false); + } + portapack::persistent_memory::set_config_converter(v); + // Retune to take converter change in account + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + // Refresh status bar with/out converter + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + }; + + converter_mode.set_by_value(portapack::persistent_memory::config_updown_converter()); + converter_mode.on_change = [this](size_t, OptionsField::value_t v) { + portapack::persistent_memory::set_config_updown_converter(v); + // Refresh status bar with icon up or down + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + }; + + button_converter_freq.set_text(to_string_short_freq(portapack::persistent_memory::config_converter_freq()) + "MHz"); + button_converter_freq.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(portapack::persistent_memory::config_converter_freq()); + new_view->on_changed = [this, &button](rf::Frequency f) { + portapack::persistent_memory::set_config_converter_freq(f); + // Retune to take converter change in account + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + button_converter_freq.set_text("<" + to_string_short_freq(f) + " MHz>"); + }; + }; + + button_save.on_select = [&nav, this](Button&) { + nav.pop(); + }; + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetConverterSettingsView::focus() { + button_save.focus(); +} + +// --------------------------------------------------------- +// Persistent Memory Settings +// --------------------------------------------------------- +SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { + add_children({&text_pmem_about, + &text_pmem_informations, + &text_pmem_status, + &check_load_mem_at_startup, + &button_save_mem_to_file, + &button_load_mem_from_file, + &button_load_mem_defaults, + &button_return}); + + bool load_mem_at_startup = false; + File pmem_flag_file_handle; + + std::string folder = "SETTINGS"; + make_new_directory(folder); + + std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG"; + auto result = pmem_flag_file_handle.open(pmem_flag_file); + if (!result.is_valid()) { + load_mem_at_startup = true; + } + check_load_mem_at_startup.set_value(load_mem_at_startup); + check_load_mem_at_startup.on_select = [this](Checkbox&, bool v) { + File pmem_flag_file_handle; + std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG"; + if (v) { + auto result = pmem_flag_file_handle.open(pmem_flag_file); + if (result.is_valid()) { + auto result = pmem_flag_file_handle.create(pmem_flag_file); // third: create if it is not there + if (!result.is_valid()) { + text_pmem_status.set("pmem flag file created"); + } else { + text_pmem_status.set("!err. creating pmem flagfile!"); + } + } else { + text_pmem_status.set("pmem flag already present"); + } + } else { + auto result = delete_file(pmem_flag_file); + if (result.code() != FR_OK) { + text_pmem_status.set("!err. deleting pmem flagfile!"); + } else { + text_pmem_status.set("pmem flag file deleted"); + } + } + }; + + button_save_mem_to_file.on_select = [&nav, this](Button&) { + if (!portapack::persistent_memory::save_persistent_settings_to_file("SETTINGS/pmem_settings")) { + text_pmem_status.set("!problem saving settings!"); + } else { + text_pmem_status.set("settings saved"); + } + }; + + button_load_mem_from_file.on_select = [&nav, this](Button&) { + if (!portapack::persistent_memory::load_persistent_settings_from_file("SETTINGS/pmem_settings")) { + text_pmem_status.set("!problem loading settings!"); + } else { + text_pmem_status.set("settings loaded"); + // Refresh status bar with icon up or down + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); + } + }; + + button_load_mem_defaults.on_select = [&nav, this](Button&) { + nav.push( + "Warning!", + "This will reset the p.mem\nand set the default settings", + YESNO, + [this](bool choice) { + if (choice) { + portapack::persistent_memory::cache::defaults(); + } + }); + }; + + button_return.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetPersistentMemoryView::focus() { + button_return.focus(); +} + +// +// Audio settings +// +SetAudioView::SetAudioView(NavigationView& nav) { + add_children({&labels, + &field_tone_mix, + &button_save, + &button_cancel}); + + field_tone_mix.set_value(persistent_memory::tone_mix()); + + button_save.on_select = [&nav, this](Button&) { + persistent_memory::set_tone_mix(field_tone_mix.value()); + nav.pop(); + }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetAudioView::focus() { + button_save.focus(); +} + +SetQRCodeView::SetQRCodeView(NavigationView& nav) { + add_children({&checkbox_bigger_qr, + &button_save, + &button_cancel}); + + checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code()); + + button_save.on_select = [&nav, this](Button&) { + persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value()); + nav.pop(); + }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetQRCodeView::focus() { + button_save.focus(); +} + +// --------------------------------------------------------- +// Settings main menu +// --------------------------------------------------------- +SettingsMenuView::SettingsMenuView(NavigationView& nav) { + if (portapack::persistent_memory::show_gui_return_icon()) { + add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + } + add_items({ + {"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push(); }}, + {"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, + {"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push(); }}, + {"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav]() { nav.push(); }}, + {"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push(); }}, + {"App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push(); }}, + {"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, + {"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push(); }}, + {"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push(); }}, + }); + set_max_rows(2); // allow wider buttons +} } /* namespace ui */ diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 34b736125..4ab1afd2a 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -34,464 +34,416 @@ namespace ui { struct SetDateTimeModel { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; }; class SetDateTimeView : public View { -public: - SetDateTimeView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Date/Time"; }; - -private: - Labels labels { - { { 6 * 8, 7 * 16 }, "YYYY-MM-DD HH:MM:SS", Color::grey() }, - { { 10 * 8, 9 * 16 }, "- - : :", Color::light_grey() } - }; - - NumberField field_year { - { 6 * 8, 9 * 16 }, - 4, - { 2015, 2099 }, - 1, - '0', - }; - NumberField field_month { - { 11 * 8, 9 * 16 }, - 2, - { 1, 12 }, - 1, - '0', - }; - NumberField field_day { - { 14 * 8, 9 * 16 }, - 2, - { 1, 31 }, - 1, - '0', - }; - - NumberField field_hour { - { 17 * 8, 9 * 16 }, - 2, - { 0, 23 }, - 1, - '0', - }; - NumberField field_minute { - { 20 * 8, 9 * 16 }, - 2, - { 0, 59 }, - 1, - '0', - }; - NumberField field_second { - { 23 * 8, 9 * 16 }, - 2, - { 0, 59 }, - 1, - '0', - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel" - }; - - void form_init(const SetDateTimeModel& model); - SetDateTimeModel form_collect(); + public: + SetDateTimeView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Date/Time"; }; + + private: + Labels labels{ + {{6 * 8, 7 * 16}, "YYYY-MM-DD HH:MM:SS", Color::grey()}, + {{10 * 8, 9 * 16}, "- - : :", Color::light_grey()}}; + + NumberField field_year{ + {6 * 8, 9 * 16}, + 4, + {2015, 2099}, + 1, + '0', + }; + NumberField field_month{ + {11 * 8, 9 * 16}, + 2, + {1, 12}, + 1, + '0', + }; + NumberField field_day{ + {14 * 8, 9 * 16}, + 2, + {1, 31}, + 1, + '0', + }; + + NumberField field_hour{ + {17 * 8, 9 * 16}, + 2, + {0, 23}, + 1, + '0', + }; + NumberField field_minute{ + {20 * 8, 9 * 16}, + 2, + {0, 59}, + 1, + '0', + }; + NumberField field_second{ + {23 * 8, 9 * 16}, + 2, + {0, 59}, + 1, + '0', + }; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel"}; + + void form_init(const SetDateTimeModel& model); + SetDateTimeModel form_collect(); }; struct SetFrequencyCorrectionModel { - int8_t ppm; - uint32_t freq; + int8_t ppm; + uint32_t freq; }; class SetRadioView : public View { -public: - SetRadioView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Radio"; }; - -private: - const Style style_text { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::light_grey(), - }; - uint8_t freq_step_khz = 3; - - Text label_source { - { 0, 1 * 16, 17 * 8, 16 }, - "Reference Source:" - }; - - Text value_source { - { (240 - 11 * 8), 1 * 16, 11 * 8, 16 }, - "---" - }; - - Text value_source_frequency { - { (240 - 11 * 8), 2 * 16, 11 * 8, 16 }, - "---" - }; - - Labels labels_correction { - { { 2 * 8, 3 * 16 }, "Frequency correction:", Color::light_grey() }, - { { 6 * 8, 4 * 16 }, "PPM", Color::light_grey() }, - }; - - Checkbox check_clkout { - { 18, (6 * 16 - 4) }, - 13, - "Enable CLKOUT" - }; - - NumberField field_clkout_freq { - { 20 * 8, 6 * 16 }, - 5, - { 10, 60000 }, - 1000, - ' ' - }; - - Labels labels_clkout_khz { - { { 26 * 8, 6 * 16 }, "kHz", Color::light_grey() } - }; - - Text value_freq_step { - { 21 * 8, (7 * 16 ), 4 * 8, 16 }, - "| " - }; - - Labels labels_bias { - { { 24, 8 * 16 }, "CAUTION: Ensure that all", Color::red() }, - { { 28, 9 * 16 }, "devices attached to the", Color::red() }, - { { 8, 10 * 16 }, "antenna connector can accept", Color::red() }, - { { 68, 11 * 16 }, "a DC voltage!", Color::red() } - }; - - NumberField field_ppm { - { 2 * 8, 4 * 16 }, - 3, - { -50, 50 }, - 1, - '0', - }; - - Checkbox check_bias { - { 18, 12 * 16 }, - 5, - "Turn on bias voltage" - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; - - void form_init(const SetFrequencyCorrectionModel& model); - SetFrequencyCorrectionModel form_collect(); + public: + SetRadioView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Radio"; }; + + private: + const Style style_text{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::light_grey(), + }; + uint8_t freq_step_khz = 3; + + Text label_source{ + {0, 1 * 16, 17 * 8, 16}, + "Reference Source:"}; + + Text value_source{ + {(240 - 11 * 8), 1 * 16, 11 * 8, 16}, + "---"}; + + Text value_source_frequency{ + {(240 - 11 * 8), 2 * 16, 11 * 8, 16}, + "---"}; + + Labels labels_correction{ + {{2 * 8, 3 * 16}, "Frequency correction:", Color::light_grey()}, + {{6 * 8, 4 * 16}, "PPM", Color::light_grey()}, + }; + + Checkbox check_clkout{ + {18, (6 * 16 - 4)}, + 13, + "Enable CLKOUT"}; + + NumberField field_clkout_freq{ + {20 * 8, 6 * 16}, + 5, + {10, 60000}, + 1000, + ' '}; + + Labels labels_clkout_khz{ + {{26 * 8, 6 * 16}, "kHz", Color::light_grey()}}; + + Text value_freq_step{ + {21 * 8, (7 * 16), 4 * 8, 16}, + "| "}; + + Labels labels_bias{ + {{24, 8 * 16}, "CAUTION: Ensure that all", Color::red()}, + {{28, 9 * 16}, "devices attached to the", Color::red()}, + {{8, 10 * 16}, "antenna connector can accept", Color::red()}, + {{68, 11 * 16}, "a DC voltage!", Color::red()}}; + + NumberField field_ppm{ + {2 * 8, 4 * 16}, + 3, + {-50, 50}, + 1, + '0', + }; + + Checkbox check_bias{ + {18, 12 * 16}, + 5, + "Turn on bias voltage"}; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; + + void form_init(const SetFrequencyCorrectionModel& model); + SetFrequencyCorrectionModel form_collect(); }; using portapack::persistent_memory::backlight_timeout_t; class SetUIView : public View { -public: - SetUIView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "UI"; }; - -private: - - Checkbox checkbox_disable_touchscreen { - { 3 * 8, 2 * 16 }, - 20, - "Disable touchscreen" - }; - - Checkbox checkbox_speaker { - { 3 * 8, 4 * 16 }, - 20, - "Hide H1 Speaker option" - }; - - Checkbox checkbox_bloff { - { 3 * 8, 6 * 16 }, - 20, - "Backlight off after:" - }; - OptionsField options_bloff { - { 52, 7 * 16 + 8 }, - 20, - { - { "5 seconds", backlight_timeout_t::Timeout5Sec }, - { "15 seconds", backlight_timeout_t::Timeout15Sec }, - { "30 seconds", backlight_timeout_t::Timeout30Sec }, - { "1 minute", backlight_timeout_t::Timeout60Sec }, - { "3 minutes", backlight_timeout_t::Timeout180Sec }, - { "5 minutes", backlight_timeout_t::Timeout300Sec }, - { "10 minutes", backlight_timeout_t::Timeout600Sec }, - { "1 hour", backlight_timeout_t::Timeout3600Sec }, - } - }; - - Checkbox checkbox_showsplash { - { 3 * 8, 9 * 16 }, - 20, - "Show splash" - }; - - Checkbox checkbox_showclock { - { 3 * 8, 11 * 16 }, - 20, - "Show clock with:" - }; - - OptionsField options_clockformat { - { 52, 12 * 16 + 8 }, - 20, - { - { "time only", 0 }, - { "time and date", 1 } - } - }; - - Checkbox checkbox_guireturnflag { - { 3 * 8, 14 * 16 }, - 25, - "add return icon in GUI" - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; + public: + SetUIView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "UI"; }; + + private: + Checkbox checkbox_disable_touchscreen{ + {3 * 8, 2 * 16}, + 20, + "Disable touchscreen"}; + + Checkbox checkbox_speaker{ + {3 * 8, 4 * 16}, + 20, + "Hide H1 Speaker option"}; + + Checkbox checkbox_bloff{ + {3 * 8, 6 * 16}, + 20, + "Backlight off after:"}; + OptionsField options_bloff{ + {52, 7 * 16 + 8}, + 20, + { + {"5 seconds", backlight_timeout_t::Timeout5Sec}, + {"15 seconds", backlight_timeout_t::Timeout15Sec}, + {"30 seconds", backlight_timeout_t::Timeout30Sec}, + {"1 minute", backlight_timeout_t::Timeout60Sec}, + {"3 minutes", backlight_timeout_t::Timeout180Sec}, + {"5 minutes", backlight_timeout_t::Timeout300Sec}, + {"10 minutes", backlight_timeout_t::Timeout600Sec}, + {"1 hour", backlight_timeout_t::Timeout3600Sec}, + }}; + + Checkbox checkbox_showsplash{ + {3 * 8, 9 * 16}, + 20, + "Show splash"}; + + Checkbox checkbox_showclock{ + {3 * 8, 11 * 16}, + 20, + "Show clock with:"}; + + OptionsField options_clockformat{ + {52, 12 * 16 + 8}, + 20, + {{"time only", 0}, + {"time and date", 1}}}; + + Checkbox checkbox_guireturnflag{ + {3 * 8, 14 * 16}, + 25, + "add return icon in GUI"}; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; }; class SetAppSettingsView : public View { -public: - SetAppSettingsView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "AppSettings"; }; - -private: - - Checkbox checkbox_load_app_settings { - { 3 * 8, 2 * 16 }, - 25, - "Load app settings" - }; - - Checkbox checkbox_save_app_settings { - { 3 * 8, 4 * 16 }, - 25, - "Save app settings" - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; + public: + SetAppSettingsView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "AppSettings"; }; + + private: + Checkbox checkbox_load_app_settings{ + {3 * 8, 2 * 16}, + 25, + "Load app settings"}; + + Checkbox checkbox_save_app_settings{ + {3 * 8, 4 * 16}, + 25, + "Save app settings"}; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; }; class SetConverterSettingsView : public View { -public: - SetConverterSettingsView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Converter"; }; - -private: - - Checkbox check_show_converter { - { 18, 4 * 16}, - 19, - "show/hide converter" - }; - - Checkbox check_converter { - { 18, 6 * 16}, - 7, - "enable/disable converter" - }; - - OptionsField converter_mode { - { 18 , 8 * 16 + 4 }, - 0, - { - {" + ",0}, // up converter - {" - ",1} // down converter - } - }; - - Button button_converter_freq { - { 18 + 4 * 8 , 8 * 16 , 16 * 8 , 24 }, - "", - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; + public: + SetConverterSettingsView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Converter"; }; + + private: + Checkbox check_show_converter{ + {18, 4 * 16}, + 19, + "show/hide converter"}; + + Checkbox check_converter{ + {18, 6 * 16}, + 7, + "enable/disable converter"}; + + OptionsField converter_mode{ + {18, 8 * 16 + 4}, + 0, + { + {" + ", 0}, // up converter + {" - ", 1} // down converter + }}; + + Button button_converter_freq{ + {18 + 4 * 8, 8 * 16, 16 * 8, 24}, + "", + }; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; }; class SetAudioView : public View { -public: - SetAudioView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Audio"; }; - -private: - Labels labels { - { { 2 * 8, 3 * 16 }, "Tone key mix: %", Color::light_grey() }, - }; - - NumberField field_tone_mix { - { 16 * 8, 3 * 16 }, - 2, - { 10, 99 }, - 1, - '0' - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; -}; + public: + SetAudioView(NavigationView& nav); + + void focus() override; + std::string title() const override { return "Audio"; }; + + private: + Labels labels{ + {{2 * 8, 3 * 16}, "Tone key mix: %", Color::light_grey()}, + }; + + NumberField field_tone_mix{ + {16 * 8, 3 * 16}, + 2, + {10, 99}, + 1, + '0'}; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; +}; class SetQRCodeView : public View { -public: - SetQRCodeView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "QR Code"; }; - -private: - Checkbox checkbox_bigger_qr { - { 3 * 8, 9 * 16 }, - 20, - "Show large QR code" - }; - - Button button_save { - { 2 * 8, 16 * 16, 12 * 8, 32 }, - "Save" - }; - - Button button_cancel { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Cancel", - }; + public: + SetQRCodeView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "QR Code"; }; + + private: + Checkbox checkbox_bigger_qr{ + {3 * 8, 9 * 16}, + 20, + "Show large QR code"}; + + Button button_save{ + {2 * 8, 16 * 16, 12 * 8, 32}, + "Save"}; + + Button button_cancel{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Cancel", + }; }; class SetPersistentMemoryView : public View { -public: - SetPersistentMemoryView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "P.Mem Mgmt"; }; - -private: - - Text text_pmem_about { - { 0, 1 * 16, 240 , 16 }, - "PersistentMemory from/to SD" - }; - - Text text_pmem_informations { - { 0, 2 * 16, 240 , 16 }, - "use: when no/dead coin bat." - }; - - Text text_pmem_status { - { 0, 3 * 16, 240 , 16 }, - "" - }; - - Checkbox check_load_mem_at_startup { - { 18, 6 * 16}, - 19, - "load from sd at startup" - }; - - Button button_save_mem_to_file { - { 0, 8 * 16, 240, 32 }, - "save p.mem to sdcard" - }; - - Button button_load_mem_from_file { - { 0, 10 * 16 + 4 , 240, 32 }, - "load p.mem from sdcard" - }; - - Button button_load_mem_defaults { - { 0, 12 * 16 + 8 , 240, 32 }, - "! reset p.mem, load defaults !" - }; - - Button button_return { - { 16 * 8, 16 * 16, 12 * 8, 32 }, - "Return", - }; -}; + public: + SetPersistentMemoryView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "P.Mem Mgmt"; }; + + private: + Text text_pmem_about{ + {0, 1 * 16, 240, 16}, + "PersistentMemory from/to SD"}; + + Text text_pmem_informations{ + {0, 2 * 16, 240, 16}, + "use: when no/dead coin bat."}; + Text text_pmem_status{ + {0, 3 * 16, 240, 16}, + ""}; + + Checkbox check_load_mem_at_startup{ + {18, 6 * 16}, + 19, + "load from sd at startup"}; + + Button button_save_mem_to_file{ + {0, 8 * 16, 240, 32}, + "save p.mem to sdcard"}; + + Button button_load_mem_from_file{ + {0, 10 * 16 + 4, 240, 32}, + "load p.mem from sdcard"}; + + Button button_load_mem_defaults{ + {0, 12 * 16 + 8, 240, 32}, + "! reset p.mem, load defaults !"}; + + Button button_return{ + {16 * 8, 16 * 16, 12 * 8, 32}, + "Return", + }; +}; class SettingsMenuView : public BtnGridView { -public: - SettingsMenuView(NavigationView& nav); - - std::string title() const override { return "Settings"; }; + public: + SettingsMenuView(NavigationView& nav); + + std::string title() const override { return "Settings"; }; }; } /* namespace ui */ -#endif/*__UI_SETTINGS_H__*/ +#endif /*__UI_SETTINGS_H__*/ diff --git a/firmware/application/apps/ui_sigfrx.cpp b/firmware/application/apps/ui_sigfrx.cpp index 3ebbedadc..9ca057fcd 100644 --- a/firmware/application/apps/ui_sigfrx.cpp +++ b/firmware/application/apps/ui_sigfrx.cpp @@ -46,105 +46,100 @@ using namespace portapack; namespace ui { void SIGFRXView::focus() { - button_exit.focus(); + button_exit.focus(); } SIGFRXView::~SIGFRXView() { - receiver_model.disable(); + receiver_model.disable(); } void SIGFRXView::paint(Painter& painter) { - uint8_t i, xp; - - //portapack::display.drawBMP({0, 302-160}, fox_bmp); - portapack::display.fill_rectangle({0,16,240,160-16}, ui::Color::white()); - for (i = 0; i < 6; i++) { - xp = sigfrx_marks[i*3]; - painter.draw_string({ (ui::Coord)sigfrx_marks[(i*3)+1], 144-20 }, style_white, to_string_dec_uint(sigfrx_marks[(i*3)+2]) ); - portapack::display.draw_line({xp, 144-4}, {xp, 144}, ui::Color::black()); - } + uint8_t i, xp; + + // portapack::display.drawBMP({0, 302-160}, fox_bmp); + portapack::display.fill_rectangle({0, 16, 240, 160 - 16}, ui::Color::white()); + for (i = 0; i < 6; i++) { + xp = sigfrx_marks[i * 3]; + painter.draw_string({(ui::Coord)sigfrx_marks[(i * 3) + 1], 144 - 20}, style_white, to_string_dec_uint(sigfrx_marks[(i * 3) + 2])); + portapack::display.draw_line({xp, 144 - 4}, {xp, 144}, ui::Color::black()); + } } void SIGFRXView::on_channel_spectrum(const ChannelSpectrum& spectrum) { - portapack::display.fill_rectangle({0, 144, 240, 4},ui::Color::white()); - - uint8_t xmax = 0, imax = 0; - size_t i; - - for (i=0; i<120; i++) { - if (spectrum.db[i] > xmax) { - xmax = spectrum.db[i]; - imax = i; - } - } - for (i=136; i<256; i++) { - if (spectrum.db[i-16] > xmax) { - xmax = spectrum.db[i-16]; - imax = i-16; - } - } - - if ((imax >= last_channel-2) && (imax <= last_channel+2)) { - if (detect_counter >= 5) { - // Latched ! - } else { - detect_counter++; - } - } else { - if (detect_counter >= 5) text_channel.set("... "); - detect_counter = 0; - } - - last_channel = imax; - - portapack::display.fill_rectangle({(ui::Coord)(imax-2), 144, 4, 4}, ui::Color::red()); + portapack::display.fill_rectangle({0, 144, 240, 4}, ui::Color::white()); + + uint8_t xmax = 0, imax = 0; + size_t i; + + for (i = 0; i < 120; i++) { + if (spectrum.db[i] > xmax) { + xmax = spectrum.db[i]; + imax = i; + } + } + for (i = 136; i < 256; i++) { + if (spectrum.db[i - 16] > xmax) { + xmax = spectrum.db[i - 16]; + imax = i - 16; + } + } + + if ((imax >= last_channel - 2) && (imax <= last_channel + 2)) { + if (detect_counter >= 5) { + // Latched ! + } else { + detect_counter++; + } + } else { + if (detect_counter >= 5) text_channel.set("... "); + detect_counter = 0; + } + + last_channel = imax; + + portapack::display.fill_rectangle({(ui::Coord)(imax - 2), 144, 4, 4}, ui::Color::red()); } void SIGFRXView::on_show() { - /*EventDispatcher::message_map().register_handler(Message::ID::ChannelSpectrum, - [this](const Message* const p) { - this->on_channel_spectrum(reinterpret_cast(p)->spectrum); - } - );*/ + /*EventDispatcher::message_map().register_handler(Message::ID::ChannelSpectrum, + [this](const Message* const p) { + this->on_channel_spectrum(reinterpret_cast(p)->spectrum); + } + );*/ } void SIGFRXView::on_hide() { - //EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrum); + // EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrum); } SIGFRXView::SIGFRXView( - NavigationView& nav -) -{ - receiver_model.set_baseband_configuration({ - .mode = 255, // DEBUG - .sampling_rate = 3072000, - .decimation_factor = 4, - }); - receiver_model.set_baseband_bandwidth(1750000); - - receiver_model.set_tuning_frequency(868110000); - - receiver_model.set_lna(0); - receiver_model.set_vga(0); - - add_children({ - &text_type, - &text_channel, - &text_data, - &button_exit - }); - - text_type.set_style(&style_white); - text_channel.set_style(&style_white); - text_data.set_style(&style_white); - - button_exit.on_select = [&nav](Button&){ - nav.pop(); - }; - - receiver_model.enable(); - + NavigationView& nav) { + receiver_model.set_baseband_configuration({ + .mode = 255, // DEBUG + .sampling_rate = 3072000, + .decimation_factor = 4, + }); + receiver_model.set_baseband_bandwidth(1750000); + + receiver_model.set_tuning_frequency(868110000); + + receiver_model.set_lna(0); + receiver_model.set_vga(0); + + add_children({&text_type, + &text_channel, + &text_data, + &button_exit}); + + text_type.set_style(&style_white); + text_channel.set_style(&style_white); + text_data.set_style(&style_white); + + button_exit.on_select = [&nav](Button&) { + nav.pop(); + }; + + receiver_model.enable(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_sigfrx.hpp b/firmware/application/apps/ui_sigfrx.hpp index 15cd734a4..ed45667ca 100644 --- a/firmware/application/apps/ui_sigfrx.hpp +++ b/firmware/application/apps/ui_sigfrx.hpp @@ -36,52 +36,47 @@ namespace ui { class SIGFRXView : public View { -public: - SIGFRXView(NavigationView& nav); - ~SIGFRXView(); - void on_channel_spectrum(const ChannelSpectrum& spectrum); - - void on_show() override; - void on_hide() override; - void focus() override; - void paint(Painter& painter) override; + public: + SIGFRXView(NavigationView& nav); + ~SIGFRXView(); + void on_channel_spectrum(const ChannelSpectrum& spectrum); -private: - uint8_t last_channel; - uint8_t detect_counter = 0; - - const Style style_white { - .font = font::fixed_8x16, - .background = Color::white(), - .foreground = Color::black() - }; - - const uint16_t sigfrx_marks[18] = { - 10, 8, 0, - 60, 52, 90, - 119, 95, 180, - 121, 122, 220, - 179, 171, 310, - 230, 214, 400 }; - - Text text_type { - { 1 * 8, 1 * 16, 28 * 8, 16 }, - "SIGFOX interceptor. Yap !" - }; - - Text text_channel { - { 1 * 8, 3 * 16, 28 * 8, 16 }, - "PL: " - }; - Text text_data { - { 1 * 8, 4 * 16, 28 * 8, 16 }, - "??: " - }; - - Button button_exit { - { 22 * 8, 160 - 32, 56, 32 }, - "Exit" - }; + void on_show() override; + void on_hide() override; + void focus() override; + void paint(Painter& painter) override; + + private: + uint8_t last_channel; + uint8_t detect_counter = 0; + + const Style style_white{ + .font = font::fixed_8x16, + .background = Color::white(), + .foreground = Color::black()}; + + const uint16_t sigfrx_marks[18] = { + 10, 8, 0, + 60, 52, 90, + 119, 95, 180, + 121, 122, 220, + 179, 171, 310, + 230, 214, 400}; + + Text text_type{ + {1 * 8, 1 * 16, 28 * 8, 16}, + "SIGFOX interceptor. Yap !"}; + + Text text_channel{ + {1 * 8, 3 * 16, 28 * 8, 16}, + "PL: "}; + Text text_data{ + {1 * 8, 4 * 16, 28 * 8, 16}, + "??: "}; + + Button button_exit{ + {22 * 8, 160 - 32, 56, 32}, + "Exit"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_siggen.cpp b/firmware/application/apps/ui_siggen.cpp index 12ed16cc1..f9bb230ab 100644 --- a/firmware/application/apps/ui_siggen.cpp +++ b/firmware/application/apps/ui_siggen.cpp @@ -34,110 +34,103 @@ using namespace portapack; namespace ui { void SigGenView::focus() { - options_shape.focus(); + options_shape.focus(); } SigGenView::~SigGenView() { - transmitter_model.disable(); - baseband::shutdown(); + transmitter_model.disable(); + baseband::shutdown(); } void SigGenView::update_config() { - if(checkbox_stop.value()) - baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value()); - else - baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0); - + if (checkbox_stop.value()) + baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value()); + else + baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0); } void SigGenView::update_tone() { - baseband::set_siggen_tone(symfield_tone.value_dec_u32()); + baseband::set_siggen_tone(symfield_tone.value_dec_u32()); } void SigGenView::start_tx() { - transmitter_model.set_sampling_rate(1536000); -// transmitter_model.set_rf_amp(true); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - update_tone(); - - /*auto duration = field_stop.value(); - if (!checkbox_auto.value()) - duration = 0;*/ - update_config(); -} + transmitter_model.set_sampling_rate(1536000); + // transmitter_model.set_rf_amp(true); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + update_tone(); + /*auto duration = field_stop.value(); + if (!checkbox_auto.value()) + duration = 0;*/ + update_config(); +} void SigGenView::on_tx_progress(const uint32_t progress, const bool done) { - (void) progress; - - if (done) { - transmitter_model.disable(); - tx_view.set_transmitting(false); - } + (void)progress; + + if (done) { + transmitter_model.disable(); + tx_view.set_transmitting(false); + } } SigGenView::SigGenView( - NavigationView& nav -) -{ - baseband::run_image(portapack::spi_flash::image_tag_siggen); - - add_children({ - &labels, - &options_shape, - &text_shape, - &symfield_tone, - &button_update, - &checkbox_auto, - &checkbox_stop, - &field_stop, - &tx_view - }); - - options_shape.on_change = [this](size_t, OptionsField::value_t v) { - text_shape.set(shape_strings[v]); - if (auto_update) - update_config(); - }; - options_shape.set_selected_index(0); - text_shape.set(shape_strings[0]); - - field_stop.set_value(1); - - symfield_tone.set_sym(1, 1); // Default: 1000 Hz - symfield_tone.on_change = [this]() { - if (auto_update) - update_tone(); - }; - - button_update.on_select = [this](Button&) { - update_tone(); - update_config(); - }; - - checkbox_auto.on_select = [this](Checkbox&, bool v) { - auto_update = v; - }; - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - transmitter_model.disable(); - tx_view.set_transmitting(false); - }; + NavigationView& nav) { + baseband::run_image(portapack::spi_flash::image_tag_siggen); + + add_children({&labels, + &options_shape, + &text_shape, + &symfield_tone, + &button_update, + &checkbox_auto, + &checkbox_stop, + &field_stop, + &tx_view}); + + options_shape.on_change = [this](size_t, OptionsField::value_t v) { + text_shape.set(shape_strings[v]); + if (auto_update) + update_config(); + }; + options_shape.set_selected_index(0); + text_shape.set(shape_strings[0]); + + field_stop.set_value(1); + + symfield_tone.set_sym(1, 1); // Default: 1000 Hz + symfield_tone.on_change = [this]() { + if (auto_update) + update_tone(); + }; + + button_update.on_select = [this](Button&) { + update_tone(); + update_config(); + }; + + checkbox_auto.on_select = [this](Checkbox&, bool v) { + auto_update = v; + }; + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + tx_view.on_stop = [this]() { + transmitter_model.disable(); + tx_view.set_transmitting(false); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_siggen.hpp b/firmware/application/apps/ui_siggen.hpp index bb00ec417..fc25f09d3 100644 --- a/firmware/application/apps/ui_siggen.hpp +++ b/firmware/application/apps/ui_siggen.hpp @@ -34,105 +34,93 @@ namespace ui { class SigGenView : public View { -public: - SigGenView(NavigationView& nav); - ~SigGenView(); - - void focus() override; - - std::string title() const override { return "Signal gen"; }; - -private: - void start_tx(); - void update_config(); - void update_tone(); - void on_tx_progress(const uint32_t progress, const bool done); - - const std::string shape_strings[7] = { - "CW-just carrier", - "Sine signal ", - "Triangle signal", - "Saw up signal ", - "Saw down signal", - "Square signal ", - "Noise signal " // using 16 bits LFSR register, 16 order polynomial feedback. - }; - - bool auto_update { false }; - - Labels labels { - { { 3 * 8, 4 + 10 }, "Shape:", Color::light_grey() }, - { { 6 * 8, 7 * 8 }, "Tone: Hz", Color::light_grey() }, - { { 22 * 8, 15 * 8 + 4 }, "s.", Color::light_grey() }, - { { 8 * 8, 20 * 8 }, "Modulation: FM", Color::light_grey() } - }; - - ImageOptionsField options_shape { - { 10 * 8, 4, 32, 32 }, - Color::white(), - Color::black(), - { - { &bitmap_sig_cw, 0 }, - { &bitmap_sig_sine, 1 }, - { &bitmap_sig_tri, 2 }, - { &bitmap_sig_saw_up, 3 }, - { &bitmap_sig_saw_down, 4 }, - { &bitmap_sig_square, 5 }, - { &bitmap_sig_noise, 6 } - } - }; - - Text text_shape { - { 15 * 8, 4 + 10, 8 * 8, 16 }, - "" - }; - - SymField symfield_tone { - { 13 * 8, 7 * 8 }, - 5, - SymField::SYMFIELD_DEC - }; - - Button button_update { - { 5 * 8, 10 * 8, 8 * 8, 3 * 8 }, - "Update" - }; - - Checkbox checkbox_auto { - { 15 * 8, 10 * 8 }, - 4, - "Auto" - }; - - Checkbox checkbox_stop { - { 5 * 8, 15 * 8 }, - 10, - "Stop after" - }; - - NumberField field_stop { - { 20 * 8, 15 * 8 + 4 }, - 2, - { 1, 99 }, - 1, - ' ' - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + SigGenView(NavigationView& nav); + ~SigGenView(); + + void focus() override; + + std::string title() const override { return "Signal gen"; }; + + private: + void start_tx(); + void update_config(); + void update_tone(); + void on_tx_progress(const uint32_t progress, const bool done); + + const std::string shape_strings[7] = { + "CW-just carrier", + "Sine signal ", + "Triangle signal", + "Saw up signal ", + "Saw down signal", + "Square signal ", + "Noise signal " // using 16 bits LFSR register, 16 order polynomial feedback. + }; + + bool auto_update{false}; + + Labels labels{ + {{3 * 8, 4 + 10}, "Shape:", Color::light_grey()}, + {{6 * 8, 7 * 8}, "Tone: Hz", Color::light_grey()}, + {{22 * 8, 15 * 8 + 4}, "s.", Color::light_grey()}, + {{8 * 8, 20 * 8}, "Modulation: FM", Color::light_grey()}}; + + ImageOptionsField options_shape{ + {10 * 8, 4, 32, 32}, + Color::white(), + Color::black(), + {{&bitmap_sig_cw, 0}, + {&bitmap_sig_sine, 1}, + {&bitmap_sig_tri, 2}, + {&bitmap_sig_saw_up, 3}, + {&bitmap_sig_saw_down, 4}, + {&bitmap_sig_square, 5}, + {&bitmap_sig_noise, 6}}}; + + Text text_shape{ + {15 * 8, 4 + 10, 8 * 8, 16}, + ""}; + + SymField symfield_tone{ + {13 * 8, 7 * 8}, + 5, + SymField::SYMFIELD_DEC}; + + Button button_update{ + {5 * 8, 10 * 8, 8 * 8, 3 * 8}, + "Update"}; + + Checkbox checkbox_auto{ + {15 * 8, 10 * 8}, + 4, + "Auto"}; + + Checkbox checkbox_stop{ + {5 * 8, 15 * 8}, + 10, + "Stop after"}; + + NumberField field_stop{ + {20 * 8, 15 * 8 + 4}, + 2, + {1, 99}, + 1, + ' '}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ -#endif/*__SIGGEN_H__*/ +#endif /*__SIGGEN_H__*/ diff --git a/firmware/application/apps/ui_sonde.cpp b/firmware/application/apps/ui_sonde.cpp index bdbe1a5c7..75480cee8 100644 --- a/firmware/application/apps/ui_sonde.cpp +++ b/firmware/application/apps/ui_sonde.cpp @@ -34,253 +34,237 @@ using namespace portapack; #include "string_format.hpp" #include "complex.hpp" - void SondeLogger::on_packet(const sonde::Packet& packet) { - const auto formatted = packet.symbols_formatted(); - log_file.write_entry(packet.received_at(), formatted.data); + const auto formatted = packet.symbols_formatted(); + log_file.write_entry(packet.received_at(), formatted.data); } namespace ui { - SondeView::SondeView(NavigationView& nav) { - - - baseband::run_image(portapack::spi_flash::image_tag_sonde); - - add_children({ - &labels, - &field_frequency, - &field_rf_amp, - &field_lna, - &field_vga, - &rssi, - &field_volume, - &check_beep, - &check_log, - &check_crc, - &text_signature, - &text_serial, - &text_timestamp, - &text_voltage, - &text_frame, - &text_temp, - &text_humid, - &geopos, - &button_see_qr, - &button_see_map - }); - - - // load app settings - auto rc = settings.load("rx_sonde", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - target_frequency_ = app_settings.rx_frequency; - } - else target_frequency_ = receiver_model.tuning_frequency(); - - - field_frequency.set_value(target_frequency_); - field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning - field_frequency.on_change = [this](rf::Frequency f) { - set_target_frequency(f); - field_frequency.set_value(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - set_target_frequency(f); - field_frequency.set_value(f); - }; - }; - - geopos.set_read_only(true); - - check_beep.on_select = [this](Checkbox&, bool v) { - beep = v; - }; - - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - }; - - check_crc.on_select = [this](Checkbox&, bool v) { - use_crc = v; - }; - + baseband::run_image(portapack::spi_flash::image_tag_sonde); + + add_children({&labels, + &field_frequency, + &field_rf_amp, + &field_lna, + &field_vga, + &rssi, + &field_volume, + &check_beep, + &check_log, + &check_crc, + &text_signature, + &text_serial, + &text_timestamp, + &text_voltage, + &text_frame, + &text_temp, + &text_humid, + &geopos, + &button_see_qr, + &button_see_map}); + + // load app settings + auto rc = settings.load("rx_sonde", &app_settings); + if (rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + target_frequency_ = app_settings.rx_frequency; + } else + target_frequency_ = receiver_model.tuning_frequency(); + + field_frequency.set_value(target_frequency_); + field_frequency.set_step(500); // euquiq: was 10000, but we are using this for fine-tunning + field_frequency.on_change = [this](rf::Frequency f) { + set_target_frequency(f); + field_frequency.set_value(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + set_target_frequency(f); + field_frequency.set_value(f); + }; + }; + + geopos.set_read_only(true); + + check_beep.on_select = [this](Checkbox&, bool v) { + beep = v; + }; + + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + check_crc.on_select = [this](Checkbox&, bool v) { + use_crc = v; + }; + receiver_model.set_tuning_frequency(tuning_frequency()); receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_baseband_bandwidth(baseband_bandwidth); - receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - - - // QR code with geo URI - button_see_qr.on_select = [this, &nav](Button&) { - nav.push(geo_uri); - }; - - button_see_map.on_select = [this, &nav](Button&) { - nav.push( - sonde_id, - gps_info.alt, - GeoPos::alt_unit::METERS, - gps_info.lat, - gps_info.lon, - 999); //set a dummy heading out of range to draw a cross...probably not ideal? - }; - - logger = std::make_unique(); - if (logger) - logger->append( LOG_ROOT_DIR "/SONDE.TXT" ); - - // initialize audio: - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - audio::output::start(); - audio::output::speaker_unmute(); - - // inject a PitchRSSIConfigureMessage in order to arm - // the pitch rssi events that will be used by the - // processor: - const PitchRSSIConfigureMessage message { true, 0 }; - - shared_memory.application_queue.push(message); - - baseband::set_pitch_rssi(0, true); + receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. + + // QR code with geo URI + button_see_qr.on_select = [this, &nav](Button&) { + nav.push(geo_uri); + }; + + button_see_map.on_select = [this, &nav](Button&) { + nav.push( + sonde_id, + gps_info.alt, + GeoPos::alt_unit::METERS, + gps_info.lat, + gps_info.lon, + 999); // set a dummy heading out of range to draw a cross...probably not ideal? + }; + + logger = std::make_unique(); + if (logger) + logger->append(LOG_ROOT_DIR "/SONDE.TXT"); + + // initialize audio: + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + audio::output::start(); + audio::output::speaker_unmute(); + + // inject a PitchRSSIConfigureMessage in order to arm + // the pitch rssi events that will be used by the + // processor: + const PitchRSSIConfigureMessage message{true, 0}; + + shared_memory.application_queue.push(message); + + baseband::set_pitch_rssi(0, true); } SondeView::~SondeView() { - // save app settings - app_settings.rx_frequency = target_frequency_; - settings.save("rx_sonde", &app_settings); + // save app settings + app_settings.rx_frequency = target_frequency_; + settings.save("rx_sonde", &app_settings); - baseband::set_pitch_rssi(0, false); + baseband::set_pitch_rssi(0, false); - receiver_model.disable(); // to switch off all, including DC bias. - baseband::shutdown(); - audio::output::stop(); + receiver_model.disable(); // to switch off all, including DC bias. + baseband::shutdown(); + audio::output::stop(); } void SondeView::focus() { - field_vga.focus(); + field_vga.focus(); } - // used to convert float to character pointer, since unfortunately function like // sprintf and c_str aren't supported. -char * SondeView::float_to_char(float x, char *p) -{ - - char *s = p + 9; // go to end of buffer - uint16_t decimals; // variable to store the decimals - int units; // variable to store the units (part to left of decimal place) - if (x < 0) { // take care of negative numbers - decimals = (int)(x * -100000) % 100000; // make 1000 for 3 decimals etc. - units = (int)(-1 * x); - } else { // positive numbers - decimals = (int)(x * 100000) % 100000; - units = (int)x; - } - - // TODO: more elegant solution (loop?) - *--s = (decimals % 10) + '0'; - decimals /= 10; - *--s = (decimals % 10) + '0'; - decimals /= 10; - *--s = (decimals % 10) + '0'; - decimals /= 10; - *--s = (decimals % 10) + '0'; - decimals /= 10; - *--s = (decimals % 10) + '0'; - *--s = '.'; - - while (units > 0) { - *--s = (units % 10) + '0'; - units /= 10; - } - if (x < 0) *--s = '-'; // unary minus sign for negative numbers - return s; +char* SondeView::float_to_char(float x, char* p) { + char* s = p + 9; // go to end of buffer + uint16_t decimals; // variable to store the decimals + int units; // variable to store the units (part to left of decimal place) + if (x < 0) { // take care of negative numbers + decimals = (int)(x * -100000) % 100000; // make 1000 for 3 decimals etc. + units = (int)(-1 * x); + } else { // positive numbers + decimals = (int)(x * 100000) % 100000; + units = (int)x; + } + + // TODO: more elegant solution (loop?) + *--s = (decimals % 10) + '0'; + decimals /= 10; + *--s = (decimals % 10) + '0'; + decimals /= 10; + *--s = (decimals % 10) + '0'; + decimals /= 10; + *--s = (decimals % 10) + '0'; + decimals /= 10; + *--s = (decimals % 10) + '0'; + *--s = '.'; + + while (units > 0) { + *--s = (units % 10) + '0'; + units /= 10; + } + if (x < 0) *--s = '-'; // unary minus sign for negative numbers + return s; } -void SondeView::on_packet(const sonde::Packet &packet) -{ - if (!use_crc || packet.crc_ok()) //euquiq: Reject bad packet if crc is on - { - - char buffer_lat[10] = {}; - char buffer_lon[10] = {}; - - strcpy(geo_uri, "geo:"); - strcat(geo_uri, float_to_char(gps_info.lat, buffer_lat)); - strcat(geo_uri, ","); - strcat(geo_uri, float_to_char(gps_info.lon, buffer_lon)); - - text_signature.set(packet.type_string()); - - sonde_id = packet.serial_number(); //used also as tag on the geomap - text_serial.set(sonde_id); - - text_timestamp.set(to_string_timestamp(packet.received_at())); - - text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 2) + "V"); - - text_frame.set(to_string_dec_uint(packet.frame(),0)); //euquiq: integrate frame #, temp & humid. - - temp_humid_info = packet.get_temp_humid(); - if (temp_humid_info.humid != 0) - { - double decimals = abs(get_decimals(temp_humid_info.humid, 10, true)); - //if (decimals < 0) - // decimals = -decimals; - text_humid.set(to_string_dec_int((int)temp_humid_info.humid) + "." + to_string_dec_uint(decimals, 1) + "%"); - } - - if (temp_humid_info.temp != 0) - { - double decimals = abs(get_decimals(temp_humid_info.temp, 10, true)); - // if (decimals < 0) - // decimals = -decimals; - text_temp.set(to_string_dec_int((int)temp_humid_info.temp) + "." + to_string_dec_uint(decimals, 1) + "C"); - } - - gps_info = packet.get_GPS_data(); - - geopos.set_altitude(gps_info.alt); - geopos.set_lat(gps_info.lat); - geopos.set_lon(gps_info.lon); - - if (logger && logging) { - logger->on_packet(packet); - } - - if(beep) { - baseband::request_beep(); - } - } +void SondeView::on_packet(const sonde::Packet& packet) { + if (!use_crc || packet.crc_ok()) // euquiq: Reject bad packet if crc is on + { + char buffer_lat[10] = {}; + char buffer_lon[10] = {}; + + strcpy(geo_uri, "geo:"); + strcat(geo_uri, float_to_char(gps_info.lat, buffer_lat)); + strcat(geo_uri, ","); + strcat(geo_uri, float_to_char(gps_info.lon, buffer_lon)); + + text_signature.set(packet.type_string()); + + sonde_id = packet.serial_number(); // used also as tag on the geomap + text_serial.set(sonde_id); + + text_timestamp.set(to_string_timestamp(packet.received_at())); + + text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 2) + "V"); + + text_frame.set(to_string_dec_uint(packet.frame(), 0)); // euquiq: integrate frame #, temp & humid. + + temp_humid_info = packet.get_temp_humid(); + if (temp_humid_info.humid != 0) { + double decimals = abs(get_decimals(temp_humid_info.humid, 10, true)); + // if (decimals < 0) + // decimals = -decimals; + text_humid.set(to_string_dec_int((int)temp_humid_info.humid) + "." + to_string_dec_uint(decimals, 1) + "%"); + } + + if (temp_humid_info.temp != 0) { + double decimals = abs(get_decimals(temp_humid_info.temp, 10, true)); + // if (decimals < 0) + // decimals = -decimals; + text_temp.set(to_string_dec_int((int)temp_humid_info.temp) + "." + to_string_dec_uint(decimals, 1) + "C"); + } + + gps_info = packet.get_GPS_data(); + + geopos.set_altitude(gps_info.alt); + geopos.set_lat(gps_info.lat); + geopos.set_lon(gps_info.lon); + + if (logger && logging) { + logger->on_packet(packet); + } + + if (beep) { + baseband::request_beep(); + } + } } void SondeView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); } void SondeView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - //radio::set_tuning_frequency(tuning_frequency()); - // we better remember the tuned frequency, by using this function instead: - receiver_model.set_tuning_frequency(target_frequency_); + target_frequency_ = new_value; + // radio::set_tuning_frequency(tuning_frequency()); + // we better remember the tuned frequency, by using this function instead: + receiver_model.set_tuning_frequency(target_frequency_); } uint32_t SondeView::tuning_frequency() const { - return target_frequency_ - (sampling_rate / 4); + return target_frequency_ - (sampling_rate / 4); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_sonde.hpp b/firmware/application/apps/ui_sonde.hpp index 1c8ca29a9..a0768548b 100644 --- a/firmware/application/apps/ui_sonde.hpp +++ b/firmware/application/apps/ui_sonde.hpp @@ -39,177 +39,155 @@ #include class SondeLogger { -public: - Optional append(const std::filesystem::path& filename) { - return log_file.append(filename); - } - - void on_packet(const sonde::Packet& packet); - -private: - LogFile log_file { }; + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + + void on_packet(const sonde::Packet& packet); + + private: + LogFile log_file{}; }; namespace ui { class SondeView : public View { -public: - static constexpr uint32_t sampling_rate = 2457600; - static constexpr uint32_t baseband_bandwidth = 1750000; - - SondeView(NavigationView& nav); - ~SondeView(); - - void focus() override; - - std::string title() const override { return "Radiosnd RX"; }; - - - -private: - std::unique_ptr logger { }; - uint32_t target_frequency_ { 402700000 }; - bool logging { false }; - bool use_crc { false }; - bool beep { false }; - - char geo_uri[32] = {}; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - sonde::GPS_data gps_info { }; - sonde::temp_humid temp_humid_info { }; - std::string sonde_id { }; - - // AudioOutput audio_output { }; - - Labels labels { - { { 4 * 8, 2 * 16 }, "Type:", Color::light_grey() }, - { { 6 * 8, 3 * 16 }, "ID:", Color::light_grey() }, - { { 0 * 8, 4 * 16 }, "DateTime:", Color::light_grey() }, - - { { 3 * 8, 5 * 16 }, "Vbatt:", Color::light_grey() }, - { { 3 * 8, 6 * 16 }, "Frame:", Color::light_grey() }, - { { 4 * 8, 7 * 16 }, "Temp:", Color::light_grey() }, - { { 0 * 8, 8 * 16 }, "Humidity:", Color::light_grey() } - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 8 }, - }; - - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - - NumberField field_volume { - { 28 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - - - Checkbox check_beep { - { 22 * 8, 6 * 16 }, - 3, - "Beep" - }; - - Checkbox check_log { - { 22 * 8, 8 * 16 }, - 3, - "Log" - }; - - Checkbox check_crc { - { 22 * 8, 10 * 16 }, - 3, - "CRC" - }; - - Text text_signature { - { 9 * 8, 2 * 16, 10 * 8, 16 }, - "..." - }; - - Text text_serial { - { 9 * 8, 3 * 16, 11 * 8, 16 }, - "..." - }; - - Text text_timestamp { - { 9 * 8, 4 * 16, 11 * 8, 16 }, - "..." - }; - - Text text_voltage { - { 9 * 8, 5 * 16, 10 * 8, 16 }, - "..." - }; - - Text text_frame { - { 9 * 8, 6 * 16, 10 * 8, 16 }, - "..." - }; - - Text text_temp { - { 9 * 8, 7 * 16, 10 * 8, 16 }, - "..." - }; - - Text text_humid { - { 9 * 8, 8 * 16, 10 * 8, 16 }, - "..." - }; - - GeoPos geopos { - { 0, 12 * 16 }, - GeoPos::alt_unit::METERS - }; - - - Button button_see_qr { - { 2 * 8, 15 * 16, 12 * 8, 3 * 16 }, - "See QR" - }; - - Button button_see_map { - { 16 * 8, 15 * 16, 12 * 8, 3 * 16 }, - "See on map" - }; - - MessageHandlerRegistration message_handler_packet { - Message::ID::SondePacket, - [this](Message* const p) { - const auto message = static_cast(p); - const sonde::Packet packet { message->packet, message->type }; - this->on_packet(packet); - } - }; - - void on_packet(const sonde::Packet& packet); - void on_headphone_volume_changed(int32_t v); - char * float_to_char(float x, char *p); - void set_target_frequency(const uint32_t new_value); - - uint32_t tuning_frequency() const; + public: + static constexpr uint32_t sampling_rate = 2457600; + static constexpr uint32_t baseband_bandwidth = 1750000; + + SondeView(NavigationView& nav); + ~SondeView(); + + void focus() override; + + std::string title() const override { return "Radiosnd RX"; }; + + private: + std::unique_ptr logger{}; + uint32_t target_frequency_{402700000}; + bool logging{false}; + bool use_crc{false}; + bool beep{false}; + + char geo_uri[32] = {}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + sonde::GPS_data gps_info{}; + sonde::temp_humid temp_humid_info{}; + std::string sonde_id{}; + + // AudioOutput audio_output { }; + + Labels labels{ + {{4 * 8, 2 * 16}, "Type:", Color::light_grey()}, + {{6 * 8, 3 * 16}, "ID:", Color::light_grey()}, + {{0 * 8, 4 * 16}, "DateTime:", Color::light_grey()}, + + {{3 * 8, 5 * 16}, "Vbatt:", Color::light_grey()}, + {{3 * 8, 6 * 16}, "Frame:", Color::light_grey()}, + {{4 * 8, 7 * 16}, "Temp:", Color::light_grey()}, + {{0 * 8, 8 * 16}, "Humidity:", Color::light_grey()}}; + + FrequencyField field_frequency{ + {0 * 8, 0 * 8}, + }; + + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + + NumberField field_volume{ + {28 * 8, 0 * 16}, + 2, + {0, 99}, + 1, + ' ', + }; + + Checkbox check_beep{ + {22 * 8, 6 * 16}, + 3, + "Beep"}; + + Checkbox check_log{ + {22 * 8, 8 * 16}, + 3, + "Log"}; + + Checkbox check_crc{ + {22 * 8, 10 * 16}, + 3, + "CRC"}; + + Text text_signature{ + {9 * 8, 2 * 16, 10 * 8, 16}, + "..."}; + + Text text_serial{ + {9 * 8, 3 * 16, 11 * 8, 16}, + "..."}; + + Text text_timestamp{ + {9 * 8, 4 * 16, 11 * 8, 16}, + "..."}; + + Text text_voltage{ + {9 * 8, 5 * 16, 10 * 8, 16}, + "..."}; + + Text text_frame{ + {9 * 8, 6 * 16, 10 * 8, 16}, + "..."}; + + Text text_temp{ + {9 * 8, 7 * 16, 10 * 8, 16}, + "..."}; + + Text text_humid{ + {9 * 8, 8 * 16, 10 * 8, 16}, + "..."}; + + GeoPos geopos{ + {0, 12 * 16}, + GeoPos::alt_unit::METERS}; + + Button button_see_qr{ + {2 * 8, 15 * 16, 12 * 8, 3 * 16}, + "See QR"}; + + Button button_see_map{ + {16 * 8, 15 * 16, 12 * 8, 3 * 16}, + "See on map"}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::SondePacket, + [this](Message* const p) { + const auto message = static_cast(p); + const sonde::Packet packet{message->packet, message->type}; + this->on_packet(packet); + }}; + + void on_packet(const sonde::Packet& packet); + void on_headphone_volume_changed(int32_t v); + char* float_to_char(float x, char* p); + void set_target_frequency(const uint32_t new_value); + + uint32_t tuning_frequency() const; }; } /* namespace ui */ -#endif/*__UI_SONDE_H__*/ +#endif /*__UI_SONDE_H__*/ diff --git a/firmware/application/apps/ui_spectrum_painter.cpp b/firmware/application/apps/ui_spectrum_painter.cpp index 0a1749dab..fd24e00ce 100644 --- a/firmware/application/apps/ui_spectrum_painter.cpp +++ b/firmware/application/apps/ui_spectrum_painter.cpp @@ -32,202 +32,195 @@ namespace ui { SpectrumPainterView::SpectrumPainterView( - NavigationView& nav -) : nav_ (nav) { - baseband::run_image(portapack::spi_flash::image_tag_spectrum_painter); - - add_children({ - &labels, - &tab_view, - &input_image, - &input_text, - &progressbar, - &field_frequency, - &field_rfgain, - &field_rfamp, - &check_loop, - &button_play, - &option_bandwidth, - &field_duration, - &field_pause, - }); - - Rect view_rect = { 0, 3 * 8, 240, 80 }; - input_image.set_parent_rect(view_rect); - input_text.set_parent_rect(view_rect); - - field_frequency.set_value(target_frequency()); - field_frequency.set_step(5000); - field_frequency.on_change = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - }; - field_frequency.on_edit = [this, &nav]() { - auto new_view = nav.push(this->target_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); - this->field_frequency.set_value(f); - }; - }; - - tx_gain = 10; - field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ). - field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file. - tx_gain = v; - portapack::transmitter_model.set_tx_gain(tx_gain); - }; - - field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's) - field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file. - rf_amp = (bool)v; - portapack::transmitter_model.set_rf_amp(rf_amp); - }; - - input_image.on_input_avaliable = [this]() { - image_input_avaliable = true; - }; - - button_play.on_select = [this](ImageButton&) { - if (tx_active == false) { - tx_mode = tab_view.selected(); - - if (tx_mode == 0 && image_input_avaliable == false) - return; - - //Enable Bias Tee if selected - radio::set_antenna_bias(portapack::get_antenna_bias()); - - radio::enable({ - portapack::receiver_model.tuning_frequency(), - 3072000U, - 1750000, - rf::Direction::Transmit, - rf_amp, - static_cast(portapack::receiver_model.lna()), - static_cast(portapack::receiver_model.vga()) - }); - - if (portapack::persistent_memory::stealth_mode()){ - DisplaySleepMessage message; - EventDispatcher::send_message(message); - } - - button_play.set_bitmap(&bitmap_stop); - - if (tx_mode == 0) { - tx_current_max_lines = input_image.get_height(); - tx_current_width = input_image.get_width(); - } - else { - tx_current_max_lines = input_text.get_height(); - tx_current_width = input_text.get_width(); - } - - progressbar.set_max(tx_current_max_lines); - progressbar.set_value(0); - - baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, false, option_bandwidth.selected_index_value()); - } - else { - stop_tx(); - } - }; - - option_bandwidth.on_change = [this](size_t, ui::OptionsField::value_t value) { - baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, true, value); - }; - - field_duration.set_value(10); - field_pause.set_value(5); + NavigationView& nav) + : nav_(nav) { + baseband::run_image(portapack::spi_flash::image_tag_spectrum_painter); + + add_children({ + &labels, + &tab_view, + &input_image, + &input_text, + &progressbar, + &field_frequency, + &field_rfgain, + &field_rfamp, + &check_loop, + &button_play, + &option_bandwidth, + &field_duration, + &field_pause, + }); + + Rect view_rect = {0, 3 * 8, 240, 80}; + input_image.set_parent_rect(view_rect); + input_text.set_parent_rect(view_rect); + + field_frequency.set_value(target_frequency()); + field_frequency.set_step(5000); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(this->target_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_target_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + tx_gain = 10; + field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ). + field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file. + tx_gain = v; + portapack::transmitter_model.set_tx_gain(tx_gain); + }; + + field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's) + field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file. + rf_amp = (bool)v; + portapack::transmitter_model.set_rf_amp(rf_amp); + }; + + input_image.on_input_avaliable = [this]() { + image_input_avaliable = true; + }; + + button_play.on_select = [this](ImageButton&) { + if (tx_active == false) { + tx_mode = tab_view.selected(); + + if (tx_mode == 0 && image_input_avaliable == false) + return; + + // Enable Bias Tee if selected + radio::set_antenna_bias(portapack::get_antenna_bias()); + + radio::enable({portapack::receiver_model.tuning_frequency(), + 3072000U, + 1750000, + rf::Direction::Transmit, + rf_amp, + static_cast(portapack::receiver_model.lna()), + static_cast(portapack::receiver_model.vga())}); + + if (portapack::persistent_memory::stealth_mode()) { + DisplaySleepMessage message; + EventDispatcher::send_message(message); + } + + button_play.set_bitmap(&bitmap_stop); + + if (tx_mode == 0) { + tx_current_max_lines = input_image.get_height(); + tx_current_width = input_image.get_width(); + } else { + tx_current_max_lines = input_text.get_height(); + tx_current_width = input_text.get_width(); + } + + progressbar.set_max(tx_current_max_lines); + progressbar.set_value(0); + + baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, false, option_bandwidth.selected_index_value()); + } else { + stop_tx(); + } + }; + + option_bandwidth.on_change = [this](size_t, ui::OptionsField::value_t value) { + baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, true, value); + }; + + field_duration.set_value(10); + field_pause.set_value(5); } void SpectrumPainterView::start_tx() { - tx_current_line = 0; - tx_active = true; - tx_timestamp_start = chTimeNow(); + tx_current_line = 0; + tx_active = true; + tx_timestamp_start = chTimeNow(); } void SpectrumPainterView::stop_tx() { - button_play.set_bitmap(&bitmap_play); - portapack::transmitter_model.disable(); - tx_active = false; - tx_current_line = 0; + button_play.set_bitmap(&bitmap_play); + portapack::transmitter_model.disable(); + tx_active = false; + tx_current_line = 0; } void SpectrumPainterView::frame_sync() { - if (tx_active) { - if (fifo->is_empty()) { - - int32_t sequence_duration = (field_duration.value() + ( check_loop.value() ? field_pause.value() : 0)) * 1000; - int32_t sequence_time = tx_time_elapsed() % sequence_duration; - bool is_pausing = sequence_time > field_duration.value() * 1000; - - if (is_pausing) { - fifo->in(std::vector(tx_current_width)); - } else { - auto current_time_line = sequence_time * tx_current_max_lines / (field_duration.value() * 1000); - - if (tx_current_line > current_time_line && !check_loop.value()) { - fifo->in(std::vector(tx_current_width)); - stop_tx(); - return; - } - - tx_current_line = current_time_line; - progressbar.set_value(current_time_line); - - if (tx_mode == 0) { - std::vector line = input_image.get_line(current_time_line); - fifo->in(line); - } - else { - std::vector line = input_text.get_line(current_time_line); - fifo->in(line); - } - } - } - } + if (tx_active) { + if (fifo->is_empty()) { + int32_t sequence_duration = (field_duration.value() + (check_loop.value() ? field_pause.value() : 0)) * 1000; + int32_t sequence_time = tx_time_elapsed() % sequence_duration; + bool is_pausing = sequence_time > field_duration.value() * 1000; + + if (is_pausing) { + fifo->in(std::vector(tx_current_width)); + } else { + auto current_time_line = sequence_time * tx_current_max_lines / (field_duration.value() * 1000); + + if (tx_current_line > current_time_line && !check_loop.value()) { + fifo->in(std::vector(tx_current_width)); + stop_tx(); + return; + } + + tx_current_line = current_time_line; + progressbar.set_value(current_time_line); + + if (tx_mode == 0) { + std::vector line = input_image.get_line(current_time_line); + fifo->in(line); + } else { + std::vector line = input_text.get_line(current_time_line); + fifo->in(line); + } + } + } + } } SpectrumPainterView::~SpectrumPainterView() { - portapack::transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); - baseband::shutdown(); + portapack::transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); + baseband::shutdown(); } void SpectrumPainterView::focus() { - tab_view.focus(); + tab_view.focus(); } void SpectrumPainterView::on_target_frequency_changed(rf::Frequency f) { - set_target_frequency(f); + set_target_frequency(f); } void SpectrumPainterView::set_target_frequency(const rf::Frequency new_value) { - portapack::persistent_memory::set_tuned_frequency(new_value); + portapack::persistent_memory::set_tuned_frequency(new_value); } rf::Frequency SpectrumPainterView::target_frequency() const { - return portapack::persistent_memory::tuned_frequency(); + return portapack::persistent_memory::tuned_frequency(); } void SpectrumPainterView::paint(Painter& painter) { - View::paint(painter); - - size_t c; - Point pos = { 0, screen_pos().y() + 8 + footer_location }; - - for (c = 0; c < 20; c++) { - painter.draw_bitmap( - pos, - bitmap_stripes, - ui::Color(191, 191, 0), - ui::Color::black() - ); - if (c != 9) - pos += { 24, 0 }; - else - pos = { 0, screen_pos().y() + 8 + footer_location + 32 + 8 }; - } + View::paint(painter); + + size_t c; + Point pos = {0, screen_pos().y() + 8 + footer_location}; + + for (c = 0; c < 20; c++) { + painter.draw_bitmap( + pos, + bitmap_stripes, + ui::Color(191, 191, 0), + ui::Color::black()); + if (c != 9) + pos += {24, 0}; + else + pos = {0, screen_pos().y() + 8 + footer_location + 32 + 8}; + } } -} +} // namespace ui diff --git a/firmware/application/apps/ui_spectrum_painter.hpp b/firmware/application/apps/ui_spectrum_painter.hpp index 0ecf98653..1c7ebc7d5 100644 --- a/firmware/application/apps/ui_spectrum_painter.hpp +++ b/firmware/application/apps/ui_spectrum_painter.hpp @@ -38,140 +38,127 @@ namespace ui { class SpectrumPainterView : public View { -public: - SpectrumPainterView(NavigationView& nav); - ~SpectrumPainterView(); - - SpectrumPainterView(const SpectrumPainterView&) = delete; - SpectrumPainterView(SpectrumPainterView&&) = delete; - SpectrumPainterView& operator=(const SpectrumPainterView&) = delete; - SpectrumPainterView& operator=(SpectrumPainterView&&) = delete; - - void focus() override; - void paint(Painter& painter) override; - - std::string title() const override { return "Spec.Painter"; }; - -private: - void on_target_frequency_changed(rf::Frequency f); - void set_target_frequency(const rf::Frequency new_value); - rf::Frequency target_frequency() const; - - NavigationView& nav_; - bool image_input_avaliable { false }; - bool tx_active { false }; - uint32_t tx_mode { 0 }; - uint16_t tx_current_line { 0 }; - uint16_t tx_current_max_lines { 0 }; - uint16_t tx_current_width { 0 }; - systime_t tx_timestamp_start { 0 }; - - inline uint32_t tx_time_elapsed() { - auto now = chTimeNow(); - return now - tx_timestamp_start; - } - - int32_t tx_gain { 47 }; - bool rf_amp { false }; - - SpectrumInputImageView input_image { nav_ }; - SpectrumInputTextView input_text { nav_ }; - - std::array input_views { { &input_image, &input_text } }; - - TabView tab_view { - { "Image", Color::white(), input_views[0] }, - { "Text", Color::white(), input_views[1] } - }; - - static constexpr int32_t footer_location = 15 * 16 + 8; - ProgressBar progressbar { - { 4, footer_location - 16, 240-8, 16 } - }; - - Labels labels { - { { 10 * 8, footer_location + 1 * 16 }, "GAIN A:", Color::light_grey() }, - { { 1 * 8, footer_location + 2 * 16 }, "BW: Du: P:", Color::light_grey() }, - }; - - FrequencyField field_frequency { - { 0 * 8, footer_location + 1 * 16 }, - }; - - NumberField field_rfgain { - { 14 * 8, footer_location + 1 * 16 }, - 2, - { 0, 47 }, - 1, - ' ' - }; - - NumberField field_rfamp { - { 19 * 8, footer_location + 1 * 16 }, - 2, - { 0, 14 }, - 14, - ' ' - }; - - Checkbox check_loop { - { 21 * 8, footer_location + 1 * 16 }, - 4, - "Loop", - true - }; - - ImageButton button_play { - { 28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16 }, - &bitmap_play, - Color::green(), - Color::black() - }; - - OptionsField option_bandwidth { - { 4 * 8, footer_location + 2 * 16 }, - 5, - { - BW_OPTIONS - } - }; - - NumberField field_duration { - { 13 * 8, footer_location + 2 * 16 }, - 3, - { 1, 999 }, - 1, - ' ' - }; - - NumberField field_pause { - { 19 * 8, footer_location + 2 * 16 }, - 2, - { 0, 99 }, - 1, - ' ' - }; - - SpectrumPainterFIFO* fifo { nullptr }; - void start_tx(); - void frame_sync(); - void stop_tx(); - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::SpectrumPainterBufferResponseConfigure, - [this](const Message* const p) { - const auto message = static_cast(p); - this->fifo = message->fifo; - this->start_tx(); - } - }; - - MessageHandlerRegistration message_handler_sample { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->frame_sync(); - } - }; + public: + SpectrumPainterView(NavigationView& nav); + ~SpectrumPainterView(); + + SpectrumPainterView(const SpectrumPainterView&) = delete; + SpectrumPainterView(SpectrumPainterView&&) = delete; + SpectrumPainterView& operator=(const SpectrumPainterView&) = delete; + SpectrumPainterView& operator=(SpectrumPainterView&&) = delete; + + void focus() override; + void paint(Painter& painter) override; + + std::string title() const override { return "Spec.Painter"; }; + + private: + void on_target_frequency_changed(rf::Frequency f); + void set_target_frequency(const rf::Frequency new_value); + rf::Frequency target_frequency() const; + + NavigationView& nav_; + bool image_input_avaliable{false}; + bool tx_active{false}; + uint32_t tx_mode{0}; + uint16_t tx_current_line{0}; + uint16_t tx_current_max_lines{0}; + uint16_t tx_current_width{0}; + systime_t tx_timestamp_start{0}; + + inline uint32_t tx_time_elapsed() { + auto now = chTimeNow(); + return now - tx_timestamp_start; + } + + int32_t tx_gain{47}; + bool rf_amp{false}; + + SpectrumInputImageView input_image{nav_}; + SpectrumInputTextView input_text{nav_}; + + std::array input_views{{&input_image, &input_text}}; + + TabView tab_view{ + {"Image", Color::white(), input_views[0]}, + {"Text", Color::white(), input_views[1]}}; + + static constexpr int32_t footer_location = 15 * 16 + 8; + ProgressBar progressbar{ + {4, footer_location - 16, 240 - 8, 16}}; + + Labels labels{ + {{10 * 8, footer_location + 1 * 16}, "GAIN A:", Color::light_grey()}, + {{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Color::light_grey()}, + }; + + FrequencyField field_frequency{ + {0 * 8, footer_location + 1 * 16}, + }; + + NumberField field_rfgain{ + {14 * 8, footer_location + 1 * 16}, + 2, + {0, 47}, + 1, + ' '}; + + NumberField field_rfamp{ + {19 * 8, footer_location + 1 * 16}, + 2, + {0, 14}, + 14, + ' '}; + + Checkbox check_loop{ + {21 * 8, footer_location + 1 * 16}, + 4, + "Loop", + true}; + + ImageButton button_play{ + {28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16}, + &bitmap_play, + Color::green(), + Color::black()}; + + OptionsField option_bandwidth{ + {4 * 8, footer_location + 2 * 16}, + 5, + {BW_OPTIONS}}; + + NumberField field_duration{ + {13 * 8, footer_location + 2 * 16}, + 3, + {1, 999}, + 1, + ' '}; + + NumberField field_pause{ + {19 * 8, footer_location + 2 * 16}, + 2, + {0, 99}, + 1, + ' '}; + + SpectrumPainterFIFO* fifo{nullptr}; + void start_tx(); + void frame_sync(); + void stop_tx(); + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::SpectrumPainterBufferResponseConfigure, + [this](const Message* const p) { + const auto message = static_cast(p); + this->fifo = message->fifo; + this->start_tx(); + }}; + + MessageHandlerRegistration message_handler_sample{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->frame_sync(); + }}; }; -} +} // namespace ui diff --git a/firmware/application/apps/ui_spectrum_painter_image.cpp b/firmware/application/apps/ui_spectrum_painter_image.cpp index c514dee96..3b3574224 100644 --- a/firmware/application/apps/ui_spectrum_painter_image.cpp +++ b/firmware/application/apps/ui_spectrum_painter_image.cpp @@ -32,31 +32,28 @@ namespace ui { SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) { - hidden(true); - - add_children({ - &button_load_image - }); - - button_load_image.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".bmp"); - - constexpr auto data_directory = u"SPECTRUM"; - if (std::filesystem::is_directory(data_directory) == false) { - if (make_new_directory(data_directory).ok()) - open_view->push_dir(data_directory); - } - else - open_view->push_dir(data_directory); - - open_view->on_changed = [this](std::filesystem::path new_file_path) { - this->file = new_file_path.string(); - painted = false; - this->set_dirty(); - - this->on_input_avaliable(); - }; - }; + hidden(true); + + add_children({&button_load_image}); + + button_load_image.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".bmp"); + + constexpr auto data_directory = u"SPECTRUM"; + if (std::filesystem::is_directory(data_directory) == false) { + if (make_new_directory(data_directory).ok()) + open_view->push_dir(data_directory); + } else + open_view->push_dir(data_directory); + + open_view->on_changed = [this](std::filesystem::path new_file_path) { + this->file = new_file_path.string(); + painted = false; + this->set_dirty(); + + this->on_input_avaliable(); + }; + }; } SpectrumInputImageView::~SpectrumInputImageView() { @@ -67,199 +64,198 @@ void SpectrumInputImageView::focus() { } bool SpectrumInputImageView::drawBMP_scaled(const ui::Rect r, const std::string file) { - File bmpimage; - size_t file_pos = 0; - uint16_t pointer = 0; - int16_t px = 0, py, zoom_factor = 0; - bmp_header_t bmp_header; - char buffer[257]; - ui::Color line_buffer[240]; - - auto result = bmpimage.open(file); - if(result.is_valid()) - return false; - - bmpimage.seek(file_pos); - auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header)); - if (!((bmp_header.signature == 0x4D42) && // "BM" Signature - (bmp_header.planes == 1) && // Seems always to be 1 - (bmp_header.compression == 0 || bmp_header.compression == 3 ))) { // No compression - return false; - } - - switch(bmp_header.bpp) { - case 16: - file_pos = 0x36; - memset(buffer, 0, 16); - bmpimage.read(buffer, 16); - if(buffer[1] == 0x7C) - type = 3; // A1R5G5B5 - else - type = 0; // R5G6B5 - break; - case 24: - type = 1; - break; - case 32: - default: - type = 2; - break; - } - - width = bmp_header.width; - height = bmp_header.height; - - data_start = file_pos = bmp_header.image_data; - - while (r.width() < (width >> zoom_factor) || r.height() < (height >> zoom_factor)) { - zoom_factor++; - } - - py = height + 16; - - while(1) { - while(px < width) { - bmpimage.seek(file_pos); - memset(buffer, 0, 257); - read_size = bmpimage.read(buffer, 256); - if (read_size.is_error()) - return false; // Read error - - pointer = 0; - while(pointer < 256) { - if(pointer + 4 > 256) - break; - switch(type) { - case 0: // R5G6B5 - if ((((1 << zoom_factor) - 1) & px) == 0x00) - line_buffer[px >> zoom_factor] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); - - pointer += 2; - file_pos += 2; - break; - - case 3: // A1R5G5B5 - if ((((1 << zoom_factor) - 1) & px) == 0x00) - line_buffer[px >> zoom_factor] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); - - pointer += 2; - file_pos += 2; - break; - - case 1: // 24 - default: - if ((((1 << zoom_factor) - 1) & px) == 0x00) - line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - pointer += 3; - file_pos += 3; - break; - - case 2: // 32 - if ((((1 << zoom_factor) - 1) & px) == 0x00) - line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - pointer += 4; - file_pos += 4; - break; - } - - px++; - if(px >= width) { - break; - } - } - - if(read_size.value() != 256) - break; - } - - if ((((1 << zoom_factor) - 1) & py) == 0x00) - portapack::display.render_line({ r.left(), r.top() + (py >> zoom_factor) }, px >> zoom_factor, line_buffer); - - px = 0; - py--; - - if(read_size.value() < 256 || py < 0) - break; - } - - return true; + File bmpimage; + size_t file_pos = 0; + uint16_t pointer = 0; + int16_t px = 0, py, zoom_factor = 0; + bmp_header_t bmp_header; + char buffer[257]; + ui::Color line_buffer[240]; + + auto result = bmpimage.open(file); + if (result.is_valid()) + return false; + + bmpimage.seek(file_pos); + auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header)); + if (!((bmp_header.signature == 0x4D42) && // "BM" Signature + (bmp_header.planes == 1) && // Seems always to be 1 + (bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression + return false; + } + + switch (bmp_header.bpp) { + case 16: + file_pos = 0x36; + memset(buffer, 0, 16); + bmpimage.read(buffer, 16); + if (buffer[1] == 0x7C) + type = 3; // A1R5G5B5 + else + type = 0; // R5G6B5 + break; + case 24: + type = 1; + break; + case 32: + default: + type = 2; + break; + } + + width = bmp_header.width; + height = bmp_header.height; + + data_start = file_pos = bmp_header.image_data; + + while (r.width() < (width >> zoom_factor) || r.height() < (height >> zoom_factor)) { + zoom_factor++; + } + + py = height + 16; + + while (1) { + while (px < width) { + bmpimage.seek(file_pos); + memset(buffer, 0, 257); + read_size = bmpimage.read(buffer, 256); + if (read_size.is_error()) + return false; // Read error + + pointer = 0; + while (pointer < 256) { + if (pointer + 4 > 256) + break; + switch (type) { + case 0: // R5G6B5 + if ((((1 << zoom_factor) - 1) & px) == 0x00) + line_buffer[px >> zoom_factor] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); + + pointer += 2; + file_pos += 2; + break; + + case 3: // A1R5G5B5 + if ((((1 << zoom_factor) - 1) & px) == 0x00) + line_buffer[px >> zoom_factor] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); + + pointer += 2; + file_pos += 2; + break; + + case 1: // 24 + default: + if ((((1 << zoom_factor) - 1) & px) == 0x00) + line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + pointer += 3; + file_pos += 3; + break; + + case 2: // 32 + if ((((1 << zoom_factor) - 1) & px) == 0x00) + line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + pointer += 4; + file_pos += 4; + break; + } + + px++; + if (px >= width) { + break; + } + } + + if (read_size.value() != 256) + break; + } + + if ((((1 << zoom_factor) - 1) & py) == 0x00) + portapack::display.render_line({r.left(), r.top() + (py >> zoom_factor)}, px >> zoom_factor, line_buffer); + + px = 0; + py--; + + if (read_size.value() < 256 || py < 0) + break; + } + + return true; } -uint16_t SpectrumInputImageView::get_width(){ - return this->width; +uint16_t SpectrumInputImageView::get_width() { + return this->width; } -uint16_t SpectrumInputImageView::get_height(){ - return this->height; +uint16_t SpectrumInputImageView::get_height() { + return this->height; } std::vector SpectrumInputImageView::get_line(uint16_t y) { - File bmpimage; - bmpimage.open(this->file); - - //seek to line - uint32_t line_size = width * (type == 2 ? 4 : (type == 1 ? 3 : 2)); - uint32_t line_offset = y * line_size; - bmpimage.seek(data_start + line_offset); - - // allocate memory and read - auto buffer = new uint8_t[line_size]; - auto bytes_read = bmpimage.read(buffer, line_size); - - // greyscale - auto grey_buffer = new uint8_t[width]; - int pointer = 0; - for (uint16_t px = 0; px < width; px++) { - ui::Color color; - switch(type) { - case 0: // R5G6B5 - pointer = px * 2; - color = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); - break; - - case 3: // A1R5G5B5 - pointer = px * 2; - color = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); - break; - - case 1: // 24 - default: - pointer = px * 3; - color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - break; - - case 2: // 32 - pointer = px * 4; - color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - break; - } - - grey_buffer[px] = color.to_greyscale(); - } - - delete buffer; - - std::vector values(width); - for(int i = 0; i < width; i++) { - values[i] = grey_buffer[i]; - } - - delete grey_buffer; - - return values; + File bmpimage; + bmpimage.open(this->file); + + // seek to line + uint32_t line_size = width * (type == 2 ? 4 : (type == 1 ? 3 : 2)); + uint32_t line_offset = y * line_size; + bmpimage.seek(data_start + line_offset); + + // allocate memory and read + auto buffer = new uint8_t[line_size]; + auto bytes_read = bmpimage.read(buffer, line_size); + + // greyscale + auto grey_buffer = new uint8_t[width]; + int pointer = 0; + for (uint16_t px = 0; px < width; px++) { + ui::Color color; + switch (type) { + case 0: // R5G6B5 + pointer = px * 2; + color = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); + break; + + case 3: // A1R5G5B5 + pointer = px * 2; + color = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); + break; + + case 1: // 24 + default: + pointer = px * 3; + color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + break; + + case 2: // 32 + pointer = px * 4; + color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + break; + } + + grey_buffer[px] = color.to_greyscale(); + } + + delete buffer; + + std::vector values(width); + for (int i = 0; i < width; i++) { + values[i] = grey_buffer[i]; + } + + delete grey_buffer; + + return values; } void SpectrumInputImageView::paint(Painter& painter) { - painter.fill_rectangle( - {{0, 40}, {240, 204}}, - style().background - ); - - if (!painted) { - // This is very slow for big pictures. Do only once. - this->drawBMP_scaled({{ 0, 40 }, {240, 160}}, this->file); - painted = true; - } + painter.fill_rectangle( + {{0, 40}, {240, 204}}, + style().background); + + if (!painted) { + // This is very slow for big pictures. Do only once. + this->drawBMP_scaled({{0, 40}, {240, 160}}, this->file); + painted = true; + } } -} +} // namespace ui diff --git a/firmware/application/apps/ui_spectrum_painter_image.hpp b/firmware/application/apps/ui_spectrum_painter_image.hpp index 51f1f2764..42778efa3 100644 --- a/firmware/application/apps/ui_spectrum_painter_image.hpp +++ b/firmware/application/apps/ui_spectrum_painter_image.hpp @@ -35,33 +35,32 @@ namespace ui { class SpectrumInputImageView : public View { -public: - SpectrumInputImageView(NavigationView& nav); - ~SpectrumInputImageView(); + public: + SpectrumInputImageView(NavigationView& nav); + ~SpectrumInputImageView(); - void focus() override; - void paint(Painter&) override; + void focus() override; + void paint(Painter&) override; - uint16_t get_width(); - uint16_t get_height(); - std::vector get_line(uint16_t); + uint16_t get_width(); + uint16_t get_height(); + std::vector get_line(uint16_t); - std::function on_input_avaliable { }; + std::function on_input_avaliable{}; -private: - bool painted {false}; - std::string file {""}; - uint16_t width {0}; - uint16_t height {0}; - uint8_t type {0}; - uint32_t data_start {0}; - - Button button_load_image { - { 0 * 8, 11 * 16 - 4, 30 * 8, 28 }, - "Load Image ..." - }; - - bool drawBMP_scaled(const ui::Rect r, const std::string file); + private: + bool painted{false}; + std::string file{""}; + uint16_t width{0}; + uint16_t height{0}; + uint8_t type{0}; + uint32_t data_start{0}; + + Button button_load_image{ + {0 * 8, 11 * 16 - 4, 30 * 8, 28}, + "Load Image ..."}; + + bool drawBMP_scaled(const ui::Rect r, const std::string file); }; -} +} // namespace ui diff --git a/firmware/application/apps/ui_spectrum_painter_text.cpp b/firmware/application/apps/ui_spectrum_painter_text.cpp index f4a93cde8..10174d9a0 100644 --- a/firmware/application/apps/ui_spectrum_painter_text.cpp +++ b/firmware/application/apps/ui_spectrum_painter_text.cpp @@ -33,32 +33,30 @@ namespace ui { SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) { - hidden(true); - - add_children({ - &text_message_0, - &text_message_1, - &text_message_2, - &text_message_3, - &text_message_4, - &text_message_5, - &text_message_6, - &text_message_7, - &text_message_8, - &text_message_9, - &button_message - }); - - button_message.on_select = [this, &nav](Button&) { - this->on_set_text(nav); - }; + hidden(true); + + add_children({&text_message_0, + &text_message_1, + &text_message_2, + &text_message_3, + &text_message_4, + &text_message_5, + &text_message_6, + &text_message_7, + &text_message_8, + &text_message_9, + &button_message}); + + button_message.on_select = [this, &nav](Button&) { + this->on_set_text(nav); + }; } SpectrumInputTextView::~SpectrumInputTextView() { } void SpectrumInputTextView::on_set_text(NavigationView& nav) { - text_prompt(nav, buffer, 300); + text_prompt(nav, buffer, 300); } void SpectrumInputTextView::focus() { @@ -66,46 +64,45 @@ void SpectrumInputTextView::focus() { } void SpectrumInputTextView::paint(Painter& painter) { - message = buffer; - for (uint32_t i = 0 ; i < text_message.size(); i++) { - if (message.length() > i * 30) - text_message[i]->set(message.substr(i * 30, 30)); - else - text_message[i]->set(""); - } - - painter.fill_rectangle( - {{0, 40}, {240, 204}}, - style().background - ); + message = buffer; + for (uint32_t i = 0; i < text_message.size(); i++) { + if (message.length() > i * 30) + text_message[i]->set(message.substr(i * 30, 30)); + else + text_message[i]->set(""); + } + + painter.fill_rectangle( + {{0, 40}, {240, 204}}, + style().background); } constexpr uint32_t pixel_repeat = 32; -uint16_t SpectrumInputTextView::get_width(){ - return 16 * pixel_repeat; +uint16_t SpectrumInputTextView::get_width() { + return 16 * pixel_repeat; } -uint16_t SpectrumInputTextView::get_height(){ - return this->message.length() * 8; +uint16_t SpectrumInputTextView::get_height() { + return this->message.length() * 8; } std::vector SpectrumInputTextView::get_line(uint16_t y) { - auto character_position = y / 8; - auto character = this->message[character_position]; - auto glyph_data = ui::font::fixed_8x16.glyph(character).pixels(); - - auto line_in_character = y % 8; - std::vector data(16 * pixel_repeat); - - for (uint32_t index = 0; index < 16; index++) { - auto glyph_byte = index; - auto glyph_bit = line_in_character; - uint8_t glyph_pixel = (glyph_data[glyph_byte] & (1 << glyph_bit)) >> glyph_bit; - - for (uint32_t j = 0; j < pixel_repeat; j++) - data[index*pixel_repeat + j] = glyph_pixel * 255; - } - - return data; -} + auto character_position = y / 8; + auto character = this->message[character_position]; + auto glyph_data = ui::font::fixed_8x16.glyph(character).pixels(); + + auto line_in_character = y % 8; + std::vector data(16 * pixel_repeat); + + for (uint32_t index = 0; index < 16; index++) { + auto glyph_byte = index; + auto glyph_bit = line_in_character; + uint8_t glyph_pixel = (glyph_data[glyph_byte] & (1 << glyph_bit)) >> glyph_bit; + + for (uint32_t j = 0; j < pixel_repeat; j++) + data[index * pixel_repeat + j] = glyph_pixel * 255; + } + + return data; } +} // namespace ui diff --git a/firmware/application/apps/ui_spectrum_painter_text.hpp b/firmware/application/apps/ui_spectrum_painter_text.hpp index 41b236e56..d7086a165 100644 --- a/firmware/application/apps/ui_spectrum_painter_text.hpp +++ b/firmware/application/apps/ui_spectrum_painter_text.hpp @@ -35,90 +35,78 @@ namespace ui { class SpectrumInputTextView : public View { -public: - SpectrumInputTextView(NavigationView& nav); - ~SpectrumInputTextView(); - - void focus() override; - void paint(Painter&) override; - - uint16_t get_width(); - uint16_t get_height(); - std::vector get_line(uint16_t); - -private: - std::string buffer { "PORTAPACK" }; - std::string message { }; - void on_set_text(NavigationView& nav); - - Text text_message_0 { - { 0 * 8, 0 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_1 { - { 0 * 8, 1 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_2 { - { 0 * 8, 2 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_3 { - { 0 * 8, 3 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_4 { - { 0 * 8, 4 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_5 { - { 0 * 8, 5 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_6 { - { 0 * 8, 6 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_7 { - { 0 * 8, 7 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_8 { - { 0 * 8, 8 * 16, 30 * 8, 16 }, - "" - }; - - Text text_message_9 { - { 0 * 8, 9 * 16, 30 * 8, 16 }, - "" - }; - - std::array text_message { { - &text_message_0, - &text_message_1, - &text_message_2, - &text_message_3, - &text_message_4, - &text_message_5, - &text_message_6, - &text_message_7, - &text_message_8, - &text_message_9, - } }; - - Button button_message { - { 0 * 8, 11 * 16 - 4, 30 * 8, 28 }, - "Set message" - }; + public: + SpectrumInputTextView(NavigationView& nav); + ~SpectrumInputTextView(); + void focus() override; + void paint(Painter&) override; + + uint16_t get_width(); + uint16_t get_height(); + std::vector get_line(uint16_t); + + private: + std::string buffer{"PORTAPACK"}; + std::string message{}; + void on_set_text(NavigationView& nav); + + Text text_message_0{ + {0 * 8, 0 * 16, 30 * 8, 16}, + ""}; + + Text text_message_1{ + {0 * 8, 1 * 16, 30 * 8, 16}, + ""}; + + Text text_message_2{ + {0 * 8, 2 * 16, 30 * 8, 16}, + ""}; + + Text text_message_3{ + {0 * 8, 3 * 16, 30 * 8, 16}, + ""}; + + Text text_message_4{ + {0 * 8, 4 * 16, 30 * 8, 16}, + ""}; + + Text text_message_5{ + {0 * 8, 5 * 16, 30 * 8, 16}, + ""}; + + Text text_message_6{ + {0 * 8, 6 * 16, 30 * 8, 16}, + ""}; + + Text text_message_7{ + {0 * 8, 7 * 16, 30 * 8, 16}, + ""}; + + Text text_message_8{ + {0 * 8, 8 * 16, 30 * 8, 16}, + ""}; + + Text text_message_9{ + {0 * 8, 9 * 16, 30 * 8, 16}, + ""}; + + std::array text_message{{ + &text_message_0, + &text_message_1, + &text_message_2, + &text_message_3, + &text_message_4, + &text_message_5, + &text_message_6, + &text_message_7, + &text_message_8, + &text_message_9, + }}; + + Button button_message{ + {0 * 8, 11 * 16 - 4, 30 * 8, 28}, + "Set message"}; }; -} +} // namespace ui diff --git a/firmware/application/apps/ui_sstvtx.cpp b/firmware/application/apps/ui_sstvtx.cpp index 2bf603f3a..32f1133d0 100644 --- a/firmware/application/apps/ui_sstvtx.cpp +++ b/firmware/application/apps/ui_sstvtx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -35,277 +35,272 @@ using namespace portapack; namespace ui { void SSTVTXView::focus() { - if (file_error) - nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT, nullptr); - else - options_bitmaps.focus(); + if (file_error) + nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT, nullptr); + else + options_bitmaps.focus(); } -void SSTVTXView::read_boundary(uint8_t * const buffer, uint32_t position, uint32_t length) { - uint32_t to_read, split; - uint8_t * buffer_copy = buffer; - - bmp_file.seek(position); - - // FatFS bug workaround: we can't read across 512 byte boundaries ?! - while (length) { - to_read = (length <= 512) ? length : 512; - if ((position & 512) != ((position + to_read - 1) & 512)) { - // Crossing: do 2 reads before and after the boundary - split = 512 - (position & 511); - bmp_file.read(buffer_copy, split); - bmp_file.read(buffer_copy + split, to_read - split); - } else { - // No crossing, just read normally - bmp_file.read(buffer_copy, to_read); - } - position += to_read; - buffer_copy += to_read; - length -= to_read; - } +void SSTVTXView::read_boundary(uint8_t* const buffer, uint32_t position, uint32_t length) { + uint32_t to_read, split; + uint8_t* buffer_copy = buffer; + + bmp_file.seek(position); + + // FatFS bug workaround: we can't read across 512 byte boundaries ?! + while (length) { + to_read = (length <= 512) ? length : 512; + if ((position & 512) != ((position + to_read - 1) & 512)) { + // Crossing: do 2 reads before and after the boundary + split = 512 - (position & 511); + bmp_file.read(buffer_copy, split); + bmp_file.read(buffer_copy + split, to_read - split); + } else { + // No crossing, just read normally + bmp_file.read(buffer_copy, to_read); + } + position += to_read; + buffer_copy += to_read; + length -= to_read; + } } void SSTVTXView::paint(Painter&) { - ui::Color line_buffer[160]; - Coord line; - uint32_t data_idx, bmp_px, pixel_idx; - - data_idx = bmp_header.image_data; - - for (line = 0; line < (256 / 2); line++) { - - // Buffer a whole line - read_boundary(pixels_buffer, data_idx, sizeof(pixels_buffer)); - - for (bmp_px = 0; bmp_px < 160; bmp_px++) { - pixel_idx = bmp_px * 3 * 2; - line_buffer[bmp_px] = Color(pixels_buffer[pixel_idx + 2], - pixels_buffer[pixel_idx + 1], - pixels_buffer[pixel_idx + 0]); - } - portapack::display.render_line({ 16, 80 + 128 - line }, 160, line_buffer); - data_idx += sizeof(pixels_buffer) * 2; - } + ui::Color line_buffer[160]; + Coord line; + uint32_t data_idx, bmp_px, pixel_idx; + + data_idx = bmp_header.image_data; + + for (line = 0; line < (256 / 2); line++) { + // Buffer a whole line + read_boundary(pixels_buffer, data_idx, sizeof(pixels_buffer)); + + for (bmp_px = 0; bmp_px < 160; bmp_px++) { + pixel_idx = bmp_px * 3 * 2; + line_buffer[bmp_px] = Color(pixels_buffer[pixel_idx + 2], + pixels_buffer[pixel_idx + 1], + pixels_buffer[pixel_idx + 0]); + } + portapack::display.render_line({16, 80 + 128 - line}, 160, line_buffer); + data_idx += sizeof(pixels_buffer) * 2; + } } SSTVTXView::~SSTVTXView() { - // save app settings - app_settings.tx_frequency = transmitter_model.tuning_frequency(); - settings.save("tx_sstv", &app_settings); + // save app settings + app_settings.tx_frequency = transmitter_model.tuning_frequency(); + settings.save("tx_sstv", &app_settings); - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void SSTVTXView::on_tuning_frequency_changed(rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); + transmitter_model.set_tuning_frequency(f); } void SSTVTXView::prepare_scanline() { - sstv_scanline scanline_buffer; - uint32_t component, pixel_idx; - uint8_t offset; - - if (scanline_counter >= (256 * 3)) { - progressbar.set_value(0); - transmitter_model.disable(); - options_bitmaps.set_focusable(true); - tx_view.set_transmitting(false); - return; - } - - progressbar.set_value(scanline_counter); - - // Scottie 2 scanline: - // (First line: 1200 9ms) - // 1500 1.5ms - // Green - // 1500 1.5ms - // Blue - // 1200 9ms - // 1500 1.5ms - // Red - // Scanline time: 88.064ms (275.2us/pixel @ 320 pixels/line) - - component = scanline_counter % 3; - - if ((!scanline_counter && tx_sstv_mode->sync_on_first) || (component == tx_sstv_mode->sync_index)) { - // Sync - scanline_buffer.start_tone.frequency = SSTV_F2D(1200); - scanline_buffer.start_tone.duration = tx_sstv_mode->samples_per_sync; - scanline_buffer.gap_tone.frequency = SSTV_F2D(1500); - scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap; - } else { - // Regular scanline - scanline_buffer.start_tone.duration = 0; - if (tx_sstv_mode->gaps) { - scanline_buffer.gap_tone.frequency = SSTV_F2D(1500); - scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap; - } - } - - if (!component) { - // Read a new line - read_boundary(pixels_buffer, - bmp_header.image_data + ((255 - (scanline_counter / 3)) * sizeof(pixels_buffer)), - sizeof(pixels_buffer)); - } - - offset = component_map[component]; - for (uint32_t bmp_px = 0; bmp_px < 320; bmp_px++) { - pixel_idx = bmp_px * 3; - scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx + offset]; - } - - baseband::set_fifo_data((int8_t *)&scanline_buffer); - - scanline_counter++; + sstv_scanline scanline_buffer; + uint32_t component, pixel_idx; + uint8_t offset; + + if (scanline_counter >= (256 * 3)) { + progressbar.set_value(0); + transmitter_model.disable(); + options_bitmaps.set_focusable(true); + tx_view.set_transmitting(false); + return; + } + + progressbar.set_value(scanline_counter); + + // Scottie 2 scanline: + // (First line: 1200 9ms) + // 1500 1.5ms + // Green + // 1500 1.5ms + // Blue + // 1200 9ms + // 1500 1.5ms + // Red + // Scanline time: 88.064ms (275.2us/pixel @ 320 pixels/line) + + component = scanline_counter % 3; + + if ((!scanline_counter && tx_sstv_mode->sync_on_first) || (component == tx_sstv_mode->sync_index)) { + // Sync + scanline_buffer.start_tone.frequency = SSTV_F2D(1200); + scanline_buffer.start_tone.duration = tx_sstv_mode->samples_per_sync; + scanline_buffer.gap_tone.frequency = SSTV_F2D(1500); + scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap; + } else { + // Regular scanline + scanline_buffer.start_tone.duration = 0; + if (tx_sstv_mode->gaps) { + scanline_buffer.gap_tone.frequency = SSTV_F2D(1500); + scanline_buffer.gap_tone.duration = tx_sstv_mode->samples_per_gap; + } + } + + if (!component) { + // Read a new line + read_boundary(pixels_buffer, + bmp_header.image_data + ((255 - (scanline_counter / 3)) * sizeof(pixels_buffer)), + sizeof(pixels_buffer)); + } + + offset = component_map[component]; + for (uint32_t bmp_px = 0; bmp_px < 320; bmp_px++) { + pixel_idx = bmp_px * 3; + scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx + offset]; + } + + baseband::set_fifo_data((int8_t*)&scanline_buffer); + + scanline_counter++; } void SSTVTXView::start_tx() { - // The baseband SSTV TX code (proc_sstv) has a 2-scanline buffer. It is preloaded before - // TX start, and asks for fill-up when a new scanline starts being read. This should - // leave enough time for the code in prepare_scanline() before it ends. - - scanline_counter = 0; - prepare_scanline(); // Preload one scanline - - transmitter_model.set_sampling_rate(3072000U); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_sstv_data( - tx_sstv_mode->vis_code, - tx_sstv_mode->samples_per_pixel - ); - - // Todo: Find a better way to prevent user from changing bitmap during tx - options_bitmaps.set_focusable(false); - tx_view.focus(); + // The baseband SSTV TX code (proc_sstv) has a 2-scanline buffer. It is preloaded before + // TX start, and asks for fill-up when a new scanline starts being read. This should + // leave enough time for the code in prepare_scanline() before it ends. + + scanline_counter = 0; + prepare_scanline(); // Preload one scanline + + transmitter_model.set_sampling_rate(3072000U); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_sstv_data( + tx_sstv_mode->vis_code, + tx_sstv_mode->samples_per_pixel); + + // Todo: Find a better way to prevent user from changing bitmap during tx + options_bitmaps.set_focusable(false); + tx_view.focus(); } void SSTVTXView::on_bitmap_changed(const size_t index) { - bmp_file.open("/sstv/" + bitmaps[index].string()); - bmp_file.read(&bmp_header, sizeof(bmp_header)); - set_dirty(); + bmp_file.open("/sstv/" + bitmaps[index].string()); + bmp_file.read(&bmp_header, sizeof(bmp_header)); + set_dirty(); } void SSTVTXView::on_mode_changed(const size_t index) { - sstv_color_seq tx_color_sequence; - - tx_sstv_mode = &sstv_modes[index]; - - tx_color_sequence = sstv_modes[index].color_sequence; - if (tx_color_sequence == SSTV_COLOR_RGB) { - component_map[0] = 2; - component_map[1] = 1; - component_map[2] = 0; - } else if (tx_color_sequence == SSTV_COLOR_GBR) { - component_map[0] = 1; - component_map[1] = 0; - component_map[2] = 2; - } - - progressbar.set_max(sstv_modes[index].lines * 3); + sstv_color_seq tx_color_sequence; + + tx_sstv_mode = &sstv_modes[index]; + + tx_color_sequence = sstv_modes[index].color_sequence; + if (tx_color_sequence == SSTV_COLOR_RGB) { + component_map[0] = 2; + component_map[1] = 1; + component_map[2] = 0; + } else if (tx_color_sequence == SSTV_COLOR_GBR) { + component_map[0] = 1; + component_map[1] = 0; + component_map[2] = 2; + } + + progressbar.set_max(sstv_modes[index].lines * 3); } SSTVTXView::SSTVTXView( - NavigationView& nav -) : nav_ (nav) -{ - std::vector file_list; - using option_t = std::pair; - using options_t = std::vector; - options_t bitmap_options; - options_t mode_options; - uint32_t c; - - // load app settings - auto rc = settings.load("tx_sstv", &app_settings); - if(rc == SETTINGS_OK) { - transmitter_model.set_rf_amp(app_settings.tx_amp); - transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); - transmitter_model.set_tuning_frequency(app_settings.tx_frequency); - transmitter_model.set_tx_gain(app_settings.tx_gain); - } - - // Search for valid bitmaps - file_list = scan_root_files(u"/sstv", u"*.bmp"); - if (!file_list.size()) { - file_error = true; - return; - } - for (const auto& file_name : file_list) { - if (!bmp_file.open("/sstv/" + file_name.string()).is_valid()) { - bmp_file.read(&bmp_header, sizeof(bmp_header)); - if ((bmp_header.signature == 0x4D42) && // "BM" - (bmp_header.width == 320) && // Must be exactly 320x256 pixels for now - (bmp_header.height == 256) && - (bmp_header.planes == 1) && - (bmp_header.bpp == 24) && // 24 bpp only - (bmp_header.compression == 0)) { // No compression - bitmaps.push_back(file_name); - } - } - } - if (!bitmaps.size()) { - file_error = true; - return; - } - - // Maybe this could be merged with proc_tones ? Pretty much the same except lots - // of different tones (256+) - baseband::run_image(portapack::spi_flash::image_tag_sstv_tx); - - add_children({ - &labels, - &options_bitmaps, - &options_modes, - &progressbar, - &tx_view - }); - - // Populate file list - for (const auto& bitmap : bitmaps) - bitmap_options.emplace_back(bitmap.string().substr(0, 16), 0); - options_bitmaps.set_options(bitmap_options); - - // Populate mode list - for (c = 0; c < SSTV_MODES_NB; c++) - mode_options.emplace_back(sstv_modes[c].name, c); - options_modes.set_options(mode_options); - - options_bitmaps.on_change = [this](size_t i, int32_t) { - this->on_bitmap_changed(i); - }; - options_bitmaps.set_selected_index(0); // First file - on_bitmap_changed(0); - - options_modes.on_change = [this](size_t i, int32_t) { - this->on_mode_changed(i); - }; - options_modes.set_selected_index(1); // Scottie 2 - on_mode_changed(1); - - tx_view.on_edit_frequency = [this, &nav]() { - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - receiver_model.set_tuning_frequency(f); - }; - }; - - tx_view.on_start = [this]() { - start_tx(); - tx_view.set_transmitting(true); - }; - - tx_view.on_stop = [this]() { - baseband::set_sstv_data(0, 0); - tx_view.set_transmitting(false); - transmitter_model.disable(); - options_bitmaps.set_focusable(true); - }; + NavigationView& nav) + : nav_(nav) { + std::vector file_list; + using option_t = std::pair; + using options_t = std::vector; + options_t bitmap_options; + options_t mode_options; + uint32_t c; + + // load app settings + auto rc = settings.load("tx_sstv", &app_settings); + if (rc == SETTINGS_OK) { + transmitter_model.set_rf_amp(app_settings.tx_amp); + transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth); + transmitter_model.set_tuning_frequency(app_settings.tx_frequency); + transmitter_model.set_tx_gain(app_settings.tx_gain); + } + + // Search for valid bitmaps + file_list = scan_root_files(u"/sstv", u"*.bmp"); + if (!file_list.size()) { + file_error = true; + return; + } + for (const auto& file_name : file_list) { + if (!bmp_file.open("/sstv/" + file_name.string()).is_valid()) { + bmp_file.read(&bmp_header, sizeof(bmp_header)); + if ((bmp_header.signature == 0x4D42) && // "BM" + (bmp_header.width == 320) && // Must be exactly 320x256 pixels for now + (bmp_header.height == 256) && + (bmp_header.planes == 1) && + (bmp_header.bpp == 24) && // 24 bpp only + (bmp_header.compression == 0)) { // No compression + bitmaps.push_back(file_name); + } + } + } + if (!bitmaps.size()) { + file_error = true; + return; + } + + // Maybe this could be merged with proc_tones ? Pretty much the same except lots + // of different tones (256+) + baseband::run_image(portapack::spi_flash::image_tag_sstv_tx); + + add_children({&labels, + &options_bitmaps, + &options_modes, + &progressbar, + &tx_view}); + + // Populate file list + for (const auto& bitmap : bitmaps) + bitmap_options.emplace_back(bitmap.string().substr(0, 16), 0); + options_bitmaps.set_options(bitmap_options); + + // Populate mode list + for (c = 0; c < SSTV_MODES_NB; c++) + mode_options.emplace_back(sstv_modes[c].name, c); + options_modes.set_options(mode_options); + + options_bitmaps.on_change = [this](size_t i, int32_t) { + this->on_bitmap_changed(i); + }; + options_bitmaps.set_selected_index(0); // First file + on_bitmap_changed(0); + + options_modes.on_change = [this](size_t i, int32_t) { + this->on_mode_changed(i); + }; + options_modes.set_selected_index(1); // Scottie 2 + on_mode_changed(1); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + receiver_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + baseband::set_sstv_data(0, 0); + tx_view.set_transmitting(false); + transmitter_model.disable(); + options_bitmaps.set_focusable(true); + }; } } /* namespace ui */ diff --git a/firmware/application/apps/ui_sstvtx.hpp b/firmware/application/apps/ui_sstvtx.hpp index f54a8131f..e59bea7bb 100644 --- a/firmware/application/apps/ui_sstvtx.hpp +++ b/firmware/application/apps/ui_sstvtx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -41,82 +41,76 @@ using namespace sstv; namespace ui { class SSTVTXView : public View { -public: - SSTVTXView(NavigationView& nav); - ~SSTVTXView(); - - SSTVTXView(const SSTVTXView&) = delete; - SSTVTXView(SSTVTXView&&) = delete; - SSTVTXView& operator=(const SSTVTXView&) = delete; - SSTVTXView& operator=(SSTVTXView&&) = delete; - - void focus() override; - void paint(Painter&) override; - - std::string title() const override { return "SSTV TX"; }; - -private: - NavigationView& nav_; - - sstv_scanline scanline_buffer { }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - - bool file_error { false }; - File bmp_file { }; - bmp_header_t bmp_header { }; - std::vector bitmaps { }; - uint32_t scanline_counter { 0 }; - uint8_t pixels_buffer[320 * 3]; // 320 pixels @ 24bpp - const sstv_mode * tx_sstv_mode { }; - - uint8_t component_map[3] { }; - - void read_boundary(uint8_t * buffer, uint32_t position, uint32_t length); - void on_bitmap_changed(const size_t index); - void on_mode_changed(const size_t index); - void on_tuning_frequency_changed(rf::Frequency f); - void start_tx(); - void prepare_scanline(); - - Labels labels { - { { 1 * 8, 1 * 8 }, "File:", Color::light_grey() }, - { { 1 * 8, 3 * 8 }, "Mode:", Color::light_grey() } - }; - - OptionsField options_bitmaps { - { 6 * 8, 1 * 8 }, - 16, - { } - }; - OptionsField options_modes { - { 6 * 8, 3 * 8 }, - 16, - { } - }; - - ProgressBar progressbar { - { 16, 25 * 8, 208, 16 } - }; - - TransmitterView tx_view { - 16 * 16, - 10000, - 12 - }; - - MessageHandlerRegistration message_handler_fifo_signal { - Message::ID::RequestSignal, - [this](const Message* const p) { - const auto message = static_cast(p); - if (message->signal == RequestSignalMessage::Signal::FillRequest) { - this->prepare_scanline(); - } - } - }; + public: + SSTVTXView(NavigationView& nav); + ~SSTVTXView(); + + SSTVTXView(const SSTVTXView&) = delete; + SSTVTXView(SSTVTXView&&) = delete; + SSTVTXView& operator=(const SSTVTXView&) = delete; + SSTVTXView& operator=(SSTVTXView&&) = delete; + + void focus() override; + void paint(Painter&) override; + + std::string title() const override { return "SSTV TX"; }; + + private: + NavigationView& nav_; + + sstv_scanline scanline_buffer{}; + // app save settings + std::app_settings settings{}; + std::app_settings::AppSettings app_settings{}; + + bool file_error{false}; + File bmp_file{}; + bmp_header_t bmp_header{}; + std::vector bitmaps{}; + uint32_t scanline_counter{0}; + uint8_t pixels_buffer[320 * 3]; // 320 pixels @ 24bpp + const sstv_mode* tx_sstv_mode{}; + + uint8_t component_map[3]{}; + + void read_boundary(uint8_t* buffer, uint32_t position, uint32_t length); + void on_bitmap_changed(const size_t index); + void on_mode_changed(const size_t index); + void on_tuning_frequency_changed(rf::Frequency f); + void start_tx(); + void prepare_scanline(); + + Labels labels{ + {{1 * 8, 1 * 8}, "File:", Color::light_grey()}, + {{1 * 8, 3 * 8}, "Mode:", Color::light_grey()}}; + + OptionsField options_bitmaps{ + {6 * 8, 1 * 8}, + 16, + {}}; + OptionsField options_modes{ + {6 * 8, 3 * 8}, + 16, + {}}; + + ProgressBar progressbar{ + {16, 25 * 8, 208, 16}}; + + TransmitterView tx_view{ + 16 * 16, + 10000, + 12}; + + MessageHandlerRegistration message_handler_fifo_signal{ + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->prepare_scanline(); + } + }}; }; } /* namespace ui */ -#endif/*__UI_SSTVTX_H__*/ +#endif /*__UI_SSTVTX_H__*/ diff --git a/firmware/application/apps/ui_test.cpp b/firmware/application/apps/ui_test.cpp index a5e816159..96d84372d 100644 --- a/firmware/application/apps/ui_test.cpp +++ b/firmware/application/apps/ui_test.cpp @@ -29,123 +29,121 @@ using namespace portapack; #include "string_format.hpp" void TestLogger::log_raw_data(const testapp::Packet& packet, const int32_t alt) { - std::string entry = to_string_dec_uint(packet.value()) + " " + to_string_dec_int(alt); - - // Raw hex dump - //for (size_t c = 0; c < 10; c++) - // entry += to_string_hex(packet[c], 8) + " "; - - log_file.write_entry(packet.received_at(), entry); + std::string entry = to_string_dec_uint(packet.value()) + " " + to_string_dec_int(alt); + + // Raw hex dump + // for (size_t c = 0; c < 10; c++) + // entry += to_string_hex(packet[c], 8) + " "; + + log_file.write_entry(packet.received_at(), entry); } namespace ui { TestView::TestView(NavigationView& nav) { - baseband::run_image(portapack::spi_flash::image_tag_test); - - add_children({ - &labels, - &field_frequency, - &field_rf_amp, - &field_lna, - &field_vga, - &rssi, - &text_debug_a, - &text_debug_b, - &button_cal, - &check_log - }); - - field_frequency.set_value(target_frequency_); - field_frequency.set_step(10000); - field_frequency.on_change = [this](rf::Frequency f) { - set_target_frequency(f); - field_frequency.set_value(f); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(receiver_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - set_target_frequency(f); - field_frequency.set_value(f); - }; - }; - - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - }; - - button_cal.on_select = [this](Button&) { - cal_value = raw_alt - 0x80; - }; - - logger = std::make_unique(); - if (logger) - logger->append("saucepan.txt"); - - radio::enable({ - tuning_frequency(), - sampling_rate, - baseband_bandwidth, - rf::Direction::Receive, - receiver_model.rf_amp(), - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()), - }); + baseband::run_image(portapack::spi_flash::image_tag_test); + + add_children({&labels, + &field_frequency, + &field_rf_amp, + &field_lna, + &field_vga, + &rssi, + &text_debug_a, + &text_debug_b, + &button_cal, + &check_log}); + + field_frequency.set_value(target_frequency_); + field_frequency.set_step(10000); + field_frequency.on_change = [this](rf::Frequency f) { + set_target_frequency(f); + field_frequency.set_value(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + set_target_frequency(f); + field_frequency.set_value(f); + }; + }; + + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + button_cal.on_select = [this](Button&) { + cal_value = raw_alt - 0x80; + }; + + logger = std::make_unique(); + if (logger) + logger->append("saucepan.txt"); + + radio::enable({ + tuning_frequency(), + sampling_rate, + baseband_bandwidth, + rf::Direction::Receive, + receiver_model.rf_amp(), + static_cast(receiver_model.lna()), + static_cast(receiver_model.vga()), + }); } TestView::~TestView() { - radio::disable(); - baseband::shutdown(); + radio::disable(); + baseband::shutdown(); } void TestView::focus() { - field_vga.focus(); + field_vga.focus(); } void TestView::on_packet(const testapp::Packet& packet) { - const auto hex_formatted = packet.symbols_formatted(); - auto v = packet.value(); - - packet_count++; - uint32_t diff = ((v - 1) - prev_v); - if (diff < 50) - packets_lost += diff; - prev_v = v; - - text_debug_a.set(hex_formatted.data.substr(0, 30)); - - text_debug_b.set(to_string_dec_uint((packets_lost * 1000) / packet_count) + " per 1000"); - - raw_alt = packet.alt(); - display.draw_pixel(Point(cur_x, 4 * 16 + (256 - ((raw_alt - cal_value) / 4))), Color::white()); - - cur_x++; - if (cur_x >= 240) { - display.fill_rectangle(Rect(0, 5 * 16, 240, 256), Color::black()); - cur_x = 0; - } - - if (logger && logging) - logger->log_raw_data(packet, raw_alt - cal_value); - - //radio::disable(); - - /*text_serial.set(packet.serial_number()); - text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V"); - - altitude = packet.GPS_altitude(); - latitude = packet.GPS_latitude(); - longitude = packet.GPS_longitude();*/ + const auto hex_formatted = packet.symbols_formatted(); + auto v = packet.value(); + + packet_count++; + uint32_t diff = ((v - 1) - prev_v); + if (diff < 50) + packets_lost += diff; + prev_v = v; + + text_debug_a.set(hex_formatted.data.substr(0, 30)); + + text_debug_b.set(to_string_dec_uint((packets_lost * 1000) / packet_count) + " per 1000"); + + raw_alt = packet.alt(); + display.draw_pixel(Point(cur_x, 4 * 16 + (256 - ((raw_alt - cal_value) / 4))), Color::white()); + + cur_x++; + if (cur_x >= 240) { + display.fill_rectangle(Rect(0, 5 * 16, 240, 256), Color::black()); + cur_x = 0; + } + + if (logger && logging) + logger->log_raw_data(packet, raw_alt - cal_value); + + // radio::disable(); + + /*text_serial.set(packet.serial_number()); + text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V"); + + altitude = packet.GPS_altitude(); + latitude = packet.GPS_latitude(); + longitude = packet.GPS_longitude();*/ } void TestView::set_target_frequency(const uint32_t new_value) { - target_frequency_ = new_value; - radio::set_tuning_frequency(tuning_frequency()); + target_frequency_ = new_value; + radio::set_tuning_frequency(tuning_frequency()); } uint32_t TestView::tuning_frequency() const { - return target_frequency_ - (sampling_rate / 4); + return target_frequency_ - (sampling_rate / 4); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_test.hpp b/firmware/application/apps/ui_test.hpp index d99ed1090..bd70bb8f0 100644 --- a/firmware/application/apps/ui_test.hpp +++ b/firmware/application/apps/ui_test.hpp @@ -36,99 +36,90 @@ #include class TestLogger { -public: - Optional append(const std::string& filename) { - return log_file.append(filename); - } - - void log_raw_data(const testapp::Packet& packet, const int32_t alt); - -private: - LogFile log_file { }; + public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const testapp::Packet& packet, const int32_t alt); + + private: + LogFile log_file{}; }; namespace ui { class TestView : public View { -public: - static constexpr uint32_t sampling_rate = 2457600*2; - static constexpr uint32_t baseband_bandwidth = 1750000; - - TestView(NavigationView& nav); - ~TestView(); - - void focus() override; - - std::string title() const override { return "Test app"; }; - -private: - uint32_t target_frequency_ { 439206000 }; - Coord cur_x { 0 }; - uint32_t packet_count { 0 }; - uint32_t packets_lost { 0 }; - uint32_t prev_v { 0 }; - uint32_t raw_alt { 0 }; - uint32_t cal_value { 0 }; - bool logging { false }; - - Labels labels { - { { 0 * 8, 1 * 16 }, "Data:", Color::light_grey() } - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 8 }, - }; - RFAmpField field_rf_amp { - { 13 * 8, 0 * 16 } - }; - - LNAGainField field_lna { - { 15 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 18 * 8, 0 * 16 } - }; - - RSSI rssi { - { 21 * 8, 0, 6 * 8, 4 }, - }; - - Text text_debug_a { - { 0 * 8, 4 * 16, 30 * 8, 16 }, - "..." - }; - Text text_debug_b { - { 0 * 8, 5 * 16, 30 * 8, 16 }, - "..." - }; - - Button button_cal { - { 17 * 8, 2 * 16, 5 * 8, 2 * 16 }, - "CAL" - }; - Checkbox check_log { - { 23 * 8, 2 * 16 }, - 3, - "LOG" - }; - - std::unique_ptr logger { }; - - MessageHandlerRegistration message_handler_packet { - Message::ID::TestAppPacket, - [this](Message* const p) { - const auto message = static_cast(p); - const testapp::Packet packet { message->packet }; - this->on_packet(packet); - } - }; - - void on_packet(const testapp::Packet& packet); - void set_target_frequency(const uint32_t new_value); - uint32_t tuning_frequency() const; + public: + static constexpr uint32_t sampling_rate = 2457600 * 2; + static constexpr uint32_t baseband_bandwidth = 1750000; + + TestView(NavigationView& nav); + ~TestView(); + + void focus() override; + + std::string title() const override { return "Test app"; }; + + private: + uint32_t target_frequency_{439206000}; + Coord cur_x{0}; + uint32_t packet_count{0}; + uint32_t packets_lost{0}; + uint32_t prev_v{0}; + uint32_t raw_alt{0}; + uint32_t cal_value{0}; + bool logging{false}; + + Labels labels{ + {{0 * 8, 1 * 16}, "Data:", Color::light_grey()}}; + + FrequencyField field_frequency{ + {0 * 8, 0 * 8}, + }; + RFAmpField field_rf_amp{ + {13 * 8, 0 * 16}}; + + LNAGainField field_lna{ + {15 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {18 * 8, 0 * 16}}; + + RSSI rssi{ + {21 * 8, 0, 6 * 8, 4}, + }; + + Text text_debug_a{ + {0 * 8, 4 * 16, 30 * 8, 16}, + "..."}; + Text text_debug_b{ + {0 * 8, 5 * 16, 30 * 8, 16}, + "..."}; + + Button button_cal{ + {17 * 8, 2 * 16, 5 * 8, 2 * 16}, + "CAL"}; + Checkbox check_log{ + {23 * 8, 2 * 16}, + 3, + "LOG"}; + + std::unique_ptr logger{}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::TestAppPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const testapp::Packet packet{message->packet}; + this->on_packet(packet); + }}; + + void on_packet(const testapp::Packet& packet); + void set_target_frequency(const uint32_t new_value); + uint32_t tuning_frequency() const; }; } /* namespace ui */ -#endif/*__UI_TEST_H__*/ +#endif /*__UI_TEST_H__*/ diff --git a/firmware/application/apps/ui_tone_search.cpp b/firmware/application/apps/ui_tone_search.cpp index 0f190d552..e17ed52b6 100644 --- a/firmware/application/apps/ui_tone_search.cpp +++ b/firmware/application/apps/ui_tone_search.cpp @@ -30,26 +30,25 @@ using namespace portapack; namespace ui { void ToneSearchView::focus() { - //field_frequency_min.focus(); + // field_frequency_min.focus(); } ToneSearchView::~ToneSearchView() { - receiver_model.disable(); - baseband::shutdown(); + receiver_model.disable(); + baseband::shutdown(); } ToneSearchView::ToneSearchView( - NavigationView& nav -) : nav_ (nav) -{ - //baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); - - add_children({ - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - }); + NavigationView& nav) + : nav_(nav) { + // baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); + + add_children({ + &labels, + &field_lna, + &field_vga, + &field_rf_amp, + }); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_tone_search.hpp b/firmware/application/apps/ui_tone_search.hpp index 1d121138e..523d57c1c 100644 --- a/firmware/application/apps/ui_tone_search.hpp +++ b/firmware/application/apps/ui_tone_search.hpp @@ -28,46 +28,42 @@ namespace ui { class ToneSearchView : public View { -public: - ToneSearchView(NavigationView& nav); - ~ToneSearchView(); - - void focus() override; - - std::string title() const override { return "Tone search"; }; + public: + ToneSearchView(NavigationView& nav); + ~ToneSearchView(); -private: - NavigationView& nav_; - - Labels labels { - { { 0 * 8, 0 * 8 }, "LNA: VGA: AMP:", Color::light_grey() } - }; - - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; - - /* - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->fifo ) { - ChannelSpectrum channel_spectrum; - while( fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - this->do_timers(); - } - };*/ + void focus() override; + + std::string title() const override { return "Tone search"; }; + + private: + NavigationView& nav_; + + Labels labels{ + {{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Color::light_grey()}}; + + LNAGainField field_lna{ + {4 * 8, 0 * 16}}; + + VGAGainField field_vga{ + {11 * 8, 0 * 16}}; + + RFAmpField field_rf_amp{ + {18 * 8, 0 * 16}}; + + /* + MessageHandlerRegistration message_handler_frame_sync { + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if( this->fifo ) { + ChannelSpectrum channel_spectrum; + while( fifo->out(channel_spectrum) ) { + this->on_channel_spectrum(channel_spectrum); + } + } + this->do_timers(); + } + };*/ }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_touch_calibration.cpp b/firmware/application/apps/ui_touch_calibration.cpp index 26960106f..fa91e07c5 100644 --- a/firmware/application/apps/ui_touch_calibration.cpp +++ b/firmware/application/apps/ui_touch_calibration.cpp @@ -29,178 +29,177 @@ using namespace portapack; namespace ui { TouchCalibrationView::TouchCalibrationView( - NavigationView& nav -) : nav { nav }, - calibration { touch::Calibration() } -{ - add_children({ - &image_calibrate_0, - &image_calibrate_1, - &image_calibrate_2, - &image_verify_0, - &image_verify_1, - &image_verify_2, - &label_calibrate, - &label_verify, - &label_success, - &label_failure, - &button_cancel, - &button_ok, - }); - - button_cancel.on_select = [this](Button&){ this->on_cancel(); }; - button_ok.on_select = [this](Button&){ this->on_ok(); }; - - set_phase(Phase::Calibrate0); + NavigationView& nav) + : nav{nav}, + calibration{touch::Calibration()} { + add_children({ + &image_calibrate_0, + &image_calibrate_1, + &image_calibrate_2, + &image_verify_0, + &image_verify_1, + &image_verify_2, + &label_calibrate, + &label_verify, + &label_success, + &label_failure, + &button_cancel, + &button_ok, + }); + + button_cancel.on_select = [this](Button&) { this->on_cancel(); }; + button_ok.on_select = [this](Button&) { this->on_ok(); }; + + set_phase(Phase::Calibrate0); } void TouchCalibrationView::focus() { - button_cancel.focus(); + button_cancel.focus(); } void TouchCalibrationView::update_target() { - const auto phase_calibrate = (phase == Phase::Calibrate0) || (phase == Phase::Calibrate1) || (phase == Phase::Calibrate2); - const auto phase_verify = (phase == Phase::Verify0) || (phase == Phase::Verify1) || (phase == Phase::Verify2); + const auto phase_calibrate = (phase == Phase::Calibrate0) || (phase == Phase::Calibrate1) || (phase == Phase::Calibrate2); + const auto phase_verify = (phase == Phase::Verify0) || (phase == Phase::Verify1) || (phase == Phase::Verify2); - image_calibrate_0.hidden(phase != Phase::Calibrate0); - image_calibrate_1.hidden(phase != Phase::Calibrate1); - image_calibrate_2.hidden(phase != Phase::Calibrate2); + image_calibrate_0.hidden(phase != Phase::Calibrate0); + image_calibrate_1.hidden(phase != Phase::Calibrate1); + image_calibrate_2.hidden(phase != Phase::Calibrate2); - image_verify_0.hidden(phase != Phase::Verify0); - image_verify_1.hidden(phase != Phase::Verify1); - image_verify_2.hidden(phase != Phase::Verify2); + image_verify_0.hidden(phase != Phase::Verify0); + image_verify_1.hidden(phase != Phase::Verify1); + image_verify_2.hidden(phase != Phase::Verify2); - label_calibrate.hidden(!phase_calibrate); - label_verify.hidden(!phase_verify); - label_success.hidden(phase != Phase::Success); - label_failure.hidden(phase != Phase::Failure); + label_calibrate.hidden(!phase_calibrate); + label_verify.hidden(!phase_verify); + label_success.hidden(phase != Phase::Success); + label_failure.hidden(phase != Phase::Failure); - button_ok.hidden((phase != Phase::Success) && (phase != Phase::Failure)); + button_ok.hidden((phase != Phase::Success) && (phase != Phase::Failure)); - /* TODO: Such a hack to get around a poor repaint implementation! This "technique" - * occurs in other places... - */ - set_dirty(); + /* TODO: Such a hack to get around a poor repaint implementation! This "technique" + * occurs in other places... + */ + set_dirty(); } void TouchCalibrationView::set_phase(const Phase value) { - if( value != phase ) { - phase = value; - update_target(); - } + if (value != phase) { + phase = value; + update_target(); + } } uint32_t TouchCalibrationView::distance_squared(const Point& touch_point, const Image& target) { - const auto target_point = target.screen_rect().center(); - const int32_t dx = target_point.x() - touch_point.x(); - const int32_t dy = target_point.y() - touch_point.y(); - const uint32_t dx2 = dx * dx; - const uint32_t dy2 = dy * dy; - return dx2 + dy2; + const auto target_point = target.screen_rect().center(); + const int32_t dx = target_point.x() - touch_point.x(); + const int32_t dy = target_point.y() - touch_point.y(); + const uint32_t dx2 = dx * dx; + const uint32_t dy2 = dy * dy; + return dx2 + dy2; } void TouchCalibrationView::touch_complete() { - auto next_phase = static_cast(toUType(phase) + 1); - - switch(phase) { - case Phase::Calibrate0: - case Phase::Verify0: - digitizer_points[0] = average; - break; - - case Phase::Calibrate1: - case Phase::Verify1: - digitizer_points[1] = average; - break; - - case Phase::Calibrate2: - case Phase::Verify2: - digitizer_points[2] = average; - break; - - default: - break; - } - - if( phase == Phase::Calibrate2 ) { - const std::array display_points { { - image_calibrate_0.screen_rect().center(), - image_calibrate_1.screen_rect().center(), - image_calibrate_2.screen_rect().center(), - } }; - - calibration = { digitizer_points, display_points }; - } - - if( phase == Phase::Verify2 ) { - const auto calibrated_0 = calibration.translate(digitizer_points[0]); - const auto d_sq_0 = distance_squared(calibrated_0, image_verify_0); - - const auto calibrated_1 = calibration.translate(digitizer_points[1]); - const auto d_sq_1 = distance_squared(calibrated_1, image_verify_1); - - const auto calibrated_2 = calibration.translate(digitizer_points[2]); - const auto d_sq_2 = distance_squared(calibrated_2, image_verify_2); - - if( (d_sq_0 < verify_d_sq_max) && (d_sq_1 < verify_d_sq_max) && (d_sq_2 < verify_d_sq_max) ) { - next_phase = Phase::Success; - } else { - next_phase = Phase::Failure; - } - } - - set_phase(next_phase); + auto next_phase = static_cast(toUType(phase) + 1); + + switch (phase) { + case Phase::Calibrate0: + case Phase::Verify0: + digitizer_points[0] = average; + break; + + case Phase::Calibrate1: + case Phase::Verify1: + digitizer_points[1] = average; + break; + + case Phase::Calibrate2: + case Phase::Verify2: + digitizer_points[2] = average; + break; + + default: + break; + } + + if (phase == Phase::Calibrate2) { + const std::array display_points{{ + image_calibrate_0.screen_rect().center(), + image_calibrate_1.screen_rect().center(), + image_calibrate_2.screen_rect().center(), + }}; + + calibration = {digitizer_points, display_points}; + } + + if (phase == Phase::Verify2) { + const auto calibrated_0 = calibration.translate(digitizer_points[0]); + const auto d_sq_0 = distance_squared(calibrated_0, image_verify_0); + + const auto calibrated_1 = calibration.translate(digitizer_points[1]); + const auto d_sq_1 = distance_squared(calibrated_1, image_verify_1); + + const auto calibrated_2 = calibration.translate(digitizer_points[2]); + const auto d_sq_2 = distance_squared(calibrated_2, image_verify_2); + + if ((d_sq_0 < verify_d_sq_max) && (d_sq_1 < verify_d_sq_max) && (d_sq_2 < verify_d_sq_max)) { + next_phase = Phase::Success; + } else { + next_phase = Phase::Failure; + } + } + + set_phase(next_phase); } void TouchCalibrationView::on_ok() { - if( phase == Phase::Success ) { - persistent_memory::set_touch_calibration(calibration); - nav.pop(); - } - if( phase == Phase::Failure ) { - set_phase(Phase::Calibrate0); - } + if (phase == Phase::Success) { + persistent_memory::set_touch_calibration(calibration); + nav.pop(); + } + if (phase == Phase::Failure) { + set_phase(Phase::Calibrate0); + } } void TouchCalibrationView::on_cancel() { - nav.pop(); + nav.pop(); } void TouchCalibrationView::on_frame_sync() { - switch(phase) { - case Phase::Calibrate0: - case Phase::Calibrate1: - case Phase::Calibrate2: - case Phase::Verify0: - case Phase::Verify1: - case Phase::Verify2: - break; - - default: - return; - } - - const auto frame = get_touch_frame(); - const auto metrics = touch::calculate_metrics(frame); - const auto x = metrics.x * 1024; - const auto y = metrics.y * 1024; - - if( metrics.r < 640.0f ) { - if( samples_count > 0 ) { - average.x = ((average.x * 7) + x) / 8; - average.y = ((average.y * 7) + y) / 8; - } else { - average.x = x; - average.y = y; - } - - samples_count += 1; - } else { - if( samples_count >= samples_limit ) { - touch_complete(); - } - samples_count = 0; - } + switch (phase) { + case Phase::Calibrate0: + case Phase::Calibrate1: + case Phase::Calibrate2: + case Phase::Verify0: + case Phase::Verify1: + case Phase::Verify2: + break; + + default: + return; + } + + const auto frame = get_touch_frame(); + const auto metrics = touch::calculate_metrics(frame); + const auto x = metrics.x * 1024; + const auto y = metrics.y * 1024; + + if (metrics.r < 640.0f) { + if (samples_count > 0) { + average.x = ((average.x * 7) + x) / 8; + average.y = ((average.y * 7) + y) / 8; + } else { + average.x = x; + average.y = y; + } + + samples_count += 1; + } else { + if (samples_count >= samples_limit) { + touch_complete(); + } + samples_count = 0; + } } } /* namespace ui */ diff --git a/firmware/application/apps/ui_touch_calibration.hpp b/firmware/application/apps/ui_touch_calibration.hpp index b47fc8599..f64bca1d6 100644 --- a/firmware/application/apps/ui_touch_calibration.hpp +++ b/firmware/application/apps/ui_touch_calibration.hpp @@ -29,132 +29,119 @@ namespace ui { class TouchCalibrationView : public View { -public: - TouchCalibrationView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Calibration"; }; - -private: - enum class Phase { - Init, - Calibrate0, - Calibrate1, - Calibrate2, - Verify0, - Verify1, - Verify2, - Success, - Failure, - }; - - NavigationView& nav; - Phase phase { Phase::Init }; - - void update_target(); - - void set_phase(const Phase value); - - uint32_t distance_squared(const Point& touch_point, const Image& target); - - void touch_complete(); - void on_ok(); - void on_cancel(); - - const uint32_t samples_limit { 40 }; - const uint32_t verify_d_sq_max = 10 * 10; - - uint32_t samples_count { 0 }; - - touch::DigitizerPoint average { }; - - std::array digitizer_points { }; - - touch::Calibration calibration; - - Image image_calibrate_0 { - { 32 - 16, 32 - 16, 32, 32 }, - &bitmap_target_calibrate, - Color::white(), - Color::black() - }; - - Image image_calibrate_1 { - { 240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32 }, - &bitmap_target_calibrate, - Color::white(), - Color::black() - }; - - Image image_calibrate_2 { - { 240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32 }, - &bitmap_target_calibrate, - Color::white(), - Color::black() - }; - - Image image_verify_0 { - { 32 - 16, 32 - 16, 32, 32 }, - &bitmap_target_verify, - Color::white(), - Color::black() - }; - - Image image_verify_1 { - { 240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32 }, - &bitmap_target_verify, - Color::white(), - Color::black() - }; - - Image image_verify_2 { - { 240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32 }, - &bitmap_target_verify, - Color::white(), - Color::black() - }; - - Text label_calibrate { - { 16, 5 * 16, 26 * 8, 1 * 16 }, - "Touch targets to calibrate" - }; - - Text label_verify { - { 28, 5 * 16, 23 * 8, 1 * 16 }, - "Touch targets to verify" - }; - - Text label_success { - { 32, 5 * 16, 22 * 8, 1 * 16 }, - "Apply new calibration?" - }; - - Text label_failure { - { 16, 5 * 16, 26 * 8, 1 * 16 }, - "Calibration failed. Retry?" - }; - - Button button_cancel { - { 40, 200, 64, 24 }, - "Cancel" - }; - - Button button_ok { - { 136, 200, 64, 24 }, - "OK" - }; - - void on_frame_sync(); - - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->on_frame_sync(); - } - }; + public: + TouchCalibrationView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Calibration"; }; + + private: + enum class Phase { + Init, + Calibrate0, + Calibrate1, + Calibrate2, + Verify0, + Verify1, + Verify2, + Success, + Failure, + }; + + NavigationView& nav; + Phase phase{Phase::Init}; + + void update_target(); + + void set_phase(const Phase value); + + uint32_t distance_squared(const Point& touch_point, const Image& target); + + void touch_complete(); + void on_ok(); + void on_cancel(); + + const uint32_t samples_limit{40}; + const uint32_t verify_d_sq_max = 10 * 10; + + uint32_t samples_count{0}; + + touch::DigitizerPoint average{}; + + std::array digitizer_points{}; + + touch::Calibration calibration; + + Image image_calibrate_0{ + {32 - 16, 32 - 16, 32, 32}, + &bitmap_target_calibrate, + Color::white(), + Color::black()}; + + Image image_calibrate_1{ + {240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32}, + &bitmap_target_calibrate, + Color::white(), + Color::black()}; + + Image image_calibrate_2{ + {240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32}, + &bitmap_target_calibrate, + Color::white(), + Color::black()}; + + Image image_verify_0{ + {32 - 16, 32 - 16, 32, 32}, + &bitmap_target_verify, + Color::white(), + Color::black()}; + + Image image_verify_1{ + {240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32}, + &bitmap_target_verify, + Color::white(), + Color::black()}; + + Image image_verify_2{ + {240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32}, + &bitmap_target_verify, + Color::white(), + Color::black()}; + + Text label_calibrate{ + {16, 5 * 16, 26 * 8, 1 * 16}, + "Touch targets to calibrate"}; + + Text label_verify{ + {28, 5 * 16, 23 * 8, 1 * 16}, + "Touch targets to verify"}; + + Text label_success{ + {32, 5 * 16, 22 * 8, 1 * 16}, + "Apply new calibration?"}; + + Text label_failure{ + {16, 5 * 16, 26 * 8, 1 * 16}, + "Calibration failed. Retry?"}; + + Button button_cancel{ + {40, 200, 64, 24}, + "Cancel"}; + + Button button_ok{ + {136, 200, 64, 24}, + "OK"}; + + void on_frame_sync(); + + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->on_frame_sync(); + }}; }; } /* namespace ui */ -#endif/*__UI_TOUCH_CALIBRATION_HPP__*/ +#endif /*__UI_TOUCH_CALIBRATION_HPP__*/ diff --git a/firmware/application/apps/ui_touchtunes.cpp b/firmware/application/apps/ui_touchtunes.cpp index 7420b993d..7fb66d9d5 100644 --- a/firmware/application/apps/ui_touchtunes.cpp +++ b/firmware/application/apps/ui_touchtunes.cpp @@ -28,57 +28,56 @@ #include "string_format.hpp" #include "cpld_update.hpp" - using namespace portapack; using namespace encoders; namespace ui { void TouchTunesView::focus() { - field_pin.focus(); + field_pin.focus(); } TouchTunesView::~TouchTunesView() { - transmitter_model.disable(); - hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. - baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. + transmitter_model.disable(); + hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. + baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up. } void TouchTunesView::stop_tx() { - transmitter_model.disable(); - tx_mode = IDLE; - progressbar.set_value(0); - - // EW Mode Check - if(check_ew.value()) { - start_ew(); - } else { - text_status.set("Ready"); - } + transmitter_model.disable(); + tx_mode = IDLE; + progressbar.set_value(0); + + // EW Mode Check + if (check_ew.value()) { + start_ew(); + } else { + text_status.set("Ready"); + } } void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) { - if (!done) { - // Progress - if (tx_mode == SINGLE) - progressbar.set_value(progress); - else if (tx_mode == SCAN) - progressbar.set_value((pin * TOUCHTUNES_REPEATS) + progress); - } else { - // Done transmitting - if (tx_mode == SINGLE) { - stop_tx(); - } else if (tx_mode == SCAN) { - if (pin == TOUCHTUNES_MAX_PIN) { - stop_tx(); - } else { - transmitter_model.disable(); - pin++; - field_pin.set_value(pin); - start_tx(scan_button_index); - } - } - } + if (!done) { + // Progress + if (tx_mode == SINGLE) + progressbar.set_value(progress); + else if (tx_mode == SCAN) + progressbar.set_value((pin * TOUCHTUNES_REPEATS) + progress); + } else { + // Done transmitting + if (tx_mode == SINGLE) { + stop_tx(); + } else if (tx_mode == SCAN) { + if (pin == TOUCHTUNES_MAX_PIN) { + stop_tx(); + } else { + transmitter_model.disable(); + pin++; + field_pin.set_value(pin); + start_tx(scan_button_index); + } + } + } } // EW (Electronic Warfare) Mode will jam the receiving jukebox @@ -87,136 +86,129 @@ void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) { // EW Mode works by transmitting a CW on 433.92MHz inbetween // transmission events. void TouchTunesView::start_ew() { - // Radio - transmitter_model.set_tuning_frequency(433920000); - transmitter_model.set_sampling_rate(3072000U); - transmitter_model.set_rf_amp(true); - transmitter_model.set_baseband_bandwidth(3500000U); - transmitter_model.set_tx_gain(47); - transmitter_model.enable(); - - //UI - text_status.set("Jamming..."); - progressbar.set_max(1); - progressbar.set_value(1); - + // Radio + transmitter_model.set_tuning_frequency(433920000); + transmitter_model.set_sampling_rate(3072000U); + transmitter_model.set_rf_amp(true); + transmitter_model.set_baseband_bandwidth(3500000U); + transmitter_model.set_tx_gain(47); + transmitter_model.enable(); + + // UI + text_status.set("Jamming..."); + progressbar.set_max(1); + progressbar.set_value(1); } void TouchTunesView::stop_ew() { - // Radio - transmitter_model.disable(); + // Radio + transmitter_model.disable(); - // UI - text_status.set("Ready"); - progressbar.set_value(0); + // UI + text_status.set("Ready"); + progressbar.set_value(0); } void TouchTunesView::start_tx(const uint32_t button_index) { - - // Check EW Mode - if(check_ew.value()) { - stop_ew(); - } - - std::string fragments = { "" }; - size_t bit; - uint64_t frame_data; - - if (check_scan.value()) { - scan_button_index = button_index; - tx_mode = SCAN; - progressbar.set_max(TOUCHTUNES_MAX_PIN * TOUCHTUNES_REPEATS); - text_status.set("Scanning..."); - } else { - tx_mode = SINGLE; - progressbar.set_max(TOUCHTUNES_REPEATS); - text_status.set("Transmitting..."); - } - - frame_data = TOUCHTUNES_SYNC_WORD; // Sync word - - // Insert pin value (LSB first) - for (bit = 0; bit < 8; bit++) { - frame_data <<= 1; - if (pin & (1 << bit)) - frame_data |= 1; - } - - // Insert button code (and its complement) - frame_data <<= 16; - frame_data |= (button_codes[button_index] << 8); - frame_data |= (button_codes[button_index] ^ 0xFF); - - // Convert to OOK symbols - for (bit = 0; bit < (8 + 8 + 16); bit++) { - fragments += (frame_data & 0x80000000UL) ? "1000" : "10"; - frame_data <<= 1; - } - - // Sync and end pulse - fragments = "111111111111111100000000" + fragments + "1000"; - - size_t bitstream_length = make_bitstream(fragments); - - transmitter_model.set_tuning_frequency(433920000); - transmitter_model.set_sampling_rate(OOK_SAMPLERATE); - transmitter_model.set_rf_amp(true); - transmitter_model.set_baseband_bandwidth(1750000); - transmitter_model.enable(); - - baseband::set_ook_data( - bitstream_length, - OOK_SAMPLERATE / 1766, // 560us - TOUCHTUNES_REPEATS, - 100 // Pause - ); + // Check EW Mode + if (check_ew.value()) { + stop_ew(); + } + + std::string fragments = {""}; + size_t bit; + uint64_t frame_data; + + if (check_scan.value()) { + scan_button_index = button_index; + tx_mode = SCAN; + progressbar.set_max(TOUCHTUNES_MAX_PIN * TOUCHTUNES_REPEATS); + text_status.set("Scanning..."); + } else { + tx_mode = SINGLE; + progressbar.set_max(TOUCHTUNES_REPEATS); + text_status.set("Transmitting..."); + } + + frame_data = TOUCHTUNES_SYNC_WORD; // Sync word + + // Insert pin value (LSB first) + for (bit = 0; bit < 8; bit++) { + frame_data <<= 1; + if (pin & (1 << bit)) + frame_data |= 1; + } + + // Insert button code (and its complement) + frame_data <<= 16; + frame_data |= (button_codes[button_index] << 8); + frame_data |= (button_codes[button_index] ^ 0xFF); + + // Convert to OOK symbols + for (bit = 0; bit < (8 + 8 + 16); bit++) { + fragments += (frame_data & 0x80000000UL) ? "1000" : "10"; + frame_data <<= 1; + } + + // Sync and end pulse + fragments = "111111111111111100000000" + fragments + "1000"; + + size_t bitstream_length = make_bitstream(fragments); + + transmitter_model.set_tuning_frequency(433920000); + transmitter_model.set_sampling_rate(OOK_SAMPLERATE); + transmitter_model.set_rf_amp(true); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_ook_data( + bitstream_length, + OOK_SAMPLERATE / 1766, // 560us + TOUCHTUNES_REPEATS, + 100 // Pause + ); } TouchTunesView::TouchTunesView( - NavigationView& -) { - baseband::run_image(portapack::spi_flash::image_tag_ook); - - add_children({ - &labels, - &field_pin, - &check_scan, - &check_ew, - &text_status, - &progressbar - }); - - field_pin.set_value(pin); - - field_pin.on_change = [this](int32_t v) { - pin = v; - }; - - // EW Mode - check_ew.on_select = [this](Checkbox&, bool v) { - if(v){ - start_ew(); - } else { - stop_ew(); - } - }; - - const auto button_fn = [this](Button& button) { - start_tx(button.id); - }; - - size_t n = 0; - for (auto& entry : remote_layout) { - buttons[n].on_select = button_fn; - buttons[n].id = n; - buttons[n].set_text(entry.text); - buttons[n].set_parent_rect({ - entry.position + Point(8, 0), - { (Dim)(entry.text.length() + 2) * 8, 4 * 8 } - }); - add_child(&buttons[n]); - n++; - } + NavigationView&) { + baseband::run_image(portapack::spi_flash::image_tag_ook); + + add_children({&labels, + &field_pin, + &check_scan, + &check_ew, + &text_status, + &progressbar}); + + field_pin.set_value(pin); + + field_pin.on_change = [this](int32_t v) { + pin = v; + }; + + // EW Mode + check_ew.on_select = [this](Checkbox&, bool v) { + if (v) { + start_ew(); + } else { + stop_ew(); + } + }; + + const auto button_fn = [this](Button& button) { + start_tx(button.id); + }; + + size_t n = 0; + for (auto& entry : remote_layout) { + buttons[n].on_select = button_fn; + buttons[n].id = n; + buttons[n].set_text(entry.text); + buttons[n].set_parent_rect({entry.position + Point(8, 0), + {(Dim)(entry.text.length() + 2) * 8, 4 * 8}}); + add_child(&buttons[n]); + n++; + } } } /* namespace ui */ diff --git a/firmware/application/apps/ui_touchtunes.hpp b/firmware/application/apps/ui_touchtunes.hpp index 89b76aaec..3d0bac30e 100644 --- a/firmware/application/apps/ui_touchtunes.hpp +++ b/firmware/application/apps/ui_touchtunes.hpp @@ -29,7 +29,7 @@ // The format is actually very simple if it is rather seen as short and long gaps between pulses (as seen in many OOK remotes). // The frames and data rate suspiciously match the NEC infrared protocol (http://www.sbprojects.com/knowledge/ir/nec.php) without // the address complement. The exact data rate would be 1786 baud (560us/fragment). -// NotPike: The data rate should be 1786 but the remote was transmitting at 1766 +// NotPike: The data rate should be 1786 but the remote was transmitting at 1766 // Pin 0 - On/Off // ffff00 a2888a2aaaa8888aa2aa2220 @@ -67,159 +67,150 @@ // Each 16bit button code is actually 8bit followed by its complement const uint8_t button_codes[32] = { - 0x32, // Pause - 0x78, // On/Off - 0x70, // P1 - 0x60, // P2 - 0xCA, // P3 - 0x20, // F1 - 0xF2, // Up - 0xA0, // F2 - 0x84, // Left - 0x44, // OK - 0xC4, // Right - 0x30, // F3 - 0x80, // Down - 0xB0, // F4 - 0xF0, // 1 - 0x08, // 2 - 0x88, // 3 - 0x48, // 4 - 0xC8, // 5 - 0x28, // 6 - 0xA8, // 7 - 0x68, // 8 - 0xE8, // 9 - 0x18, // Music_Karaoke - 0x98, // 0 - 0x58, // Lock_Queue - 0xD0, // Zone 1 Vol+ - 0x90, // Zone 2 Vol+ - 0xC0, // Zone 3 Vol+ - 0x50, // Zone 1 Vol- - 0x10, // Zone 2 Vol- - 0x40, // Zone 3 Vol- + 0x32, // Pause + 0x78, // On/Off + 0x70, // P1 + 0x60, // P2 + 0xCA, // P3 + 0x20, // F1 + 0xF2, // Up + 0xA0, // F2 + 0x84, // Left + 0x44, // OK + 0xC4, // Right + 0x30, // F3 + 0x80, // Down + 0xB0, // F4 + 0xF0, // 1 + 0x08, // 2 + 0x88, // 3 + 0x48, // 4 + 0xC8, // 5 + 0x28, // 6 + 0xA8, // 7 + 0x68, // 8 + 0xE8, // 9 + 0x18, // Music_Karaoke + 0x98, // 0 + 0x58, // Lock_Queue + 0xD0, // Zone 1 Vol+ + 0x90, // Zone 2 Vol+ + 0xC0, // Zone 3 Vol+ + 0x50, // Zone 1 Vol- + 0x10, // Zone 2 Vol- + 0x40, // Zone 3 Vol- }; namespace ui { class TouchTunesView : public View { -public: - TouchTunesView(NavigationView& nav); - ~TouchTunesView(); - - void focus() override; - - std::string title() const override { return "TouchTunes"; }; - -private: - uint32_t scan_button_index { }; - uint32_t pin { 0 }; - - enum tx_modes { - IDLE = 0, - SINGLE, - SCAN - }; - - tx_modes tx_mode = IDLE; - - void start_tx(const uint32_t button_index); - void stop_tx(); - void on_tx_progress(const uint32_t progress, const bool done); - void start_ew(); - void stop_ew(); - - struct remote_layout_t { - Point position; - std::string text; - }; - - const std::array remote_layout { { - { { 12 * 8, 0 }, "PAUSE" }, - { { 21 * 8, 0 }, "POWER" }, - - { { 14 * 8, 5 * 8 }, "P1" }, - { { 18 * 8, 5 * 8 }, "P2" }, - { { 22 * 8, 5 * 8 }, "P3" }, - - { { 14 * 8, 10 * 8 }, "F1" }, - { { 18 * 8 + 4, 10 * 8 }, "^" }, - { { 22 * 8, 10 * 8 }, "F2" }, - - { { 14 * 8, 14 * 8 }, "<" }, - { { 18 * 8, 14 * 8 }, "OK" }, - { { 23 * 8, 14 * 8 }, ">" }, - - { { 14 * 8, 18 * 8 }, "F3" }, - { { 18 * 8 + 4, 18 * 8 }, "V" }, - { { 22 * 8, 18 * 8 }, "F4" }, - - { { 0 * 8, 5 * 8 }, "1" }, - { { 4 * 8, 5 * 8 }, "2" }, - { { 8 * 8, 5 * 8 }, "3" }, - { { 0 * 8, 10 * 8 }, "4" }, - { { 4 * 8, 10 * 8 }, "5" }, - { { 8 * 8, 10 * 8 }, "6" }, - { { 0 * 8, 15 * 8 }, "7" }, - { { 4 * 8, 15 * 8 }, "8" }, - { { 8 * 8, 15 * 8 }, "9" }, - { { 0 * 8, 20 * 8 }, "*" }, - { { 4 * 8, 20 * 8 }, "0" }, - { { 8 * 8, 20 * 8 }, "#" }, - - { { 13 * 8, 23 * 8 }, "+" }, - { { 18 * 8, 23 * 8 }, "+" }, - { { 23 * 8, 23 * 8 }, "+" }, - - { { 13 * 8, 29 * 8 }, "-" }, - { { 18 * 8, 29 * 8 }, "-" }, - { { 23 * 8, 29 * 8 }, "-" } - } }; - - Labels labels { - { { 2 * 8, 1 * 8 }, "PIN:", Color::light_grey() }, - { { 13 * 8 + 4, 27 * 8 }, "VOL1 VOL2 VOL3", Color::light_grey() } - }; - - std::array buttons { }; - - NumberField field_pin { - { 6 * 8, 1 * 8 }, - 3, - { 0, 255 }, - 1, - '0' - }; - - Checkbox check_scan { - { 2 * 8, 25 * 8 }, - 4, - "Scan" - }; - - Checkbox check_ew { - { 2 * 8, 29 * 8 }, - 4, - "EW Mode" - }; - - Text text_status { - { 2 * 8, 33 * 8, 128, 16 }, - "Ready" - }; - - ProgressBar progressbar { - { 2 * 8, 35 * 8, 208, 16 } - }; - - MessageHandlerRegistration message_handler_tx_progress { - Message::ID::TXProgress, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.progress, message.done); - } - }; + public: + TouchTunesView(NavigationView& nav); + ~TouchTunesView(); + + void focus() override; + + std::string title() const override { return "TouchTunes"; }; + + private: + uint32_t scan_button_index{}; + uint32_t pin{0}; + + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + tx_modes tx_mode = IDLE; + + void start_tx(const uint32_t button_index); + void stop_tx(); + void on_tx_progress(const uint32_t progress, const bool done); + void start_ew(); + void stop_ew(); + + struct remote_layout_t { + Point position; + std::string text; + }; + + const std::array remote_layout{{{{12 * 8, 0}, "PAUSE"}, + {{21 * 8, 0}, "POWER"}, + + {{14 * 8, 5 * 8}, "P1"}, + {{18 * 8, 5 * 8}, "P2"}, + {{22 * 8, 5 * 8}, "P3"}, + + {{14 * 8, 10 * 8}, "F1"}, + {{18 * 8 + 4, 10 * 8}, "^"}, + {{22 * 8, 10 * 8}, "F2"}, + + {{14 * 8, 14 * 8}, "<"}, + {{18 * 8, 14 * 8}, "OK"}, + {{23 * 8, 14 * 8}, ">"}, + + {{14 * 8, 18 * 8}, "F3"}, + {{18 * 8 + 4, 18 * 8}, "V"}, + {{22 * 8, 18 * 8}, "F4"}, + + {{0 * 8, 5 * 8}, "1"}, + {{4 * 8, 5 * 8}, "2"}, + {{8 * 8, 5 * 8}, "3"}, + {{0 * 8, 10 * 8}, "4"}, + {{4 * 8, 10 * 8}, "5"}, + {{8 * 8, 10 * 8}, "6"}, + {{0 * 8, 15 * 8}, "7"}, + {{4 * 8, 15 * 8}, "8"}, + {{8 * 8, 15 * 8}, "9"}, + {{0 * 8, 20 * 8}, "*"}, + {{4 * 8, 20 * 8}, "0"}, + {{8 * 8, 20 * 8}, "#"}, + + {{13 * 8, 23 * 8}, "+"}, + {{18 * 8, 23 * 8}, "+"}, + {{23 * 8, 23 * 8}, "+"}, + + {{13 * 8, 29 * 8}, "-"}, + {{18 * 8, 29 * 8}, "-"}, + {{23 * 8, 29 * 8}, "-"}}}; + + Labels labels{ + {{2 * 8, 1 * 8}, "PIN:", Color::light_grey()}, + {{13 * 8 + 4, 27 * 8}, "VOL1 VOL2 VOL3", Color::light_grey()}}; + + std::array buttons{}; + + NumberField field_pin{ + {6 * 8, 1 * 8}, + 3, + {0, 255}, + 1, + '0'}; + + Checkbox check_scan{ + {2 * 8, 25 * 8}, + 4, + "Scan"}; + + Checkbox check_ew{ + {2 * 8, 29 * 8}, + 4, + "EW Mode"}; + + Text text_status{ + {2 * 8, 33 * 8, 128, 16}, + "Ready"}; + + ProgressBar progressbar{ + {2 * 8, 35 * 8, 208, 16}}; + + MessageHandlerRegistration message_handler_tx_progress{ + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_view_wav.cpp b/firmware/application/apps/ui_view_wav.cpp index 0ec1dba69..e6489bd22 100644 --- a/firmware/application/apps/ui_view_wav.cpp +++ b/firmware/application/apps/ui_view_wav.cpp @@ -30,155 +30,150 @@ using namespace portapack; namespace ui { void ViewWavView::update_scale(int32_t new_scale) { - scale = new_scale; - ns_per_pixel = (1000000000UL / wav_reader->sample_rate()) * scale; - refresh_waveform(); - refresh_measurements(); + scale = new_scale; + ns_per_pixel = (1000000000UL / wav_reader->sample_rate()) * scale; + refresh_waveform(); + refresh_measurements(); } void ViewWavView::refresh_waveform() { - for (size_t i = 0; i < 240; i++) { - wav_reader->data_seek(position + (i * scale)); - wav_reader->read(&waveform_buffer[i], sizeof(int16_t)); - } - - waveform.set_dirty(); - - // Window - uint64_t w_start = (position * 240) / wav_reader->sample_count(); - uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240); - display.fill_rectangle({ 0, 10 * 16 + 1, 240, 16 }, Color::black()); - display.fill_rectangle({ (Coord)w_start, 21 * 8, (Dim)w_width + 1, 8 }, Color::white()); - display.draw_line({ 0, 10 * 16 + 1 }, { (Coord)w_start, 21 * 8 }, Color::white()); - display.draw_line({ 239, 10 * 16 + 1 }, { (Coord)(w_start + w_width), 21 * 8 }, Color::white()); + for (size_t i = 0; i < 240; i++) { + wav_reader->data_seek(position + (i * scale)); + wav_reader->read(&waveform_buffer[i], sizeof(int16_t)); + } + + waveform.set_dirty(); + + // Window + uint64_t w_start = (position * 240) / wav_reader->sample_count(); + uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240); + display.fill_rectangle({0, 10 * 16 + 1, 240, 16}, Color::black()); + display.fill_rectangle({(Coord)w_start, 21 * 8, (Dim)w_width + 1, 8}, Color::white()); + display.draw_line({0, 10 * 16 + 1}, {(Coord)w_start, 21 * 8}, Color::white()); + display.draw_line({239, 10 * 16 + 1}, {(Coord)(w_start + w_width), 21 * 8}, Color::white()); } void ViewWavView::refresh_measurements() { - uint64_t span_ns = ns_per_pixel * abs(field_cursor_b.value() - field_cursor_a.value()); - - if (span_ns) - text_delta.set(unit_auto_scale(span_ns, 0, 3) + "s (" + to_string_dec_uint(1000000000UL / span_ns) + "Hz)"); - else - text_delta.set("0us ?Hz"); + uint64_t span_ns = ns_per_pixel * abs(field_cursor_b.value() - field_cursor_a.value()); + + if (span_ns) + text_delta.set(unit_auto_scale(span_ns, 0, 3) + "s (" + to_string_dec_uint(1000000000UL / span_ns) + "Hz)"); + else + text_delta.set("0us ?Hz"); } void ViewWavView::paint(Painter& painter) { - // Waveform limits - painter.draw_hline({ 0, 6 * 16 - 1 }, 240, Color::grey()); - painter.draw_hline({ 0, 10 * 16 }, 240, Color::grey()); - - // Overall amplitude view, 0~127 to 0~255 color index - for (size_t i = 0; i < 240; i++) - painter.draw_vline({ (Coord)i, 11 * 16 }, 8, spectrum_rgb2_lut[amplitude_buffer[i] << 1]); + // Waveform limits + painter.draw_hline({0, 6 * 16 - 1}, 240, Color::grey()); + painter.draw_hline({0, 10 * 16}, 240, Color::grey()); + + // Overall amplitude view, 0~127 to 0~255 color index + for (size_t i = 0; i < 240; i++) + painter.draw_vline({(Coord)i, 11 * 16}, 8, spectrum_rgb2_lut[amplitude_buffer[i] << 1]); } void ViewWavView::on_pos_changed() { - position = (field_pos_seconds.value() * wav_reader->sample_rate()) + field_pos_samples.value(); - refresh_waveform(); + position = (field_pos_seconds.value() * wav_reader->sample_rate()) + field_pos_samples.value(); + refresh_waveform(); } void ViewWavView::load_wav(std::filesystem::path file_path) { - int16_t sample; - uint32_t average; - - - text_filename.set(file_path.filename().string()); - auto ms_duration = wav_reader->ms_duration(); - text_duration.set(unit_auto_scale(ms_duration, 2, 3) + "s"); - - wav_reader->rewind(); - - text_samplerate.set(to_string_dec_uint(wav_reader->sample_rate()) + "Hz"); - text_title.set(wav_reader->title()); - - // Fill amplitude buffer, world's worst downsampling - uint64_t skip = wav_reader->sample_count() / (240 * subsampling_factor); - - for (size_t i = 0; i < 240; i++) { - average = 0; - - for (size_t s = 0; s < subsampling_factor; s++) { - wav_reader->data_seek(((i * subsampling_factor) + s) * skip); - wav_reader->read(&sample, 2); - average += (abs(sample) >> 8); - } - - amplitude_buffer[i] = average / subsampling_factor; - } - - reset_controls(); - update_scale(1); + int16_t sample; + uint32_t average; + + text_filename.set(file_path.filename().string()); + auto ms_duration = wav_reader->ms_duration(); + text_duration.set(unit_auto_scale(ms_duration, 2, 3) + "s"); + + wav_reader->rewind(); + + text_samplerate.set(to_string_dec_uint(wav_reader->sample_rate()) + "Hz"); + text_title.set(wav_reader->title()); + + // Fill amplitude buffer, world's worst downsampling + uint64_t skip = wav_reader->sample_count() / (240 * subsampling_factor); + + for (size_t i = 0; i < 240; i++) { + average = 0; + + for (size_t s = 0; s < subsampling_factor; s++) { + wav_reader->data_seek(((i * subsampling_factor) + s) * skip); + wav_reader->read(&sample, 2); + average += (abs(sample) >> 8); + } + + amplitude_buffer[i] = average / subsampling_factor; + } + + reset_controls(); + update_scale(1); } void ViewWavView::reset_controls() { - field_scale.set_value(1); - field_pos_seconds.set_value(0); - field_pos_samples.set_value(0); - field_cursor_a.set_value(0); - field_cursor_b.set_value(0); + field_scale.set_value(1); + field_pos_seconds.set_value(0); + field_pos_samples.set_value(0); + field_cursor_a.set_value(0); + field_cursor_b.set_value(0); } - + ViewWavView::ViewWavView( - NavigationView& nav -) : nav_(nav) -{ - wav_reader = std::make_unique(); - - add_children({ - &labels, - &text_filename, - &text_samplerate, - &text_title, - &text_duration, - &button_open, - &waveform, - &field_pos_seconds, - &field_pos_samples, - &field_scale, - &field_cursor_a, - &field_cursor_b, - &text_delta - }); - reset_controls(); - button_open.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".WAV"); - open_view->on_changed = [this](std::filesystem::path file_path) { - if (!wav_reader->open(file_path)) { - nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr); - return; - } - if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) { - nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr); - return; - } - load_wav(file_path); - field_pos_seconds.focus(); - }; - }; - - field_scale.on_change = [this](int32_t value) { - update_scale(value); - }; - field_pos_seconds.on_change = [this](int32_t) { - on_pos_changed(); - }; - field_pos_samples.on_change = [this](int32_t) { - on_pos_changed(); - }; - - field_cursor_a.on_change = [this](int32_t v) { - waveform.set_cursor(0, v); - refresh_measurements(); - }; - field_cursor_b.on_change = [this](int32_t v) { - waveform.set_cursor(1, v); - refresh_measurements(); - }; - + NavigationView& nav) + : nav_(nav) { + wav_reader = std::make_unique(); + + add_children({&labels, + &text_filename, + &text_samplerate, + &text_title, + &text_duration, + &button_open, + &waveform, + &field_pos_seconds, + &field_pos_samples, + &field_scale, + &field_cursor_a, + &field_cursor_b, + &text_delta}); + reset_controls(); + button_open.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".WAV"); + open_view->on_changed = [this](std::filesystem::path file_path) { + if (!wav_reader->open(file_path)) { + nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr); + return; + } + if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) { + nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr); + return; + } + load_wav(file_path); + field_pos_seconds.focus(); + }; + }; + + field_scale.on_change = [this](int32_t value) { + update_scale(value); + }; + field_pos_seconds.on_change = [this](int32_t) { + on_pos_changed(); + }; + field_pos_samples.on_change = [this](int32_t) { + on_pos_changed(); + }; + + field_cursor_a.on_change = [this](int32_t v) { + waveform.set_cursor(0, v); + refresh_measurements(); + }; + field_cursor_b.on_change = [this](int32_t v) { + waveform.set_cursor(1, v); + refresh_measurements(); + }; } void ViewWavView::focus() { - button_open.focus(); + button_open.focus(); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_view_wav.hpp b/firmware/application/apps/ui_view_wav.hpp index 3a67ed2fa..57ec1e91c 100644 --- a/firmware/application/apps/ui_view_wav.hpp +++ b/firmware/application/apps/ui_view_wav.hpp @@ -28,118 +28,105 @@ namespace ui { class ViewWavView : public View { -public: - ViewWavView(NavigationView& nav); - - void focus() override; - void paint(Painter&) override; - - std::string title() const override { return "WAV viewer"; }; - -private: - NavigationView& nav_; - static constexpr uint32_t subsampling_factor = 8; - - void update_scale(int32_t new_scale); - void refresh_waveform(); - void refresh_measurements(); - void on_pos_changed(); - void load_wav(std::filesystem::path file_path); - void reset_controls(); - - std::unique_ptr wav_reader { }; - - int16_t waveform_buffer[240] { }; - uint8_t amplitude_buffer[240] { }; - int32_t scale { 1 }; - uint64_t ns_per_pixel { }; - uint64_t position { }; - - Labels labels { - { { 0 * 8, 0 * 16 }, "File:", Color::light_grey() }, - { { 0 * 8, 1 * 16 }, "Samplerate:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, "Title:", Color::light_grey() }, - { { 0 * 8, 3 * 16 }, "Duration:", Color::light_grey() }, - { { 0 * 8, 11 * 16 }, "Position: s Scale:", Color::light_grey() }, - { { 0 * 8, 12 * 16 }, "Cursor A:", Color::dark_cyan() }, - { { 0 * 8, 13 * 16 }, "Cursor B:", Color::dark_magenta() }, - { { 0 * 8, 14 * 16 }, "Delta:", Color::light_grey() } - }; - - Text text_filename { - { 5 * 8, 0 * 16, 12 * 8, 16 }, - "" - }; - Text text_samplerate { - { 11 * 8, 1 * 16, 8 * 8, 16 }, - "" - }; - Text text_title { - { 6 * 8, 2 * 16, 18 * 8, 16 }, - "" - }; - Text text_duration { - { 9 * 8, 3 * 16, 18 * 8, 16 }, - "" - }; - Button button_open { - { 24 * 8, 8, 6 * 8, 2 * 16 }, - "Open" - }; - - Waveform waveform { - { 0, 5 * 16, 240, 64 }, - waveform_buffer, - 240, - 0, - false, - Color::white() - }; - - NumberField field_pos_seconds { - { 9 * 8, 11 * 16 }, - 3, - { 0, 999 }, - 1, - ' ' - }; - NumberField field_pos_samples { - { 14 * 8, 11 * 16 }, - 6, - { 0, 999999 }, - 1, - '0' - }; - NumberField field_scale { - { 28 * 8, 11 * 16 }, - 2, - { 1, 40 }, - 1, - ' ' - }; - - NumberField field_cursor_a { - { 9 * 8, 12 * 16 }, - 3, - { 0, 239 }, - 1, - ' ', - true - }; - - NumberField field_cursor_b { - { 9 * 8, 13 * 16 }, - 3, - { 0, 239 }, - 1, - ' ', - true - }; - - Text text_delta { - { 6 * 8, 14 * 16, 30 * 8, 16 }, - "-" - }; + public: + ViewWavView(NavigationView& nav); + + void focus() override; + void paint(Painter&) override; + + std::string title() const override { return "WAV viewer"; }; + + private: + NavigationView& nav_; + static constexpr uint32_t subsampling_factor = 8; + + void update_scale(int32_t new_scale); + void refresh_waveform(); + void refresh_measurements(); + void on_pos_changed(); + void load_wav(std::filesystem::path file_path); + void reset_controls(); + + std::unique_ptr wav_reader{}; + + int16_t waveform_buffer[240]{}; + uint8_t amplitude_buffer[240]{}; + int32_t scale{1}; + uint64_t ns_per_pixel{}; + uint64_t position{}; + + Labels labels{ + {{0 * 8, 0 * 16}, "File:", Color::light_grey()}, + {{0 * 8, 1 * 16}, "Samplerate:", Color::light_grey()}, + {{0 * 8, 2 * 16}, "Title:", Color::light_grey()}, + {{0 * 8, 3 * 16}, "Duration:", Color::light_grey()}, + {{0 * 8, 11 * 16}, "Position: s Scale:", Color::light_grey()}, + {{0 * 8, 12 * 16}, "Cursor A:", Color::dark_cyan()}, + {{0 * 8, 13 * 16}, "Cursor B:", Color::dark_magenta()}, + {{0 * 8, 14 * 16}, "Delta:", Color::light_grey()}}; + + Text text_filename{ + {5 * 8, 0 * 16, 12 * 8, 16}, + ""}; + Text text_samplerate{ + {11 * 8, 1 * 16, 8 * 8, 16}, + ""}; + Text text_title{ + {6 * 8, 2 * 16, 18 * 8, 16}, + ""}; + Text text_duration{ + {9 * 8, 3 * 16, 18 * 8, 16}, + ""}; + Button button_open{ + {24 * 8, 8, 6 * 8, 2 * 16}, + "Open"}; + + Waveform waveform{ + {0, 5 * 16, 240, 64}, + waveform_buffer, + 240, + 0, + false, + Color::white()}; + + NumberField field_pos_seconds{ + {9 * 8, 11 * 16}, + 3, + {0, 999}, + 1, + ' '}; + NumberField field_pos_samples{ + {14 * 8, 11 * 16}, + 6, + {0, 999999}, + 1, + '0'}; + NumberField field_scale{ + {28 * 8, 11 * 16}, + 2, + {1, 40}, + 1, + ' '}; + + NumberField field_cursor_a{ + {9 * 8, 12 * 16}, + 3, + {0, 239}, + 1, + ' ', + true}; + + NumberField field_cursor_b{ + {9 * 8, 13 * 16}, + 3, + {0, 239}, + 1, + ' ', + true}; + + Text text_delta{ + {6 * 8, 14 * 16, 30 * 8, 16}, + "-"}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_whipcalc.cpp b/firmware/application/apps/ui_whipcalc.cpp index 70c012134..69abbc03a 100644 --- a/firmware/application/apps/ui_whipcalc.cpp +++ b/firmware/application/apps/ui_whipcalc.cpp @@ -30,190 +30,168 @@ using namespace portapack; -namespace ui -{ - - void WhipCalcView::focus() - { - field_frequency.focus(); - } - - void WhipCalcView::update_result() - { - double length, calclength, divider; - console.clear(true); - divider = ((double)options_type.selected_index_value() / 8.0); - - // Antenna lengths fields - if (field_frequency.value() > 0) - { - // Metric - length = (speed_of_light_mps / (double)field_frequency.value()) * divider; - auto m = to_string_dec_int((int)length, 0); - //auto cm = to_string_dec_int(int(length * 100.0) % 100, 2); - //auto mm = to_string_dec_int(int(length * 1000.0) % 10, 1); - calclength = get_decimals(length, 100); //cm - auto cm = to_string_dec_int(int(calclength), 0); - auto mm = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0); - text_result_metric.set(m + "m " + cm + "." + mm + "cm"); - - // Imperial - calclength = (speed_of_light_fps / (double)field_frequency.value()) * divider; - auto feet = to_string_dec_int(int(calclength), 0); - calclength = get_decimals(calclength, 12); //inches - auto inch = to_string_dec_int(int(calclength), 0); - auto inch_c = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0); - text_result_imperial.set(feet + "ft " + inch + "." + inch_c + "in"); - } - else - { - text_result_metric.set("infinity+"); - text_result_imperial.set("infinity+"); - return; - } - - uint8_t ant_count = 9; //Shown antennas counter - length *= 1000; //Get length in mm needed to extend the antenna - for (antenna_entry antenna : antenna_db) - { //go thru all antennas available - uint16_t element, refined_quarter = 0; - for (element = 0; element < antenna.elements.size(); element++) - { - if (length == antenna.elements[element]) //Exact element in length - { - element++; //Real element is +1 (zero based vector) - break; //Done with this ant - } - else if (length < antenna.elements[element]) - { - double remain, this_element, quarter = 0; - remain = length - antenna.elements[element - 1]; //mm needed from this element to reach length - this_element = antenna.elements[element] - antenna.elements[element - 1]; //total mm on this element - quarter = (remain * 4) / this_element; //havoc & portack ended on this int(quarter) resolution. - if (quarter - int(quarter) > 0.5) - { //rounding gave a measure closer to next quarter - refined_quarter = int(quarter) + 1; - if (refined_quarter == 4) - { //rounding gave a measure closer to next element - refined_quarter = 0; - element++; - } - } - else - { - refined_quarter = int(quarter); - } - break; //Done with this ant - } - } - /*if (!ant_count) - { - console.write(" and more ..."); - break; - }*/ - console.write(antenna.label + ": " + to_string_dec_int(element, 1) + frac_str[refined_quarter] + " elements\n"); - ant_count--; // For now, just showing all. - } - } - - WhipCalcView::WhipCalcView(NavigationView &nav) - { - add_children({&labels, - //&antennas_on_memory, - &field_frequency, - &options_type, - &text_result_metric, - &text_result_imperial, - &console, - &button_exit}); - - File antennas_file; - auto error = antennas_file.open("WHIPCALC/ANTENNAS.TXT"); - - if (!error.is_valid()) - { - std::string line; - char one_char[1]; - for (size_t pointer = 0; pointer < antennas_file.size(); pointer++) - { - antennas_file.seek(pointer); - antennas_file.read(one_char, 1); - if ((int)one_char[0] >= ' ') - line += one_char[0]; //Add it to the textline - else if (one_char[0] == '\n') - { //New Line - txtline_process(line); //make sense of this textline - line.clear(); //Ready for next textline - } - } - if (line.length() > 0) - txtline_process(line); //Last line had no newline at end ? - } - - if (!antenna_db.size()) - add_default_antenna(); - //antennas_on_memory.set(to_string_dec_int(antenna_db.size(),0) + " antennas"); //tell user - - options_type.set_selected_index(2); // Quarter wave - options_type.on_change = [this](size_t, OptionsField::value_t) { - this->update_result(); - }; - - field_frequency.set_step(1000000); // 1MHz step - field_frequency.on_change = [this](rf::Frequency) { - this->update_result(); - }; - field_frequency.on_edit = [this, &nav]() { - // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(transmitter_model.tuning_frequency()); - new_view->on_changed = [this](rf::Frequency f) { - this->field_frequency.set_value(f); - this->update_result(); - }; - }; - - button_exit.on_select = [this, &nav](Button &) { - nav.pop(); - }; - - field_frequency.set_value(transmitter_model.tuning_frequency()); - } - - void ui::WhipCalcView::txtline_process(std::string &line) - { - if (line.find("#") != std::string::npos) - return; //Line is just a comment - - char separator = ','; - size_t previous = 0; - uint16_t value = 0; - antenna_entry new_antenna; - size_t current = line.find(separator); - - while (current != std::string::npos) - { - if (!previous) - new_antenna.label.assign(line, 0, current); //antenna label - else - { - value = std::stoi(line.substr(previous, current - previous)); - if (!value) return; //No element length? abort antenna - new_antenna.elements.push_back(value); //Store this new element - } - previous = current + 1; - current = line.find(separator, previous); //Search for next space delimiter - } - - if (!previous) return; //Not even a label ? drop this antenna! - value = std::stoi(line.substr(previous, current - previous)); //Last element - - if (!value) return; - new_antenna.elements.push_back(value); - antenna_db.push_back(new_antenna); //Add this antenna - } - - void ui::WhipCalcView::add_default_antenna() - { - antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); //store a default ant500 - } -} // namespace ui +namespace ui { + +void WhipCalcView::focus() { + field_frequency.focus(); +} + +void WhipCalcView::update_result() { + double length, calclength, divider; + console.clear(true); + divider = ((double)options_type.selected_index_value() / 8.0); + + // Antenna lengths fields + if (field_frequency.value() > 0) { + // Metric + length = (speed_of_light_mps / (double)field_frequency.value()) * divider; + auto m = to_string_dec_int((int)length, 0); + // auto cm = to_string_dec_int(int(length * 100.0) % 100, 2); + // auto mm = to_string_dec_int(int(length * 1000.0) % 10, 1); + calclength = get_decimals(length, 100); // cm + auto cm = to_string_dec_int(int(calclength), 0); + auto mm = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0); + text_result_metric.set(m + "m " + cm + "." + mm + "cm"); + + // Imperial + calclength = (speed_of_light_fps / (double)field_frequency.value()) * divider; + auto feet = to_string_dec_int(int(calclength), 0); + calclength = get_decimals(calclength, 12); // inches + auto inch = to_string_dec_int(int(calclength), 0); + auto inch_c = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0); + text_result_imperial.set(feet + "ft " + inch + "." + inch_c + "in"); + } else { + text_result_metric.set("infinity+"); + text_result_imperial.set("infinity+"); + return; + } + + uint8_t ant_count = 9; // Shown antennas counter + length *= 1000; // Get length in mm needed to extend the antenna + for (antenna_entry antenna : antenna_db) { // go thru all antennas available + uint16_t element, refined_quarter = 0; + for (element = 0; element < antenna.elements.size(); element++) { + if (length == antenna.elements[element]) // Exact element in length + { + element++; // Real element is +1 (zero based vector) + break; // Done with this ant + } else if (length < antenna.elements[element]) { + double remain, this_element, quarter = 0; + remain = length - antenna.elements[element - 1]; // mm needed from this element to reach length + this_element = antenna.elements[element] - antenna.elements[element - 1]; // total mm on this element + quarter = (remain * 4) / this_element; // havoc & portack ended on this int(quarter) resolution. + if (quarter - int(quarter) > 0.5) { // rounding gave a measure closer to next quarter + refined_quarter = int(quarter) + 1; + if (refined_quarter == 4) { // rounding gave a measure closer to next element + refined_quarter = 0; + element++; + } + } else { + refined_quarter = int(quarter); + } + break; // Done with this ant + } + } + /*if (!ant_count) + { + console.write(" and more ..."); + break; + }*/ + console.write(antenna.label + ": " + to_string_dec_int(element, 1) + frac_str[refined_quarter] + " elements\n"); + ant_count--; // For now, just showing all. + } +} + +WhipCalcView::WhipCalcView(NavigationView& nav) { + add_children({&labels, + //&antennas_on_memory, + &field_frequency, + &options_type, + &text_result_metric, + &text_result_imperial, + &console, + &button_exit}); + + File antennas_file; + auto error = antennas_file.open("WHIPCALC/ANTENNAS.TXT"); + + if (!error.is_valid()) { + std::string line; + char one_char[1]; + for (size_t pointer = 0; pointer < antennas_file.size(); pointer++) { + antennas_file.seek(pointer); + antennas_file.read(one_char, 1); + if ((int)one_char[0] >= ' ') + line += one_char[0]; // Add it to the textline + else if (one_char[0] == '\n') { // New Line + txtline_process(line); // make sense of this textline + line.clear(); // Ready for next textline + } + } + if (line.length() > 0) + txtline_process(line); // Last line had no newline at end ? + } + + if (!antenna_db.size()) + add_default_antenna(); + // antennas_on_memory.set(to_string_dec_int(antenna_db.size(),0) + " antennas"); //tell user + + options_type.set_selected_index(2); // Quarter wave + options_type.on_change = [this](size_t, OptionsField::value_t) { + this->update_result(); + }; + + field_frequency.set_step(1000000); // 1MHz step + field_frequency.on_change = [this](rf::Frequency) { + this->update_result(); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->field_frequency.set_value(f); + this->update_result(); + }; + }; + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; + + field_frequency.set_value(transmitter_model.tuning_frequency()); +} + +void ui::WhipCalcView::txtline_process(std::string& line) { + if (line.find("#") != std::string::npos) + return; // Line is just a comment + + char separator = ','; + size_t previous = 0; + uint16_t value = 0; + antenna_entry new_antenna; + size_t current = line.find(separator); + + while (current != std::string::npos) { + if (!previous) + new_antenna.label.assign(line, 0, current); // antenna label + else { + value = std::stoi(line.substr(previous, current - previous)); + if (!value) return; // No element length? abort antenna + new_antenna.elements.push_back(value); // Store this new element + } + previous = current + 1; + current = line.find(separator, previous); // Search for next space delimiter + } + + if (!previous) return; // Not even a label ? drop this antenna! + value = std::stoi(line.substr(previous, current - previous)); // Last element + + if (!value) return; + new_antenna.elements.push_back(value); + antenna_db.push_back(new_antenna); // Add this antenna +} + +void ui::WhipCalcView::add_default_antenna() { + antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); // store a default ant500 +} +} // namespace ui diff --git a/firmware/application/apps/ui_whipcalc.hpp b/firmware/application/apps/ui_whipcalc.hpp index 2651d32fc..26380bc17 100644 --- a/firmware/application/apps/ui_whipcalc.hpp +++ b/firmware/application/apps/ui_whipcalc.hpp @@ -30,67 +30,64 @@ #include "string_format.hpp" #include -namespace ui -{ - class WhipCalcView : public View - { - public: - WhipCalcView(NavigationView &nav); - void focus() override; - std::string title() const override { return "Ant. length"; }; +namespace ui { +class WhipCalcView : public View { + public: + WhipCalcView(NavigationView& nav); + void focus() override; + std::string title() const override { return "Ant. length"; }; - private: - const double speed_of_light_mps = 299792458.0; // m/s - const double speed_of_light_fps = 983571087.90472; // feet/s - const std::string frac_str[4] = {"", " 1/4", " 1/2", " 3/4"}; + private: + const double speed_of_light_mps = 299792458.0; // m/s + const double speed_of_light_fps = 983571087.90472; // feet/s + const std::string frac_str[4] = {"", " 1/4", " 1/2", " 3/4"}; - struct antenna_entry - { - std::string label{}; - std::vector elements{}; - }; + struct antenna_entry { + std::string label{}; + std::vector elements{}; + }; - std::vector antenna_db{}; - void update_result(); - uint16_t string_to_number(std::string); - void txtline_process(std::string &); - void add_default_antenna(); + std::vector antenna_db{}; + void update_result(); + uint16_t string_to_number(std::string); + void txtline_process(std::string&); + void add_default_antenna(); - Labels labels{ - {{2 * 8, 1 * 16}, "Frequency:", Color::light_grey()}, - {{7 * 8, 2 * 16}, "Wave:", Color::light_grey()}, - {{5 * 8, 3 * 16}, "Metric:", Color::light_grey()}, - {{3 * 8, 4 * 16}, "Imperial:", Color::light_grey()}}; + Labels labels{ + {{2 * 8, 1 * 16}, "Frequency:", Color::light_grey()}, + {{7 * 8, 2 * 16}, "Wave:", Color::light_grey()}, + {{5 * 8, 3 * 16}, "Metric:", Color::light_grey()}, + {{3 * 8, 4 * 16}, "Imperial:", Color::light_grey()}}; - FrequencyField field_frequency{ - {13 * 8, 1 * 16}, - }; + FrequencyField field_frequency{ + {13 * 8, 1 * 16}, + }; - OptionsField options_type{ - {13 * 8, 2 * 16}, - 7, - {{"Full", 8}, - {"Half", 4}, - {"Quarter", 2}, - {"3/4", 6}, - {"1/8", 1}, - {"3/8", 3}, - {"5/8", 5}, - {"7/8", 7}}}; + OptionsField options_type{ + {13 * 8, 2 * 16}, + 7, + {{"Full", 8}, + {"Half", 4}, + {"Quarter", 2}, + {"3/4", 6}, + {"1/8", 1}, + {"3/8", 3}, + {"5/8", 5}, + {"7/8", 7}}}; - Text text_result_metric{ - {13 * 8, 3 * 16, 10 * 16, 16}, - "-"}; - Text text_result_imperial{ - {13 * 8, 4 * 16, 10 * 16, 16}, - "-"}; - Console console{ - {0, 6 * 16, 240, 160}}; + Text text_result_metric{ + {13 * 8, 3 * 16, 10 * 16, 16}, + "-"}; + Text text_result_imperial{ + {13 * 8, 4 * 16, 10 * 16, 16}, + "-"}; + Console console{ + {0, 6 * 16, 240, 160}}; - Button button_exit{ - {72, 17 * 16, 96, 32}, - "Back"}; - }; + Button button_exit{ + {72, 17 * 16, 96, 32}, + "Back"}; +}; } /* namespace ui */ diff --git a/firmware/application/audio.cpp b/firmware/application/audio.cpp index ce32076cb..c4366bf03 100644 --- a/firmware/application/audio.cpp +++ b/firmware/application/audio.cpp @@ -37,95 +37,95 @@ namespace { // "Master": I2S peripheral generates SCK/WS, transmits to audio codec. -constexpr i2s::ConfigTX i2s0_config_tx_master_base_clk { - .dao = i2s::DAO { - .wordwidth = i2s::WordWidth::Bits16, - .mono = 0, - .stop = 1, - .reset = 0, - .ws_sel = 0, // Master - .ws_halfperiod = 0x0f, - .mute = 1, - }, - .txrate = i2s::MCLKRate { - .x_divider = 0, - .y_divider = 0, - }, - .txbitrate = i2s::BitRate { - .bitrate = 7, - }, - .txmode = i2s::Mode { - .clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK, - .four_pin = 0, - .mclk_out_en = 1, - }, - .sck_in_sel = 1, +constexpr i2s::ConfigTX i2s0_config_tx_master_base_clk{ + .dao = i2s::DAO{ + .wordwidth = i2s::WordWidth::Bits16, + .mono = 0, + .stop = 1, + .reset = 0, + .ws_sel = 0, // Master + .ws_halfperiod = 0x0f, + .mute = 1, + }, + .txrate = i2s::MCLKRate{ + .x_divider = 0, + .y_divider = 0, + }, + .txbitrate = i2s::BitRate{ + .bitrate = 7, + }, + .txmode = i2s::Mode{ + .clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK, + .four_pin = 0, + .mclk_out_en = 1, + }, + .sck_in_sel = 1, }; -constexpr i2s::ConfigRX i2s0_config_rx_four_wire { - .dai = i2s::DAI { - .wordwidth = i2s::WordWidth::Bits16, - .mono = 0, - .stop = 1, - .reset = 0, - .ws_sel = 0, // Master - .ws_halfperiod = 0x0f, - }, - .rxrate = i2s::MCLKRate { - .x_divider = 0, - .y_divider = 0, - }, - .rxbitrate = i2s::BitRate { - .bitrate = 7, - }, - .rxmode = i2s::Mode { - .clksel = i2s::ClockSelect::FractionalDivider, - .four_pin = 1, - .mclk_out_en = 0, - }, - .sck_in_sel = 0, +constexpr i2s::ConfigRX i2s0_config_rx_four_wire{ + .dai = i2s::DAI{ + .wordwidth = i2s::WordWidth::Bits16, + .mono = 0, + .stop = 1, + .reset = 0, + .ws_sel = 0, // Master + .ws_halfperiod = 0x0f, + }, + .rxrate = i2s::MCLKRate{ + .x_divider = 0, + .y_divider = 0, + }, + .rxbitrate = i2s::BitRate{ + .bitrate = 7, + }, + .rxmode = i2s::Mode{ + .clksel = i2s::ClockSelect::FractionalDivider, + .four_pin = 1, + .mclk_out_en = 0, + }, + .sck_in_sel = 0, }; // "Slave": I2S controlled by external SCK/WS, received from audio codec. -constexpr i2s::ConfigTX i2s0_config_tx_slave_base_clk { - .dao = i2s::DAO { - .wordwidth = i2s::WordWidth::Bits16, - .mono = 0, - .stop = 1, - .reset = 0, - .ws_sel = 1, - .ws_halfperiod = 0x0f, - .mute = 1, - }, - .txrate = i2s::MCLKRate { - .x_divider = 0, - .y_divider = 0, - }, - .txbitrate = i2s::BitRate { - .bitrate = 0, - }, - .txmode = i2s::Mode { - .clksel = i2s::ClockSelect::FractionalDivider, - .four_pin = 0, - .mclk_out_en = 1, - }, - .sck_in_sel = 1, +constexpr i2s::ConfigTX i2s0_config_tx_slave_base_clk{ + .dao = i2s::DAO{ + .wordwidth = i2s::WordWidth::Bits16, + .mono = 0, + .stop = 1, + .reset = 0, + .ws_sel = 1, + .ws_halfperiod = 0x0f, + .mute = 1, + }, + .txrate = i2s::MCLKRate{ + .x_divider = 0, + .y_divider = 0, + }, + .txbitrate = i2s::BitRate{ + .bitrate = 0, + }, + .txmode = i2s::Mode{ + .clksel = i2s::ClockSelect::FractionalDivider, + .four_pin = 0, + .mclk_out_en = 1, + }, + .sck_in_sel = 1, }; -constexpr i2s::ConfigDMA i2s0_config_dma { - .dma1 = i2s::DMA { - .rx_enable = 1, - .tx_enable = 0, - .rx_depth = 4, - .tx_depth = 0, - }, - .dma2 = i2s::DMA { - .rx_enable = 0, - .tx_enable = 1, - .rx_depth = 0, - .tx_depth = 4, - }, +constexpr i2s::ConfigDMA i2s0_config_dma{ + .dma1 = i2s::DMA{ + .rx_enable = 1, + .tx_enable = 0, + .rx_depth = 4, + .tx_depth = 0, + }, + .dma2 = i2s::DMA{ + .rx_enable = 0, + .tx_enable = 1, + .rx_depth = 0, + .tx_depth = 4, + }, }; static audio::Codec* audio_codec = nullptr; @@ -135,47 +135,47 @@ static audio::Codec* audio_codec = nullptr; namespace output { void start() { - i2s::i2s0::tx_start(); - unmute(); + i2s::i2s0::tx_start(); + unmute(); } void stop() { - mute(); - i2s::i2s0::tx_stop(); + mute(); + i2s::i2s0::tx_stop(); } void mute() { - i2s::i2s0::tx_mute(); - audio_codec->headphone_disable(); + i2s::i2s0::tx_mute(); + audio_codec->headphone_disable(); } void unmute() { - i2s::i2s0::tx_unmute(); - audio_codec->headphone_enable(); + i2s::i2s0::tx_unmute(); + audio_codec->headphone_enable(); } void speaker_mute() { - i2s::i2s0::tx_mute(); - audio_codec->speaker_disable(); - } + i2s::i2s0::tx_mute(); + audio_codec->speaker_disable(); +} - void speaker_unmute() { - i2s::i2s0::tx_unmute(); - audio_codec->speaker_enable(); - } +void speaker_unmute() { + i2s::i2s0::tx_unmute(); + audio_codec->speaker_enable(); +} } /* namespace output */ namespace input { void start(int8_t alc_mode) { - audio_codec->microphone_enable(alc_mode); // added user-GUI selection for AK4951, ALC mode parameter. - i2s::i2s0::rx_start(); + audio_codec->microphone_enable(alc_mode); // added user-GUI selection for AK4951, ALC mode parameter. + i2s::i2s0::rx_start(); } void stop() { - i2s::i2s0::rx_stop(); - audio_codec->microphone_disable(); + i2s::i2s0::rx_stop(); + audio_codec->microphone_disable(); } } /* namespace input */ @@ -183,11 +183,11 @@ void stop() { namespace headphone { volume_range_t volume_range() { - return audio_codec->headphone_gain_range(); + return audio_codec->headphone_gain_range(); } void set_volume(const volume_t volume) { - audio_codec->set_headphone_volume(volume); + audio_codec->set_headphone_volume(volume); } } /* namespace headphone */ @@ -195,52 +195,51 @@ void set_volume(const volume_t volume) { namespace debug { size_t reg_count() { - return audio_codec->reg_count(); + return audio_codec->reg_count(); } uint32_t reg_read(const size_t register_number) { - return audio_codec->reg_read(register_number); + return audio_codec->reg_read(register_number); } std::string codec_name() { - return audio_codec->name(); + return audio_codec->name(); } size_t reg_bits() { - return audio_codec->reg_bits(); + return audio_codec->reg_bits(); } } /* namespace debug */ void init(audio::Codec* const codec) { - clock_manager.start_audio_pll(); + clock_manager.start_audio_pll(); - // Configure I2S before activating codec interface. - i2s::i2s0::configure( - i2s0_config_tx_master_base_clk, - i2s0_config_rx_four_wire, - i2s0_config_dma - ); + // Configure I2S before activating codec interface. + i2s::i2s0::configure( + i2s0_config_tx_master_base_clk, + i2s0_config_rx_four_wire, + i2s0_config_dma); - audio_codec = codec; - audio_codec->init(); + audio_codec = codec; + audio_codec->init(); - // Set pin mode, since it's likely GPIO (as left after CPLD JTAG interactions). - portapack::pin_i2s0_rx_sda.mode(3); + // Set pin mode, since it's likely GPIO (as left after CPLD JTAG interactions). + portapack::pin_i2s0_rx_sda.mode(3); } void shutdown() { - audio_codec->reset(); - input::stop(); - output::stop(); + audio_codec->reset(); + input::stop(); + output::stop(); - i2s::i2s0::shutdown(); + i2s::i2s0::shutdown(); - clock_manager.stop_audio_pll(); + clock_manager.stop_audio_pll(); } void set_rate(const Rate rate) { - clock_manager.set_base_audio_clock_divider(toUType(rate)); + clock_manager.set_base_audio_clock_divider(toUType(rate)); } } /* namespace audio */ diff --git a/firmware/application/audio.hpp b/firmware/application/audio.hpp index 56301ee8c..e817889b4 100644 --- a/firmware/application/audio.hpp +++ b/firmware/application/audio.hpp @@ -33,33 +33,33 @@ namespace audio { class Codec { -public: - virtual ~Codec() { } + public: + virtual ~Codec() {} - virtual std::string name() const = 0; + virtual std::string name() const = 0; - virtual bool reset() = 0; - virtual void init() = 0; + virtual bool reset() = 0; + virtual void init() = 0; - virtual void speaker_enable() = 0; - virtual void speaker_disable() = 0; + virtual void speaker_enable() = 0; + virtual void speaker_disable() = 0; - virtual void headphone_enable() = 0; - virtual void headphone_disable() = 0; - virtual volume_range_t headphone_gain_range() const = 0; - virtual void set_headphone_volume(const volume_t volume) = 0; + virtual void headphone_enable() = 0; + virtual void headphone_disable() = 0; + virtual volume_range_t headphone_gain_range() const = 0; + virtual void set_headphone_volume(const volume_t volume) = 0; - virtual void microphone_enable(int8_t alc_mode) = 0; // added user-GUI AK4951 ,selected ALC mode. - virtual void microphone_disable() = 0; + virtual void microphone_enable(int8_t alc_mode) = 0; // added user-GUI AK4951 ,selected ALC mode. + virtual void microphone_disable() = 0; - virtual size_t reg_count() const = 0; - virtual size_t reg_bits() const = 0; - virtual uint32_t reg_read(const size_t register_number) = 0; + virtual size_t reg_count() const = 0; + virtual size_t reg_bits() const = 0; + virtual uint32_t reg_read(const size_t register_number) = 0; }; namespace output { -void start(); // this other start(),no changed. ,in namespace output , used to config audio playback mode, +void start(); // this other start(),no changed. ,in namespace output , used to config audio playback mode, void stop(); void mute(); @@ -106,13 +106,13 @@ void init(audio::Codec* const codec); void shutdown(); enum class Rate { - Hz_12000 = 4, - Hz_24000 = 2, - Hz_48000 = 1, + Hz_12000 = 4, + Hz_24000 = 2, + Hz_48000 = 1, }; void set_rate(const Rate rate); } /* namespace audio */ -#endif/*__AUDIO_H__*/ +#endif /*__AUDIO_H__*/ diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 37cfb5485..4aa7720e9 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -36,346 +36,314 @@ using namespace portapack; namespace baseband { static void send_message(const Message* const message) { - // If message is only sent by this function via one thread, no need to check if - // another message is present before setting new message. - shared_memory.baseband_message = message; - creg::m0apptxevent::assert_event(); - while(shared_memory.baseband_message); + // If message is only sent by this function via one thread, no need to check if + // another message is present before setting new message. + shared_memory.baseband_message = message; + creg::m0apptxevent::assert_event(); + while (shared_memory.baseband_message) + ; } void AMConfig::apply() const { - const AMConfigureMessage message { - taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 5 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW) - taps_6k0_decim_1, // common FIR filter taps pre-decim_1 to all 5 x AM mod. types. - decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k all rest AM modes) - channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW) - modulation, // var parameter . - audio_12k_hpf_300hz_config - }; - send_message(&message); - audio::set_rate(audio::Rate::Hz_12000); + const AMConfigureMessage message{ + taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 5 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW) + taps_6k0_decim_1, // common FIR filter taps pre-decim_1 to all 5 x AM mod. types. + decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k all rest AM modes) + channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW) + modulation, // var parameter . + audio_12k_hpf_300hz_config}; + send_message(&message); + audio::set_rate(audio::Rate::Hz_12000); } void NBFMConfig::apply(const uint8_t squelch_level) const { - const NBFMConfigureMessage message { - decim_0, - decim_1, - channel, - 2, - deviation, - audio_24k_hpf_300hz_config, - audio_24k_deemph_300_6_config, - squelch_level - }; - send_message(&message); - audio::set_rate(audio::Rate::Hz_24000); + const NBFMConfigureMessage message{ + decim_0, + decim_1, + channel, + 2, + deviation, + audio_24k_hpf_300hz_config, + audio_24k_deemph_300_6_config, + squelch_level}; + send_message(&message); + audio::set_rate(audio::Rate::Hz_24000); } void WFMConfig::apply() const { - const WFMConfigureMessage message { - decim_0, // taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0 - decim_1, // taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1 - taps_64_lp_156_198, - 75000, - audio_48k_hpf_30hz_config, - audio_48k_deemph_2122_6_config - }; - send_message(&message); - audio::set_rate(audio::Rate::Hz_48000); + const WFMConfigureMessage message{ + decim_0, // taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0 + decim_1, // taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1 + taps_64_lp_156_198, + 75000, + audio_48k_hpf_30hz_config, + audio_48k_deemph_2122_6_config}; + send_message(&message); + audio::set_rate(audio::Rate::Hz_48000); } - void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration) { - shared_memory.bb_data.tones_data.tone_defs[index].delta = delta; - shared_memory.bb_data.tones_data.tone_defs[index].duration = duration; + shared_memory.bb_data.tones_data.tone_defs[index].delta = delta; + shared_memory.bb_data.tones_data.tone_defs[index].duration = duration; } -void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, - const bool dual_tone, const bool audio_out) { - const TonesConfigureMessage message { - bw, - pre_silence, - tone_count, - dual_tone, - audio_out - }; - send_message(&message); +void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, const bool dual_tone, const bool audio_out) { + const TonesConfigureMessage message{ + bw, + pre_silence, + tone_count, + dual_tone, + audio_out}; + send_message(&message); } void kill_tone() { - const TonesConfigureMessage message { - 0, - 0, - 0, - false, - false - }; - send_message(&message); + const TonesConfigureMessage message{ + 0, + 0, + 0, + false, + false}; + send_message(&message); } void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration) { - const SSTVConfigureMessage message { - vis_code, - pixel_duration - }; - send_message(&message); + const SSTVConfigureMessage message{ + vis_code, + pixel_duration}; + send_message(&message); } void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { - const AFSKRxConfigureMessage message { - baudrate, - word_length, - trigger_value, - trigger_word - }; - send_message(&message); + const AFSKRxConfigureMessage message{ + baudrate, + word_length, + trigger_value, + trigger_word}; + send_message(&message); } void set_aprs(const uint32_t baudrate) { - const APRSRxConfigureMessage message { - baudrate - }; - send_message(&message); + const APRSRxConfigureMessage message{ + baudrate}; + send_message(&message); } void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { - const BTLERxConfigureMessage message { - baudrate, - word_length, - trigger_value, - trigger_word - }; - send_message(&message); + const BTLERxConfigureMessage message{ + baudrate, + word_length, + trigger_value, + trigger_word}; + send_message(&message); } void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { - const NRFRxConfigureMessage message { - baudrate, - word_length, - trigger_value, - trigger_word - }; - send_message(&message); -} - -void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, - const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) { - const AFSKTxConfigureMessage message { - afsk_samples_per_bit, - afsk_phase_inc_mark, - afsk_phase_inc_space, - afsk_repeat, - afsk_bw, - symbol_count - }; - send_message(&message); + const NRFRxConfigureMessage message{ + baudrate, + word_length, + trigger_value, + trigger_word}; + send_message(&message); +} + +void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) { + const AFSKTxConfigureMessage message{ + afsk_samples_per_bit, + afsk_phase_inc_mark, + afsk_phase_inc_space, + afsk_repeat, + afsk_bw, + symbol_count}; + send_message(&message); } void kill_afsk() { - const AFSKTxConfigureMessage message { - 0, - 0, - 0, - 0, - 0, - false - }; - send_message(&message); -} - -void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain, - uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled, - const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled) { - const AudioTXConfigMessage message { - divider, - deviation_hz, - audio_gain, - audio_shift_bits_s16, - tone_key_delta, - (float)persistent_memory::tone_mix() / 100.0f, - am_enabled, - dsb_enabled, - usb_enabled, - lsb_enabled - }; - send_message(&message); -} - -void set_fifo_data(const int8_t * data) { - const FIFODataMessage message { - data - }; - send_message(&message); + const AFSKTxConfigureMessage message{ + 0, + 0, + 0, + 0, + 0, + false}; + send_message(&message); +} + +void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain, uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled) { + const AudioTXConfigMessage message{ + divider, + deviation_hz, + audio_gain, + audio_shift_bits_s16, + tone_key_delta, + (float)persistent_memory::tone_mix() / 100.0f, + am_enabled, + dsb_enabled, + usb_enabled, + lsb_enabled}; + send_message(&message); +} + +void set_fifo_data(const int8_t* data) { + const FIFODataMessage message{ + data}; + send_message(&message); } void set_pitch_rssi(int32_t avg, bool enabled) { - const PitchRSSIConfigureMessage message { - enabled, - avg - }; - send_message(&message); + const PitchRSSIConfigureMessage message{ + enabled, + avg}; + send_message(&message); } -void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, - const uint32_t pause_symbols, const uint8_t de_bruijn_length) { - const OOKConfigureMessage message { - stream_length, - samples_per_bit, - repeat, - pause_symbols, - de_bruijn_length - }; - send_message(&message); +void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols, const uint8_t de_bruijn_length) { + const OOKConfigureMessage message{ + stream_length, + samples_per_bit, + repeat, + pause_symbols, + de_bruijn_length}; + send_message(&message); } void kill_ook() { - const OOKConfigureMessage message { - 0, - 0, - 0, - 0, - 0 - }; - send_message(&message); -} - -void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, - const uint32_t progress_notice) { - const FSKConfigureMessage message { - stream_length, - samples_per_bit, - shift, - progress_notice - }; - send_message(&message); + const OOKConfigureMessage message{ + 0, + 0, + 0, + 0, + 0}; + send_message(&message); +} + +void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, const uint32_t progress_notice) { + const FSKConfigureMessage message{ + stream_length, + samples_per_bit, + shift, + progress_notice}; + send_message(&message); } void set_pocsag() { - const POCSAGConfigureMessage message {}; - send_message(&message); + const POCSAGConfigureMessage message{}; + send_message(&message); } void set_adsb() { - const ADSBConfigureMessage message { - 1 - }; - send_message(&message); + const ADSBConfigureMessage message{ + 1}; + send_message(&message); } void set_jammer(const bool run, const jammer::JammerType type, const uint32_t speed) { - const JammerConfigureMessage message { - run, - type, - speed - }; - send_message(&message); + const JammerConfigureMessage message{ + run, + type, + speed}; + send_message(&message); } void set_rds_data(const uint16_t message_length) { - const RDSConfigureMessage message { - message_length - }; - send_message(&message); + const RDSConfigureMessage message{ + message_length}; + send_message(&message); } void set_spectrum(const size_t sampling_rate, const size_t trigger) { - const WidebandSpectrumConfigMessage message { - sampling_rate, trigger - }; - send_message(&message); + const WidebandSpectrumConfigMessage message{ + sampling_rate, trigger}; + send_message(&message); } void set_siggen_tone(const uint32_t tone) { - const SigGenToneMessage message { - TONES_F2D(tone, TONES_SAMPLERATE) - }; - send_message(&message); + const SigGenToneMessage message{ + TONES_F2D(tone, TONES_SAMPLERATE)}; + send_message(&message); } void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration) { - const SigGenConfigMessage message { - bw, shape, duration * TONES_SAMPLERATE - }; - send_message(&message); + const SigGenConfigMessage message{ + bw, shape, duration * TONES_SAMPLERATE}; + send_message(&message); } -void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw){ - SpectrumPainterBufferConfigureRequestMessage message { width, height, update, bw }; - send_message(&message); +void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw) { + SpectrumPainterBufferConfigureRequestMessage message{width, height, update, bw}; + send_message(&message); } static bool baseband_image_running = false; void run_image(const portapack::spi_flash::image_tag_t image_tag) { - if( baseband_image_running ) { - chDbgPanic("BBRunning"); - } + if (baseband_image_running) { + chDbgPanic("BBRunning"); + } - creg::m4txevent::clear(); + creg::m4txevent::clear(); - m4_init(image_tag, portapack::memory::map::m4_code, false); - baseband_image_running = true; + m4_init(image_tag, portapack::memory::map::m4_code, false); + baseband_image_running = true; - creg::m4txevent::enable(); + creg::m4txevent::enable(); } void shutdown() { - if( !baseband_image_running ) { - return; - } + if (!baseband_image_running) { + return; + } - creg::m4txevent::disable(); + creg::m4txevent::disable(); - ShutdownMessage message; - send_message(&message); + ShutdownMessage message; + send_message(&message); - shared_memory.application_queue.reset(); + shared_memory.application_queue.reset(); - baseband_image_running = false; + baseband_image_running = false; } void spectrum_streaming_start() { - SpectrumStreamingConfigMessage message { - SpectrumStreamingConfigMessage::Mode::Running - }; - send_message(&message); + SpectrumStreamingConfigMessage message{ + SpectrumStreamingConfigMessage::Mode::Running}; + send_message(&message); } void spectrum_streaming_stop() { - SpectrumStreamingConfigMessage message { - SpectrumStreamingConfigMessage::Mode::Stopped - }; - send_message(&message); + SpectrumStreamingConfigMessage message{ + SpectrumStreamingConfigMessage::Mode::Stopped}; + send_message(&message); } void set_sample_rate(const uint32_t sample_rate) { - SamplerateConfigMessage message { sample_rate }; - send_message(&message); + SamplerateConfigMessage message{sample_rate}; + send_message(&message); } void capture_start(CaptureConfig* const config) { - CaptureConfigMessage message { config }; - send_message(&message); + CaptureConfigMessage message{config}; + send_message(&message); } void capture_stop() { - CaptureConfigMessage message { nullptr }; - send_message(&message); + CaptureConfigMessage message{nullptr}; + send_message(&message); } void replay_start(ReplayConfig* const config) { - ReplayConfigMessage message { config }; - send_message(&message); + ReplayConfigMessage message{config}; + send_message(&message); } void replay_stop() { - ReplayConfigMessage message { nullptr }; - send_message(&message); + ReplayConfigMessage message{nullptr}; + send_message(&message); } void request_beep() { - RequestSignalMessage message { RequestSignalMessage::Signal::BeepRequest }; - send_message(&message); + RequestSignalMessage message{RequestSignalMessage::Signal::BeepRequest}; + send_message(&message); } } /* namespace baseband */ diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 71887af0a..d9c1179d3 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -36,41 +36,37 @@ namespace baseband { struct AMConfig { - const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k - const fir_taps_complex<64> channel; - const AMConfigureMessage::Modulation modulation; + const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k + const fir_taps_complex<64> channel; + const AMConfigureMessage::Modulation modulation; - void apply() const; + void apply() const; }; struct NBFMConfig { - const fir_taps_real<24> decim_0; - const fir_taps_real<32> decim_1; - const fir_taps_real<32> channel; - const size_t deviation; + const fir_taps_real<24> decim_0; + const fir_taps_real<32> decim_1; + const fir_taps_real<32> channel; + const size_t deviation; - void apply(const uint8_t squelch_level) const; + void apply(const uint8_t squelch_level) const; }; struct WFMConfig { - const fir_taps_real<24> decim_0; // To handle both WFM filters , 200k and 40K for NOAA APT - const fir_taps_real<16> decim_1; + const fir_taps_real<24> decim_0; // To handle both WFM filters , 200k and 40K for NOAA APT + const fir_taps_real<16> decim_1; - void apply() const; + void apply() const; }; void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration); -void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, - const bool dual_tone, const bool audio_out); +void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, const bool dual_tone, const bool audio_out); void kill_tone(); void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration); -void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain, - uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled, - const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled); -void set_fifo_data(const int8_t * data); +void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain, uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled); +void set_fifo_data(const int8_t* data); void set_pitch_rssi(int32_t avg, bool enabled); -void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, - const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); +void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); void kill_afsk(); void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); void set_aprs(const uint32_t baudrate); @@ -79,11 +75,9 @@ void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); -void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, - const uint32_t pause_symbols, const uint8_t de_bruijn_length = 0); +void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols, const uint8_t de_bruijn_length = 0); void kill_ook(); -void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, - const uint32_t progress_notice); +void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, const uint32_t progress_notice); void set_pocsag(); void set_adsb(); void set_jammer(const bool run, const jammer::JammerType type, const uint32_t speed); @@ -108,4 +102,4 @@ void replay_stop(); } /* namespace baseband */ -#endif/*__BASEBAND_API_H__*/ +#endif /*__BASEBAND_API_H__*/ diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 96d1a6783..22757bbc7 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -30,2630 +30,5317 @@ namespace ui { static constexpr uint8_t bitmap_icon_receivers_data[] = { - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x98, 0x19, - 0xB0, 0x0D, - 0xE0, 0x07, - 0xC0, 0x03, - 0x83, 0xC1, - 0x03, 0xC0, - 0xFF, 0xFF, - 0xFF, 0xFF, -}; -static constexpr Bitmap bitmap_icon_receivers { - { 16, 16 }, bitmap_icon_receivers_data -}; + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x98, + 0x19, + 0xB0, + 0x0D, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0x83, + 0xC1, + 0x03, + 0xC0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, +}; +static constexpr Bitmap bitmap_icon_receivers{ + {16, 16}, + bitmap_icon_receivers_data}; static constexpr uint8_t bitmap_icon_utilities_data[] = { - 0x30, 0x24, - 0x78, 0x66, - 0x78, 0x66, - 0x78, 0x7E, - 0x78, 0x3C, - 0x78, 0x18, - 0x78, 0x18, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x3C, - 0x30, 0x18, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_utilities { - { 16, 16 }, bitmap_icon_utilities_data -}; + 0x30, + 0x24, + 0x78, + 0x66, + 0x78, + 0x66, + 0x78, + 0x7E, + 0x78, + 0x3C, + 0x78, + 0x18, + 0x78, + 0x18, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x3C, + 0x30, + 0x18, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_utilities{ + {16, 16}, + bitmap_icon_utilities_data}; static constexpr uint8_t bitmap_stripes_data[] = { - 0xFF, 0x03, 0xC0, - 0xFF, 0x01, 0xE0, - 0xFF, 0x00, 0xF0, - 0x7F, 0x00, 0xF8, - 0x3F, 0x00, 0xFC, - 0x1F, 0x00, 0xFE, - 0x0F, 0x00, 0xFF, - 0x07, 0x80, 0xFF, -}; -static constexpr Bitmap bitmap_stripes { - { 24, 8 }, bitmap_stripes_data -}; + 0xFF, + 0x03, + 0xC0, + 0xFF, + 0x01, + 0xE0, + 0xFF, + 0x00, + 0xF0, + 0x7F, + 0x00, + 0xF8, + 0x3F, + 0x00, + 0xFC, + 0x1F, + 0x00, + 0xFE, + 0x0F, + 0x00, + 0xFF, + 0x07, + 0x80, + 0xFF, +}; +static constexpr Bitmap bitmap_stripes{ + {24, 8}, + bitmap_stripes_data}; static constexpr uint8_t bitmap_icon_sstv_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xFE, 0x7F, - 0x03, 0xC0, - 0x53, 0xD5, - 0xAB, 0xCA, - 0x53, 0xD5, - 0xAB, 0xCA, - 0x53, 0xD5, - 0xAB, 0xCA, - 0x53, 0xD5, - 0x03, 0xC0, - 0xFF, 0xFF, - 0xFB, 0xD7, - 0xFE, 0x7F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_sstv { - { 16, 16 }, bitmap_icon_sstv_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x7F, + 0x03, + 0xC0, + 0x53, + 0xD5, + 0xAB, + 0xCA, + 0x53, + 0xD5, + 0xAB, + 0xCA, + 0x53, + 0xD5, + 0xAB, + 0xCA, + 0x53, + 0xD5, + 0x03, + 0xC0, + 0xFF, + 0xFF, + 0xFB, + 0xD7, + 0xFE, + 0x7F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_sstv{ + {16, 16}, + bitmap_icon_sstv_data}; static constexpr uint8_t bitmap_icon_morse_data[] = { - 0x00, 0x00, - 0xFE, 0x7F, - 0xFF, 0xFF, - 0xBB, 0xD0, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x0B, 0xE1, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xEB, 0xD0, - 0xFF, 0xFF, - 0xFE, 0x7F, - 0x70, 0x00, - 0x30, 0x00, - 0x10, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_morse { - { 16, 16 }, bitmap_icon_morse_data -}; + 0x00, + 0x00, + 0xFE, + 0x7F, + 0xFF, + 0xFF, + 0xBB, + 0xD0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x0B, + 0xE1, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xEB, + 0xD0, + 0xFF, + 0xFF, + 0xFE, + 0x7F, + 0x70, + 0x00, + 0x30, + 0x00, + 0x10, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_morse{ + {16, 16}, + bitmap_icon_morse_data}; static constexpr uint8_t bitmap_icon_file_wav_data[] = { - 0xFC, 0x03, - 0x04, 0x06, - 0x04, 0x0E, - 0x04, 0x1E, - 0x04, 0x3E, - 0x84, 0x20, - 0xC4, 0x22, - 0xF4, 0x20, - 0xF4, 0x2E, - 0xF4, 0x20, - 0xC4, 0x22, - 0x84, 0x24, - 0x04, 0x28, - 0x04, 0x20, - 0x04, 0x20, - 0xFC, 0x3F, -}; -static constexpr Bitmap bitmap_icon_file_wav { - { 16, 16 }, bitmap_icon_file_wav_data -}; + 0xFC, + 0x03, + 0x04, + 0x06, + 0x04, + 0x0E, + 0x04, + 0x1E, + 0x04, + 0x3E, + 0x84, + 0x20, + 0xC4, + 0x22, + 0xF4, + 0x20, + 0xF4, + 0x2E, + 0xF4, + 0x20, + 0xC4, + 0x22, + 0x84, + 0x24, + 0x04, + 0x28, + 0x04, + 0x20, + 0x04, + 0x20, + 0xFC, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_file_wav{ + {16, 16}, + bitmap_icon_file_wav_data}; static constexpr uint8_t bitmap_icon_tetra_data[] = { - 0xE0, 0x0F, - 0x18, 0x38, - 0xE4, 0x67, - 0x7E, 0xCE, - 0xC7, 0xCC, - 0x00, 0x00, - 0xFF, 0x4F, - 0xBA, 0xB2, - 0x9A, 0xEE, - 0xBA, 0xB2, - 0x00, 0x00, - 0x3B, 0xE3, - 0x73, 0x7E, - 0xC6, 0x27, - 0x1C, 0x18, - 0xF0, 0x07, -}; -static constexpr Bitmap bitmap_icon_tetra { - { 16, 16 }, bitmap_icon_tetra_data -}; + 0xE0, + 0x0F, + 0x18, + 0x38, + 0xE4, + 0x67, + 0x7E, + 0xCE, + 0xC7, + 0xCC, + 0x00, + 0x00, + 0xFF, + 0x4F, + 0xBA, + 0xB2, + 0x9A, + 0xEE, + 0xBA, + 0xB2, + 0x00, + 0x00, + 0x3B, + 0xE3, + 0x73, + 0x7E, + 0xC6, + 0x27, + 0x1C, + 0x18, + 0xF0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_tetra{ + {16, 16}, + bitmap_icon_tetra_data}; static constexpr uint8_t bitmap_icon_debug_data[] = { - 0xFE, 0x03, - 0x02, 0x07, - 0x2A, 0x0D, - 0x52, 0x0F, - 0x2A, 0x08, - 0x52, 0x09, - 0xAA, 0x0A, - 0x52, 0x09, - 0xAA, 0x0A, - 0x52, 0x01, - 0xAA, 0x12, - 0x02, 0x08, - 0x02, 0xFC, - 0x02, 0x08, - 0xFE, 0x13, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_debug { - { 16, 16 }, bitmap_icon_debug_data -}; + 0xFE, + 0x03, + 0x02, + 0x07, + 0x2A, + 0x0D, + 0x52, + 0x0F, + 0x2A, + 0x08, + 0x52, + 0x09, + 0xAA, + 0x0A, + 0x52, + 0x09, + 0xAA, + 0x0A, + 0x52, + 0x01, + 0xAA, + 0x12, + 0x02, + 0x08, + 0x02, + 0xFC, + 0x02, + 0x08, + 0xFE, + 0x13, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_debug{ + {16, 16}, + bitmap_icon_debug_data}; static constexpr uint8_t bitmap_icon_downconvert_data[] = { - 0x00, 0x00, - 0x77, 0x77, - 0x51, 0x51, - 0x33, 0x53, - 0x51, 0x51, - 0x51, 0x77, - 0x00, 0x80, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0xF8, 0x1F, - 0xF0, 0x0F, - 0xE0, 0x07, - 0xC0, 0x03, - 0x80, 0x01, -}; -static constexpr Bitmap bitmap_icon_downconvert { - { 16, 16 }, bitmap_icon_downconvert_data -}; + 0x00, + 0x00, + 0x77, + 0x77, + 0x51, + 0x51, + 0x33, + 0x53, + 0x51, + 0x51, + 0x51, + 0x77, + 0x00, + 0x80, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0xF8, + 0x1F, + 0xF0, + 0x0F, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0x80, + 0x01, +}; +static constexpr Bitmap bitmap_icon_downconvert{ + {16, 16}, + bitmap_icon_downconvert_data}; static constexpr uint8_t bitmap_more_data[] = { - 0x10, - 0x10, - 0x10, - 0x10, - 0x54, - 0x38, - 0x10, - 0x00, -}; -static constexpr Bitmap bitmap_more { - { 8, 8 }, bitmap_more_data -}; + 0x10, + 0x10, + 0x10, + 0x10, + 0x54, + 0x38, + 0x10, + 0x00, +}; +static constexpr Bitmap bitmap_more{ + {8, 8}, + bitmap_more_data}; static constexpr uint8_t bitmap_icon_nuoptix_data[] = { - 0x80, 0x01, - 0x80, 0x01, - 0x40, 0x02, - 0x40, 0x1A, - 0x40, 0x1A, - 0x20, 0x0C, - 0x20, 0x0F, - 0x20, 0x1E, - 0x10, 0x0E, - 0x10, 0x0B, - 0x10, 0x0B, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xFC, 0x3F, - 0xFC, 0x3F, -}; -static constexpr Bitmap bitmap_icon_nuoptix { - { 16, 16 }, bitmap_icon_nuoptix_data -}; + 0x80, + 0x01, + 0x80, + 0x01, + 0x40, + 0x02, + 0x40, + 0x1A, + 0x40, + 0x1A, + 0x20, + 0x0C, + 0x20, + 0x0F, + 0x20, + 0x1E, + 0x10, + 0x0E, + 0x10, + 0x0B, + 0x10, + 0x0B, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_nuoptix{ + {16, 16}, + bitmap_icon_nuoptix_data}; static constexpr uint8_t bitmap_icon_search_data[] = { - 0xF8, 0x01, - 0xFC, 0x03, - 0x0E, 0x07, - 0x07, 0x0E, - 0x03, 0x0C, - 0x0B, 0x0C, - 0x0B, 0x0C, - 0x13, 0x0C, - 0x07, 0x0E, - 0x0E, 0x07, - 0xFC, 0x1F, - 0xF8, 0x3D, - 0x00, 0x7C, - 0x00, 0xF8, - 0x00, 0xF0, - 0x00, 0x60, -}; -static constexpr Bitmap bitmap_icon_search { - { 16, 16 }, bitmap_icon_search_data -}; + 0xF8, + 0x01, + 0xFC, + 0x03, + 0x0E, + 0x07, + 0x07, + 0x0E, + 0x03, + 0x0C, + 0x0B, + 0x0C, + 0x0B, + 0x0C, + 0x13, + 0x0C, + 0x07, + 0x0E, + 0x0E, + 0x07, + 0xFC, + 0x1F, + 0xF8, + 0x3D, + 0x00, + 0x7C, + 0x00, + 0xF8, + 0x00, + 0xF0, + 0x00, + 0x60, +}; +static constexpr Bitmap bitmap_icon_search{ + {16, 16}, + bitmap_icon_search_data}; static constexpr uint8_t bitmap_icon_biast_on_data[] = { - 0x00, 0x00, - 0xFE, 0x7F, - 0x00, 0x04, - 0x00, 0x08, - 0x20, 0x10, - 0x20, 0x08, - 0x30, 0x04, - 0x30, 0x08, - 0xF8, 0x10, - 0x60, 0x08, - 0x60, 0x04, - 0x20, 0x08, - 0x20, 0x10, - 0x00, 0x08, - 0x00, 0x04, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_biast_on { - { 16, 16 }, bitmap_icon_biast_on_data -}; + 0x00, + 0x00, + 0xFE, + 0x7F, + 0x00, + 0x04, + 0x00, + 0x08, + 0x20, + 0x10, + 0x20, + 0x08, + 0x30, + 0x04, + 0x30, + 0x08, + 0xF8, + 0x10, + 0x60, + 0x08, + 0x60, + 0x04, + 0x20, + 0x08, + 0x20, + 0x10, + 0x00, + 0x08, + 0x00, + 0x04, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_biast_on{ + {16, 16}, + bitmap_icon_biast_on_data}; static constexpr uint8_t bitmap_icon_cwgen_data[] = { - 0x18, 0x00, - 0x24, 0x00, - 0x42, 0x00, - 0x42, 0x00, - 0x42, 0x00, - 0x42, 0x00, - 0x81, 0x00, - 0xAB, 0x6A, - 0x80, 0x40, - 0x00, 0x21, - 0x00, 0x21, - 0x00, 0x21, - 0x00, 0x21, - 0x00, 0x12, - 0x00, 0x0C, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_cwgen { - { 16, 16 }, bitmap_icon_cwgen_data -}; + 0x18, + 0x00, + 0x24, + 0x00, + 0x42, + 0x00, + 0x42, + 0x00, + 0x42, + 0x00, + 0x42, + 0x00, + 0x81, + 0x00, + 0xAB, + 0x6A, + 0x80, + 0x40, + 0x00, + 0x21, + 0x00, + 0x21, + 0x00, + 0x21, + 0x00, + 0x21, + 0x00, + 0x12, + 0x00, + 0x0C, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_cwgen{ + {16, 16}, + bitmap_icon_cwgen_data}; static constexpr uint8_t bitmap_icon_aprs_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xF0, 0x0F, - 0x4C, 0x32, - 0xFE, 0x7F, - 0x25, 0xA4, - 0x25, 0xA4, - 0xFF, 0xFF, - 0x25, 0xA4, - 0x25, 0xA4, - 0xFE, 0x7F, - 0x4C, 0x32, - 0xF0, 0x0F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_aprs { - { 16, 16 }, bitmap_icon_aprs_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF0, + 0x0F, + 0x4C, + 0x32, + 0xFE, + 0x7F, + 0x25, + 0xA4, + 0x25, + 0xA4, + 0xFF, + 0xFF, + 0x25, + 0xA4, + 0x25, + 0xA4, + 0xFE, + 0x7F, + 0x4C, + 0x32, + 0xF0, + 0x0F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_aprs{ + {16, 16}, + bitmap_icon_aprs_data}; static constexpr uint8_t bitmap_icon_burger_data[] = { - 0x00, 0x00, - 0xE0, 0x07, - 0xF8, 0x1F, - 0xFC, 0x3F, - 0xFE, 0x7F, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x00, 0x00, - 0x55, 0x55, - 0xAA, 0xAA, - 0x55, 0x55, - 0x00, 0x00, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFE, 0x7F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_burger { - { 16, 16 }, bitmap_icon_burger_data -}; + 0x00, + 0x00, + 0xE0, + 0x07, + 0xF8, + 0x1F, + 0xFC, + 0x3F, + 0xFE, + 0x7F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x00, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0x55, + 0x55, + 0x00, + 0x00, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFE, + 0x7F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_burger{ + {16, 16}, + bitmap_icon_burger_data}; static constexpr uint8_t bitmap_sig_tri_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xC0, 0x00, - 0x00, 0x03, 0xC0, 0x00, - 0x80, 0x07, 0xE0, 0x01, - 0x80, 0x07, 0xE0, 0x01, - 0xC0, 0x0C, 0x30, 0x03, - 0xC0, 0x0C, 0x30, 0x03, - 0x60, 0x18, 0x18, 0x06, - 0x60, 0x18, 0x18, 0x06, - 0x30, 0x30, 0x0C, 0x0C, - 0x30, 0x30, 0x0C, 0x0C, - 0x18, 0x60, 0x06, 0x18, - 0x18, 0x60, 0x06, 0x18, - 0x0E, 0xC0, 0x03, 0x70, - 0x0E, 0xC0, 0x03, 0x70, - 0x06, 0x80, 0x01, 0x60, - 0x06, 0x80, 0x01, 0x60, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_tri { - { 32, 32 }, bitmap_sig_tri_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0xC0, + 0x00, + 0x00, + 0x03, + 0xC0, + 0x00, + 0x80, + 0x07, + 0xE0, + 0x01, + 0x80, + 0x07, + 0xE0, + 0x01, + 0xC0, + 0x0C, + 0x30, + 0x03, + 0xC0, + 0x0C, + 0x30, + 0x03, + 0x60, + 0x18, + 0x18, + 0x06, + 0x60, + 0x18, + 0x18, + 0x06, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x18, + 0x60, + 0x06, + 0x18, + 0x18, + 0x60, + 0x06, + 0x18, + 0x0E, + 0xC0, + 0x03, + 0x70, + 0x0E, + 0xC0, + 0x03, + 0x70, + 0x06, + 0x80, + 0x01, + 0x60, + 0x06, + 0x80, + 0x01, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_tri{ + {32, 32}, + bitmap_sig_tri_data}; static constexpr uint8_t bitmap_sig_sine_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xC0, 0x00, - 0x80, 0x07, 0xE0, 0x01, - 0xC0, 0x0F, 0xF0, 0x03, - 0xC0, 0x0C, 0x30, 0x03, - 0x60, 0x18, 0x18, 0x06, - 0x60, 0x18, 0x18, 0x06, - 0x60, 0x18, 0x18, 0x06, - 0x60, 0x18, 0x18, 0x06, - 0x30, 0x30, 0x0C, 0x0C, - 0x30, 0x30, 0x0C, 0x0C, - 0x30, 0x30, 0x0C, 0x0C, - 0x30, 0x30, 0x0C, 0x0C, - 0x18, 0x60, 0x06, 0x18, - 0x1E, 0xE0, 0x07, 0x78, - 0x0E, 0xC0, 0x03, 0x70, - 0x06, 0x80, 0x01, 0x60, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_sine { - { 32, 32 }, bitmap_sig_sine_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0xC0, + 0x00, + 0x80, + 0x07, + 0xE0, + 0x01, + 0xC0, + 0x0F, + 0xF0, + 0x03, + 0xC0, + 0x0C, + 0x30, + 0x03, + 0x60, + 0x18, + 0x18, + 0x06, + 0x60, + 0x18, + 0x18, + 0x06, + 0x60, + 0x18, + 0x18, + 0x06, + 0x60, + 0x18, + 0x18, + 0x06, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x30, + 0x30, + 0x0C, + 0x0C, + 0x18, + 0x60, + 0x06, + 0x18, + 0x1E, + 0xE0, + 0x07, + 0x78, + 0x0E, + 0xC0, + 0x03, + 0x70, + 0x06, + 0x80, + 0x01, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_sine{ + {32, 32}, + bitmap_sig_sine_data}; static constexpr uint8_t bitmap_icon_biast_off_data[] = { - 0x00, 0x00, - 0xFE, 0x7F, - 0x00, 0x04, - 0x00, 0x08, - 0x00, 0x10, - 0x00, 0x08, - 0x88, 0x04, - 0x50, 0x08, - 0x20, 0x10, - 0x50, 0x08, - 0x88, 0x04, - 0x00, 0x08, - 0x00, 0x10, - 0x00, 0x08, - 0x00, 0x04, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_biast_off { - { 16, 16 }, bitmap_icon_biast_off_data -}; + 0x00, + 0x00, + 0xFE, + 0x7F, + 0x00, + 0x04, + 0x00, + 0x08, + 0x00, + 0x10, + 0x00, + 0x08, + 0x88, + 0x04, + 0x50, + 0x08, + 0x20, + 0x10, + 0x50, + 0x08, + 0x88, + 0x04, + 0x00, + 0x08, + 0x00, + 0x10, + 0x00, + 0x08, + 0x00, + 0x04, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_biast_off{ + {16, 16}, + bitmap_icon_biast_off_data}; static constexpr uint8_t bitmap_icon_previous_data[] = { - 0x00, 0x00, - 0xC0, 0x00, - 0xE0, 0x00, - 0x70, 0x00, - 0x38, 0x00, - 0x1C, 0x00, - 0x0E, 0x00, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x0E, 0x00, - 0x1C, 0x00, - 0x38, 0x00, - 0x70, 0x00, - 0xE0, 0x00, - 0xC0, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_previous { - { 16, 16 }, bitmap_icon_previous_data -}; + 0x00, + 0x00, + 0xC0, + 0x00, + 0xE0, + 0x00, + 0x70, + 0x00, + 0x38, + 0x00, + 0x1C, + 0x00, + 0x0E, + 0x00, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x0E, + 0x00, + 0x1C, + 0x00, + 0x38, + 0x00, + 0x70, + 0x00, + 0xE0, + 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_previous{ + {16, 16}, + bitmap_icon_previous_data}; static constexpr uint8_t bitmap_icon_tools_antenna_data[] = { - 0x38, 0x3E, - 0x10, 0x22, - 0x10, 0x26, - 0x10, 0x22, - 0x10, 0x2E, - 0x10, 0x22, - 0x10, 0x26, - 0x10, 0x22, - 0x38, 0x2E, - 0x38, 0x22, - 0x38, 0x26, - 0x38, 0x22, - 0x38, 0x2E, - 0x38, 0x22, - 0x38, 0x3E, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_tools_antenna { - { 16, 16 }, bitmap_icon_tools_antenna_data -}; + 0x38, + 0x3E, + 0x10, + 0x22, + 0x10, + 0x26, + 0x10, + 0x22, + 0x10, + 0x2E, + 0x10, + 0x22, + 0x10, + 0x26, + 0x10, + 0x22, + 0x38, + 0x2E, + 0x38, + 0x22, + 0x38, + 0x26, + 0x38, + 0x22, + 0x38, + 0x2E, + 0x38, + 0x22, + 0x38, + 0x3E, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_tools_antenna{ + {16, 16}, + bitmap_icon_tools_antenna_data}; static constexpr uint8_t bitmap_icon_new_file_data[] = { - 0x00, 0x00, - 0xFC, 0x07, - 0x04, 0x0C, - 0x04, 0x1C, - 0x04, 0x3C, - 0x84, 0x21, - 0x84, 0x21, - 0x84, 0x21, - 0xF4, 0x2F, - 0xF4, 0x2F, - 0x84, 0x21, - 0x84, 0x21, - 0x84, 0x21, - 0x04, 0x20, - 0xFC, 0x3F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_new_file { - { 16, 16 }, bitmap_icon_new_file_data -}; + 0x00, + 0x00, + 0xFC, + 0x07, + 0x04, + 0x0C, + 0x04, + 0x1C, + 0x04, + 0x3C, + 0x84, + 0x21, + 0x84, + 0x21, + 0x84, + 0x21, + 0xF4, + 0x2F, + 0xF4, + 0x2F, + 0x84, + 0x21, + 0x84, + 0x21, + 0x84, + 0x21, + 0x04, + 0x20, + 0xFC, + 0x3F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_new_file{ + {16, 16}, + bitmap_icon_new_file_data}; static constexpr uint8_t bitmap_sig_cw_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFE, 0xFF, 0xFF, 0x7F, - 0xFE, 0xFF, 0xFF, 0x7F, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_cw { - { 32, 32 }, bitmap_sig_cw_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0xFF, + 0xFF, + 0x7F, + 0xFE, + 0xFF, + 0xFF, + 0x7F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_cw{ + {32, 32}, + bitmap_sig_cw_data}; static constexpr uint8_t bitmap_icon_tpms_data[] = { - 0xC0, 0x03, - 0xF0, 0x0F, - 0x18, 0x18, - 0xEC, 0x37, - 0x36, 0x6C, - 0x1A, 0x58, - 0x0B, 0xD0, - 0x0B, 0xD0, - 0x0B, 0xD0, - 0x0B, 0xD0, - 0x1A, 0x58, - 0x36, 0x6C, - 0xEC, 0x37, - 0x18, 0x18, - 0xF0, 0x0F, - 0xC0, 0x03, -}; -static constexpr Bitmap bitmap_icon_tpms { - { 16, 16 }, bitmap_icon_tpms_data -}; + 0xC0, + 0x03, + 0xF0, + 0x0F, + 0x18, + 0x18, + 0xEC, + 0x37, + 0x36, + 0x6C, + 0x1A, + 0x58, + 0x0B, + 0xD0, + 0x0B, + 0xD0, + 0x0B, + 0xD0, + 0x0B, + 0xD0, + 0x1A, + 0x58, + 0x36, + 0x6C, + 0xEC, + 0x37, + 0x18, + 0x18, + 0xF0, + 0x0F, + 0xC0, + 0x03, +}; +static constexpr Bitmap bitmap_icon_tpms{ + {16, 16}, + bitmap_icon_tpms_data}; static constexpr uint8_t bitmap_rssipwm_data[] = { - 0x00, 0x00, 0x00, - 0x8F, 0xE7, 0x7D, - 0x51, 0x10, 0x10, - 0x51, 0x10, 0x10, - 0x8F, 0xE3, 0x10, - 0x09, 0x04, 0x11, - 0x11, 0x04, 0x11, - 0xD1, 0xF3, 0x7C, - 0x00, 0x00, 0x00, - 0x3F, 0x1E, 0x1E, - 0x21, 0x12, 0x12, - 0x21, 0x12, 0x12, - 0x21, 0x12, 0x12, - 0x21, 0x12, 0x12, - 0x21, 0x12, 0x12, - 0xE1, 0xF3, 0x73, -}; -static constexpr Bitmap bitmap_rssipwm { - { 24, 16 }, bitmap_rssipwm_data -}; + 0x00, + 0x00, + 0x00, + 0x8F, + 0xE7, + 0x7D, + 0x51, + 0x10, + 0x10, + 0x51, + 0x10, + 0x10, + 0x8F, + 0xE3, + 0x10, + 0x09, + 0x04, + 0x11, + 0x11, + 0x04, + 0x11, + 0xD1, + 0xF3, + 0x7C, + 0x00, + 0x00, + 0x00, + 0x3F, + 0x1E, + 0x1E, + 0x21, + 0x12, + 0x12, + 0x21, + 0x12, + 0x12, + 0x21, + 0x12, + 0x12, + 0x21, + 0x12, + 0x12, + 0x21, + 0x12, + 0x12, + 0xE1, + 0xF3, + 0x73, +}; +static constexpr Bitmap bitmap_rssipwm{ + {24, 16}, + bitmap_rssipwm_data}; static constexpr uint8_t bitmap_icon_stealth_data[] = { - 0x00, 0x00, - 0xC0, 0x03, - 0xE0, 0x07, - 0xE0, 0x07, - 0xF8, 0x1F, - 0x00, 0x00, - 0xE0, 0x07, - 0x60, 0x06, - 0x00, 0x00, - 0xF0, 0x0F, - 0xF0, 0x0F, - 0xF8, 0x1E, - 0xFC, 0x3F, - 0xFC, 0x3E, - 0xFC, 0x3F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_stealth { - { 16, 16 }, bitmap_icon_stealth_data -}; + 0x00, + 0x00, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xE0, + 0x07, + 0xF8, + 0x1F, + 0x00, + 0x00, + 0xE0, + 0x07, + 0x60, + 0x06, + 0x00, + 0x00, + 0xF0, + 0x0F, + 0xF0, + 0x0F, + 0xF8, + 0x1E, + 0xFC, + 0x3F, + 0xFC, + 0x3E, + 0xFC, + 0x3F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_stealth{ + {16, 16}, + bitmap_icon_stealth_data}; static constexpr uint8_t bitmap_sig_square_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xFE, 0x83, 0xFF, 0x60, - 0xFE, 0x83, 0xFF, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0x83, 0xC1, 0x60, - 0x06, 0xFF, 0xC1, 0x7F, - 0x06, 0xFF, 0xC1, 0x7F, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_square { - { 32, 32 }, bitmap_sig_square_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x83, + 0xFF, + 0x60, + 0xFE, + 0x83, + 0xFF, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0x83, + 0xC1, + 0x60, + 0x06, + 0xFF, + 0xC1, + 0x7F, + 0x06, + 0xFF, + 0xC1, + 0x7F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_square{ + {32, 32}, + bitmap_sig_square_data}; static constexpr uint8_t bitmap_icon_options_ui_data[] = { - 0xFF, 0x1F, - 0xFF, 0x13, - 0xFF, 0x1F, - 0x01, 0x10, - 0x01, 0x10, - 0x01, 0x10, - 0x01, 0x04, - 0x01, 0x0C, - 0x01, 0x1C, - 0x01, 0x3C, - 0xFF, 0x7D, - 0x00, 0xFC, - 0x00, 0x34, - 0x00, 0x20, - 0x00, 0x60, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_options_ui { - { 16, 16 }, bitmap_icon_options_ui_data -}; + 0xFF, + 0x1F, + 0xFF, + 0x13, + 0xFF, + 0x1F, + 0x01, + 0x10, + 0x01, + 0x10, + 0x01, + 0x10, + 0x01, + 0x04, + 0x01, + 0x0C, + 0x01, + 0x1C, + 0x01, + 0x3C, + 0xFF, + 0x7D, + 0x00, + 0xFC, + 0x00, + 0x34, + 0x00, + 0x20, + 0x00, + 0x60, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_options_ui{ + {16, 16}, + bitmap_icon_options_ui_data}; static constexpr uint8_t bitmap_icon_jammer_data[] = { - 0xE0, 0x07, - 0xF8, 0x1F, - 0x1C, 0x38, - 0x0E, 0x78, - 0x06, 0x7C, - 0x03, 0xCE, - 0x03, 0xC7, - 0x83, 0xC3, - 0xC3, 0xC1, - 0xE3, 0xC0, - 0x73, 0xC0, - 0x3E, 0x60, - 0x1E, 0x70, - 0x1C, 0x38, - 0xF8, 0x1F, - 0xE0, 0x07, -}; -static constexpr Bitmap bitmap_icon_jammer { - { 16, 16 }, bitmap_icon_jammer_data -}; + 0xE0, + 0x07, + 0xF8, + 0x1F, + 0x1C, + 0x38, + 0x0E, + 0x78, + 0x06, + 0x7C, + 0x03, + 0xCE, + 0x03, + 0xC7, + 0x83, + 0xC3, + 0xC3, + 0xC1, + 0xE3, + 0xC0, + 0x73, + 0xC0, + 0x3E, + 0x60, + 0x1E, + 0x70, + 0x1C, + 0x38, + 0xF8, + 0x1F, + 0xE0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_jammer{ + {16, 16}, + bitmap_icon_jammer_data}; static constexpr uint8_t bitmap_icon_qr_code_data[] = { - 0x00, 0x00, - 0xFE, 0x7E, - 0xC6, 0x62, - 0xFA, 0x5A, - 0xFA, 0x5A, - 0xDA, 0x5A, - 0xFE, 0x7E, - 0x7E, 0x7E, - 0x00, 0x00, - 0xFE, 0x46, - 0xC2, 0x06, - 0xFA, 0x18, - 0xFA, 0x18, - 0xC6, 0x60, - 0xFE, 0x62, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_qr_code { - { 16, 16 }, bitmap_icon_qr_code_data -}; + 0x00, + 0x00, + 0xFE, + 0x7E, + 0xC6, + 0x62, + 0xFA, + 0x5A, + 0xFA, + 0x5A, + 0xDA, + 0x5A, + 0xFE, + 0x7E, + 0x7E, + 0x7E, + 0x00, + 0x00, + 0xFE, + 0x46, + 0xC2, + 0x06, + 0xFA, + 0x18, + 0xFA, + 0x18, + 0xC6, + 0x60, + 0xFE, + 0x62, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_qr_code{ + {16, 16}, + bitmap_icon_qr_code_data}; static constexpr uint8_t bitmap_icon_sonde_data[] = { - 0x80, 0x03, - 0xE0, 0x0F, - 0xE0, 0x0F, - 0xF0, 0x1F, - 0xF0, 0x1F, - 0xF0, 0x1F, - 0xE0, 0x0F, - 0xE0, 0x0F, - 0x00, 0x00, - 0x20, 0x09, - 0x00, 0x00, - 0x40, 0x05, - 0x00, 0x00, - 0xC0, 0x07, - 0xC0, 0x07, - 0xC0, 0x07, -}; -static constexpr Bitmap bitmap_icon_sonde { - { 16, 16 }, bitmap_icon_sonde_data -}; + 0x80, + 0x03, + 0xE0, + 0x0F, + 0xE0, + 0x0F, + 0xF0, + 0x1F, + 0xF0, + 0x1F, + 0xF0, + 0x1F, + 0xE0, + 0x0F, + 0xE0, + 0x0F, + 0x00, + 0x00, + 0x20, + 0x09, + 0x00, + 0x00, + 0x40, + 0x05, + 0x00, + 0x00, + 0xC0, + 0x07, + 0xC0, + 0x07, + 0xC0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_sonde{ + {16, 16}, + bitmap_icon_sonde_data}; static constexpr uint8_t bitmap_icon_codetx_data[] = { - 0x00, 0x00, - 0xF0, 0x07, - 0x0C, 0x18, - 0x03, 0x60, - 0xE0, 0x03, - 0x18, 0x0C, - 0x04, 0x10, - 0xC0, 0x01, - 0x20, 0x02, - 0x00, 0x00, - 0x00, 0x00, - 0xBB, 0x6D, - 0x2A, 0x49, - 0x2A, 0x49, - 0x3A, 0x49, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_codetx { - { 16, 16 }, bitmap_icon_codetx_data -}; + 0x00, + 0x00, + 0xF0, + 0x07, + 0x0C, + 0x18, + 0x03, + 0x60, + 0xE0, + 0x03, + 0x18, + 0x0C, + 0x04, + 0x10, + 0xC0, + 0x01, + 0x20, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0xBB, + 0x6D, + 0x2A, + 0x49, + 0x2A, + 0x49, + 0x3A, + 0x49, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_codetx{ + {16, 16}, + bitmap_icon_codetx_data}; static constexpr uint8_t bitmap_icon_scanner_data[] = { - 0x03, 0x00, - 0x00, 0x00, - 0x03, 0x00, - 0x00, 0x00, - 0x0F, 0x00, - 0x00, 0x00, - 0x03, 0x01, - 0x80, 0x01, - 0xC3, 0x00, - 0xE0, 0xFF, - 0xEF, 0xFF, - 0xC0, 0x00, - 0x83, 0x01, - 0x00, 0x01, - 0x03, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_scanner { - { 16, 16 }, bitmap_icon_scanner_data -}; + 0x03, + 0x00, + 0x00, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + 0x0F, + 0x00, + 0x00, + 0x00, + 0x03, + 0x01, + 0x80, + 0x01, + 0xC3, + 0x00, + 0xE0, + 0xFF, + 0xEF, + 0xFF, + 0xC0, + 0x00, + 0x83, + 0x01, + 0x00, + 0x01, + 0x03, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_scanner{ + {16, 16}, + bitmap_icon_scanner_data}; static constexpr uint8_t bitmap_icon_lge_data[] = { - 0x00, 0x00, - 0x80, 0x00, - 0xA4, 0x12, - 0xA8, 0x0A, - 0xD0, 0x05, - 0xEC, 0x1B, - 0xF0, 0x07, - 0xFE, 0xFF, - 0xF0, 0x07, - 0xEC, 0x1B, - 0xD0, 0x05, - 0xA8, 0x0A, - 0xA4, 0x12, - 0x80, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_lge { - { 16, 16 }, bitmap_icon_lge_data -}; + 0x00, + 0x00, + 0x80, + 0x00, + 0xA4, + 0x12, + 0xA8, + 0x0A, + 0xD0, + 0x05, + 0xEC, + 0x1B, + 0xF0, + 0x07, + 0xFE, + 0xFF, + 0xF0, + 0x07, + 0xEC, + 0x1B, + 0xD0, + 0x05, + 0xA8, + 0x0A, + 0xA4, + 0x12, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_lge{ + {16, 16}, + bitmap_icon_lge_data}; static constexpr uint8_t bitmap_icon_soundboard_data[] = { - 0xF0, 0x0F, - 0x1C, 0x18, - 0x17, 0x38, - 0x15, 0x78, - 0x15, 0xF8, - 0x15, 0x82, - 0x15, 0x8B, - 0xD5, 0x83, - 0xD5, 0xBB, - 0xD5, 0x83, - 0x15, 0x8B, - 0x15, 0x92, - 0x15, 0xA0, - 0x17, 0x80, - 0x1C, 0x80, - 0xF0, 0xFF, -}; -static constexpr Bitmap bitmap_icon_soundboard { - { 16, 16 }, bitmap_icon_soundboard_data -}; + 0xF0, + 0x0F, + 0x1C, + 0x18, + 0x17, + 0x38, + 0x15, + 0x78, + 0x15, + 0xF8, + 0x15, + 0x82, + 0x15, + 0x8B, + 0xD5, + 0x83, + 0xD5, + 0xBB, + 0xD5, + 0x83, + 0x15, + 0x8B, + 0x15, + 0x92, + 0x15, + 0xA0, + 0x17, + 0x80, + 0x1C, + 0x80, + 0xF0, + 0xFF, +}; +static constexpr Bitmap bitmap_icon_soundboard{ + {16, 16}, + bitmap_icon_soundboard_data}; static constexpr uint8_t bitmap_icon_rename_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x0E, - 0x00, 0x04, - 0xFF, 0xF5, - 0x01, 0x84, - 0xC9, 0x84, - 0x55, 0x85, - 0xDD, 0x84, - 0x55, 0x85, - 0xD5, 0x84, - 0x01, 0x84, - 0xFF, 0xF5, - 0x00, 0x04, - 0x00, 0x0E, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_rename { - { 16, 16 }, bitmap_icon_rename_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0E, + 0x00, + 0x04, + 0xFF, + 0xF5, + 0x01, + 0x84, + 0xC9, + 0x84, + 0x55, + 0x85, + 0xDD, + 0x84, + 0x55, + 0x85, + 0xD5, + 0x84, + 0x01, + 0x84, + 0xFF, + 0xF5, + 0x00, + 0x04, + 0x00, + 0x0E, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_rename{ + {16, 16}, + bitmap_icon_rename_data}; static constexpr uint8_t bitmap_icon_lora_data[] = { - 0xC0, 0x03, - 0x30, 0x0C, - 0x00, 0x00, - 0xC0, 0x03, - 0x00, 0x00, - 0xC0, 0x03, - 0x60, 0x06, - 0x60, 0x06, - 0x60, 0x06, - 0x60, 0x06, - 0xC0, 0x03, - 0x00, 0x00, - 0xC0, 0x03, - 0x00, 0x00, - 0x30, 0x0C, - 0xC0, 0x03, -}; -static constexpr Bitmap bitmap_icon_lora { - { 16, 16 }, bitmap_icon_lora_data -}; + 0xC0, + 0x03, + 0x30, + 0x0C, + 0x00, + 0x00, + 0xC0, + 0x03, + 0x00, + 0x00, + 0xC0, + 0x03, + 0x60, + 0x06, + 0x60, + 0x06, + 0x60, + 0x06, + 0x60, + 0x06, + 0xC0, + 0x03, + 0x00, + 0x00, + 0xC0, + 0x03, + 0x00, + 0x00, + 0x30, + 0x0C, + 0xC0, + 0x03, +}; +static constexpr Bitmap bitmap_icon_lora{ + {16, 16}, + bitmap_icon_lora_data}; static constexpr uint8_t bitmap_icon_lcr_data[] = { - 0x0C, 0x00, - 0xFF, 0x7F, - 0x01, 0x80, - 0xC1, 0x9B, - 0xFF, 0x7F, - 0x0C, 0x00, - 0xFF, 0x7F, - 0x01, 0x80, - 0xC1, 0x9D, - 0xFF, 0x7F, - 0x0C, 0x00, - 0x0C, 0x00, - 0x0C, 0x00, - 0x0C, 0x00, - 0x0C, 0x00, - 0x0C, 0x00, -}; -static constexpr Bitmap bitmap_icon_lcr { - { 16, 16 }, bitmap_icon_lcr_data -}; + 0x0C, + 0x00, + 0xFF, + 0x7F, + 0x01, + 0x80, + 0xC1, + 0x9B, + 0xFF, + 0x7F, + 0x0C, + 0x00, + 0xFF, + 0x7F, + 0x01, + 0x80, + 0xC1, + 0x9D, + 0xFF, + 0x7F, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, +}; +static constexpr Bitmap bitmap_icon_lcr{ + {16, 16}, + bitmap_icon_lcr_data}; static constexpr uint8_t bitmap_icon_file_data[] = { - 0xFC, 0x03, - 0x04, 0x06, - 0x04, 0x0E, - 0x04, 0x1E, - 0x04, 0x3E, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x20, - 0xFC, 0x3F, -}; -static constexpr Bitmap bitmap_icon_file { - { 16, 16 }, bitmap_icon_file_data -}; + 0xFC, + 0x03, + 0x04, + 0x06, + 0x04, + 0x0E, + 0x04, + 0x1E, + 0x04, + 0x3E, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x20, + 0xFC, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_file{ + {16, 16}, + bitmap_icon_file_data}; static constexpr uint8_t bitmap_icon_clk_ext_data[] = { - 0x00, - 0x00, - 0xDC, - 0x54, - 0x54, - 0x54, - 0x54, - 0x76, - 0x00, - 0x10, - 0x38, - 0x7C, - 0x10, - 0x10, - 0x10, - 0x00, -}; -static constexpr Bitmap bitmap_icon_clk_ext { - { 8, 16 }, bitmap_icon_clk_ext_data -}; + 0x00, + 0x00, + 0xDC, + 0x54, + 0x54, + 0x54, + 0x54, + 0x76, + 0x00, + 0x10, + 0x38, + 0x7C, + 0x10, + 0x10, + 0x10, + 0x00, +}; +static constexpr Bitmap bitmap_icon_clk_ext{ + {8, 16}, + bitmap_icon_clk_ext_data}; static constexpr uint8_t bitmap_icon_trash_data[] = { - 0x00, 0x00, - 0xC0, 0x01, - 0x20, 0x02, - 0xFC, 0x1F, - 0x00, 0x00, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0xA8, 0x0A, - 0x08, 0x08, - 0xF0, 0x07, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_trash { - { 16, 16 }, bitmap_icon_trash_data -}; + 0x00, + 0x00, + 0xC0, + 0x01, + 0x20, + 0x02, + 0xFC, + 0x1F, + 0x00, + 0x00, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0xA8, + 0x0A, + 0x08, + 0x08, + 0xF0, + 0x07, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_trash{ + {16, 16}, + bitmap_icon_trash_data}; static constexpr uint8_t bitmap_icon_fox_data[] = { - 0x18, 0x18, - 0x28, 0x14, - 0x68, 0x16, - 0x68, 0x16, - 0xC8, 0x13, - 0x88, 0x11, - 0x04, 0x20, - 0x24, 0x24, - 0x22, 0x44, - 0x01, 0x80, - 0x06, 0x60, - 0x98, 0x19, - 0x20, 0x04, - 0x40, 0x02, - 0x80, 0x01, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_fox { - { 16, 16 }, bitmap_icon_fox_data -}; + 0x18, + 0x18, + 0x28, + 0x14, + 0x68, + 0x16, + 0x68, + 0x16, + 0xC8, + 0x13, + 0x88, + 0x11, + 0x04, + 0x20, + 0x24, + 0x24, + 0x22, + 0x44, + 0x01, + 0x80, + 0x06, + 0x60, + 0x98, + 0x19, + 0x20, + 0x04, + 0x40, + 0x02, + 0x80, + 0x01, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_fox{ + {16, 16}, + bitmap_icon_fox_data}; static constexpr uint8_t bitmap_icon_script_data[] = { - 0xFC, 0x07, - 0xFA, 0x0F, - 0x19, 0x1A, - 0xF9, 0x1F, - 0x1E, 0x1D, - 0xF8, 0x1F, - 0x98, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0x08, 0x14, - 0xF8, 0x1F, - 0x48, 0x1E, - 0xF8, 0xFF, - 0x78, 0x80, - 0x30, 0x40, - 0xE0, 0x3F, -}; -static constexpr Bitmap bitmap_icon_script { - { 16, 16 }, bitmap_icon_script_data -}; + 0xFC, + 0x07, + 0xFA, + 0x0F, + 0x19, + 0x1A, + 0xF9, + 0x1F, + 0x1E, + 0x1D, + 0xF8, + 0x1F, + 0x98, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0x08, + 0x14, + 0xF8, + 0x1F, + 0x48, + 0x1E, + 0xF8, + 0xFF, + 0x78, + 0x80, + 0x30, + 0x40, + 0xE0, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_script{ + {16, 16}, + bitmap_icon_script_data}; static constexpr uint8_t bitmap_icon_hackrf_data[] = { - 0xF0, 0x0F, - 0x10, 0x08, - 0x50, 0x0A, - 0x10, 0x08, - 0x10, 0x08, - 0x10, 0x08, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0xF0, 0x0F, - 0x80, 0x01, - 0x80, 0x01, -}; -static constexpr Bitmap bitmap_icon_hackrf { - { 16, 16 }, bitmap_icon_hackrf_data -}; + 0xF0, + 0x0F, + 0x10, + 0x08, + 0x50, + 0x0A, + 0x10, + 0x08, + 0x10, + 0x08, + 0x10, + 0x08, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0xF0, + 0x0F, + 0x80, + 0x01, + 0x80, + 0x01, +}; +static constexpr Bitmap bitmap_icon_hackrf{ + {16, 16}, + bitmap_icon_hackrf_data}; static constexpr uint8_t bitmap_icon_dmr_data[] = { - 0x00, 0x00, - 0xFE, 0x1F, - 0xFE, 0x3F, - 0x0E, 0x78, - 0x0E, 0x70, - 0x0E, 0x70, - 0x0E, 0x70, - 0x0E, 0x78, - 0xFE, 0x3F, - 0xFE, 0x1F, - 0x8E, 0x07, - 0x0E, 0x0F, - 0x0E, 0x1E, - 0x0E, 0x3C, - 0x0E, 0x78, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_dmr { - { 16, 16 }, bitmap_icon_dmr_data -}; + 0x00, + 0x00, + 0xFE, + 0x1F, + 0xFE, + 0x3F, + 0x0E, + 0x78, + 0x0E, + 0x70, + 0x0E, + 0x70, + 0x0E, + 0x70, + 0x0E, + 0x78, + 0xFE, + 0x3F, + 0xFE, + 0x1F, + 0x8E, + 0x07, + 0x0E, + 0x0F, + 0x0E, + 0x1E, + 0x0E, + 0x3C, + 0x0E, + 0x78, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_dmr{ + {16, 16}, + bitmap_icon_dmr_data}; static constexpr uint8_t bitmap_icon_upconvert_data[] = { - 0x80, 0x01, - 0xC0, 0x03, - 0xE0, 0x07, - 0xF0, 0x0F, - 0xF8, 0x1F, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x00, 0x00, - 0x77, 0x77, - 0x51, 0x51, - 0x33, 0x53, - 0x51, 0x51, - 0x51, 0x77, - 0x00, 0x80, -}; -static constexpr Bitmap bitmap_icon_upconvert { - { 16, 16 }, bitmap_icon_upconvert_data -}; + 0x80, + 0x01, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xF0, + 0x0F, + 0xF8, + 0x1F, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x00, + 0x00, + 0x77, + 0x77, + 0x51, + 0x51, + 0x33, + 0x53, + 0x51, + 0x51, + 0x51, + 0x77, + 0x00, + 0x80, +}; +static constexpr Bitmap bitmap_icon_upconvert{ + {16, 16}, + bitmap_icon_upconvert_data}; static constexpr uint8_t bitmap_icon_clk_int_data[] = { - 0x00, - 0x00, - 0xDC, - 0x54, - 0x54, - 0x54, - 0x54, - 0x76, - 0x00, - 0x44, - 0x6C, - 0x38, - 0x38, - 0x6C, - 0x44, - 0x00, -}; -static constexpr Bitmap bitmap_icon_clk_int { - { 8, 16 }, bitmap_icon_clk_int_data -}; + 0x00, + 0x00, + 0xDC, + 0x54, + 0x54, + 0x54, + 0x54, + 0x76, + 0x00, + 0x44, + 0x6C, + 0x38, + 0x38, + 0x6C, + 0x44, + 0x00, +}; +static constexpr Bitmap bitmap_icon_clk_int{ + {8, 16}, + bitmap_icon_clk_int_data}; static constexpr uint8_t bitmap_icon_rds_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x3C, 0x3C, - 0x7E, 0x7E, - 0x67, 0xE7, - 0x83, 0xC3, - 0xC7, 0xE1, - 0xFD, 0xBC, - 0x42, 0x42, - 0x3C, 0x3C, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_rds { - { 16, 16 }, bitmap_icon_rds_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x3C, + 0x7E, + 0x7E, + 0x67, + 0xE7, + 0x83, + 0xC3, + 0xC7, + 0xE1, + 0xFD, + 0xBC, + 0x42, + 0x42, + 0x3C, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_rds{ + {16, 16}, + bitmap_icon_rds_data}; static constexpr uint8_t bitmap_icon_file_image_data[] = { - 0x00, 0x00, - 0xFF, 0xFF, - 0x01, 0x80, - 0x01, 0x80, - 0x89, 0x80, - 0xC1, 0x81, - 0xE1, 0xA3, - 0xB1, 0xB3, - 0x89, 0xDC, - 0x07, 0x8C, - 0x01, 0x90, - 0x01, 0x80, - 0xAB, 0x82, - 0xFF, 0xD5, - 0xFF, 0xFF, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_file_image { - { 16, 16 }, bitmap_icon_file_image_data -}; + 0x00, + 0x00, + 0xFF, + 0xFF, + 0x01, + 0x80, + 0x01, + 0x80, + 0x89, + 0x80, + 0xC1, + 0x81, + 0xE1, + 0xA3, + 0xB1, + 0xB3, + 0x89, + 0xDC, + 0x07, + 0x8C, + 0x01, + 0x90, + 0x01, + 0x80, + 0xAB, + 0x82, + 0xFF, + 0xD5, + 0xFF, + 0xFF, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_file_image{ + {16, 16}, + bitmap_icon_file_image_data}; static constexpr uint8_t bitmap_icon_temperature_data[] = { - 0x00, 0x01, - 0x80, 0x01, - 0x80, 0x05, - 0xC0, 0x0D, - 0x40, 0x0D, - 0xD0, 0x1F, - 0x70, 0x15, - 0xB0, 0x1A, - 0x58, 0x35, - 0xB8, 0x3A, - 0x58, 0x34, - 0x28, 0x28, - 0x18, 0x30, - 0x30, 0x18, - 0x60, 0x0C, - 0xC0, 0x07, -}; -static constexpr Bitmap bitmap_icon_temperature { - { 16, 16 }, bitmap_icon_temperature_data -}; + 0x00, + 0x01, + 0x80, + 0x01, + 0x80, + 0x05, + 0xC0, + 0x0D, + 0x40, + 0x0D, + 0xD0, + 0x1F, + 0x70, + 0x15, + 0xB0, + 0x1A, + 0x58, + 0x35, + 0xB8, + 0x3A, + 0x58, + 0x34, + 0x28, + 0x28, + 0x18, + 0x30, + 0x30, + 0x18, + 0x60, + 0x0C, + 0xC0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_temperature{ + {16, 16}, + bitmap_icon_temperature_data}; static constexpr uint8_t bitmap_tab_edge_data[] = { - 0x00, - 0x01, - 0x01, - 0x03, - 0x03, - 0x03, - 0x07, - 0x07, - 0x07, - 0x0F, - 0x0F, - 0x0F, - 0x1F, - 0x1F, - 0x1F, - 0x1F, - 0x3F, - 0x3F, - 0x3F, - 0x7F, - 0x7F, - 0x7F, - 0xFF, - 0xFF, -}; -static constexpr Bitmap bitmap_tab_edge { - { 8, 24 }, bitmap_tab_edge_data -}; + 0x00, + 0x01, + 0x01, + 0x03, + 0x03, + 0x03, + 0x07, + 0x07, + 0x07, + 0x0F, + 0x0F, + 0x0F, + 0x1F, + 0x1F, + 0x1F, + 0x1F, + 0x3F, + 0x3F, + 0x3F, + 0x7F, + 0x7F, + 0x7F, + 0xFF, + 0xFF, +}; +static constexpr Bitmap bitmap_tab_edge{ + {8, 24}, + bitmap_tab_edge_data}; static constexpr uint8_t bitmap_icon_sdcard_data[] = { - 0xF0, 0x3F, - 0x58, 0x35, - 0x5C, 0x35, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xAC, 0x3A, - 0x5C, 0x35, - 0xAC, 0x3A, - 0x5C, 0x35, - 0xAC, 0x3A, - 0x5C, 0x35, - 0xAC, 0x3A, -}; -static constexpr Bitmap bitmap_icon_sdcard { - { 16, 16 }, bitmap_icon_sdcard_data -}; + 0xF0, + 0x3F, + 0x58, + 0x35, + 0x5C, + 0x35, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xAC, + 0x3A, + 0x5C, + 0x35, + 0xAC, + 0x3A, + 0x5C, + 0x35, + 0xAC, + 0x3A, + 0x5C, + 0x35, + 0xAC, + 0x3A, +}; +static constexpr Bitmap bitmap_icon_sdcard{ + {16, 16}, + bitmap_icon_sdcard_data}; static constexpr uint8_t bitmap_icon_file_iq_data[] = { - 0xFC, 0x03, - 0x04, 0x06, - 0x04, 0x0E, - 0x04, 0x1E, - 0x04, 0x3E, - 0x04, 0x20, - 0x04, 0x20, - 0x04, 0x21, - 0x44, 0x25, - 0x54, 0x25, - 0xF4, 0x2F, - 0xA4, 0x2A, - 0x84, 0x22, - 0x04, 0x22, - 0x04, 0x20, - 0xFC, 0x3F, -}; -static constexpr Bitmap bitmap_icon_file_iq { - { 16, 16 }, bitmap_icon_file_iq_data -}; + 0xFC, + 0x03, + 0x04, + 0x06, + 0x04, + 0x0E, + 0x04, + 0x1E, + 0x04, + 0x3E, + 0x04, + 0x20, + 0x04, + 0x20, + 0x04, + 0x21, + 0x44, + 0x25, + 0x54, + 0x25, + 0xF4, + 0x2F, + 0xA4, + 0x2A, + 0x84, + 0x22, + 0x04, + 0x22, + 0x04, + 0x20, + 0xFC, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_file_iq{ + {16, 16}, + bitmap_icon_file_iq_data}; static constexpr uint8_t bitmap_icon_sd_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xC0, 0x1F, - 0xE0, 0x1F, - 0xF0, 0x1F, - 0xF8, 0x1F, - 0x98, 0x18, - 0xE8, 0x16, - 0xC8, 0x16, - 0x98, 0x16, - 0xB8, 0x16, - 0xC8, 0x18, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_sd { - { 16, 16 }, bitmap_icon_sd_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x1F, + 0xE0, + 0x1F, + 0xF0, + 0x1F, + 0xF8, + 0x1F, + 0x98, + 0x18, + 0xE8, + 0x16, + 0xC8, + 0x16, + 0x98, + 0x16, + 0xB8, + 0x16, + 0xC8, + 0x18, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_sd{ + {16, 16}, + bitmap_icon_sd_data}; static constexpr uint8_t bitmap_icon_camera_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xE0, 0x07, - 0xF0, 0x0F, - 0x3E, 0x7C, - 0xDE, 0x7B, - 0xEE, 0x77, - 0xEE, 0x77, - 0xEE, 0x77, - 0xEE, 0x77, - 0xDE, 0x7B, - 0x3E, 0x7C, - 0xFE, 0x7F, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_camera { - { 16, 16 }, bitmap_icon_camera_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xE0, + 0x07, + 0xF0, + 0x0F, + 0x3E, + 0x7C, + 0xDE, + 0x7B, + 0xEE, + 0x77, + 0xEE, + 0x77, + 0xEE, + 0x77, + 0xEE, + 0x77, + 0xDE, + 0x7B, + 0x3E, + 0x7C, + 0xFE, + 0x7F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_camera{ + {16, 16}, + bitmap_icon_camera_data}; static constexpr uint8_t bitmap_icon_tools_wipesd_data[] = { - 0xF0, 0x3F, - 0x58, 0x35, - 0x5C, 0x35, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0x3C, 0x1C, - 0xBC, 0xC9, - 0xBC, 0xE3, - 0x2C, 0x77, - 0x5C, 0x3E, - 0xAC, 0x1C, - 0x5C, 0x3E, - 0x2C, 0x77, - 0x9C, 0xE3, - 0xAC, 0xC1, -}; -static constexpr Bitmap bitmap_icon_tools_wipesd { - { 16, 16 }, bitmap_icon_tools_wipesd_data -}; + 0xF0, + 0x3F, + 0x58, + 0x35, + 0x5C, + 0x35, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0x3C, + 0x1C, + 0xBC, + 0xC9, + 0xBC, + 0xE3, + 0x2C, + 0x77, + 0x5C, + 0x3E, + 0xAC, + 0x1C, + 0x5C, + 0x3E, + 0x2C, + 0x77, + 0x9C, + 0xE3, + 0xAC, + 0xC1, +}; +static constexpr Bitmap bitmap_icon_tools_wipesd{ + {16, 16}, + bitmap_icon_tools_wipesd_data}; static constexpr uint8_t bitmap_icon_options_radio_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x04, 0x20, - 0x12, 0x48, - 0x8A, 0x51, - 0xCA, 0x53, - 0xCA, 0x53, - 0x8A, 0x51, - 0x12, 0x48, - 0x84, 0x21, - 0xC0, 0x03, - 0x40, 0x02, - 0x60, 0x06, - 0x20, 0x04, - 0x30, 0x0C, - 0xF0, 0x0F, -}; -static constexpr Bitmap bitmap_icon_options_radio { - { 16, 16 }, bitmap_icon_options_radio_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0x20, + 0x12, + 0x48, + 0x8A, + 0x51, + 0xCA, + 0x53, + 0xCA, + 0x53, + 0x8A, + 0x51, + 0x12, + 0x48, + 0x84, + 0x21, + 0xC0, + 0x03, + 0x40, + 0x02, + 0x60, + 0x06, + 0x20, + 0x04, + 0x30, + 0x0C, + 0xF0, + 0x0F, +}; +static constexpr Bitmap bitmap_icon_options_radio{ + {16, 16}, + bitmap_icon_options_radio_data}; static constexpr uint8_t bitmap_icon_freqman_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x7E, 0x7E, - 0x81, 0x81, - 0xBD, 0xBD, - 0x81, 0x81, - 0xBD, 0xBD, - 0x81, 0x81, - 0xBD, 0x9D, - 0x81, 0x81, - 0xBD, 0xE1, - 0x81, 0x61, - 0x7E, 0x3E, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_freqman { - { 16, 16 }, bitmap_icon_freqman_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x7E, + 0x81, + 0x81, + 0xBD, + 0xBD, + 0x81, + 0x81, + 0xBD, + 0xBD, + 0x81, + 0x81, + 0xBD, + 0x9D, + 0x81, + 0x81, + 0xBD, + 0xE1, + 0x81, + 0x61, + 0x7E, + 0x3E, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_freqman{ + {16, 16}, + bitmap_icon_freqman_data}; static constexpr uint8_t bitmap_sd_card_ok_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xC0, 0x1F, - 0xE0, 0x1F, - 0xF0, 0x1F, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0x98, 0x15, - 0x68, 0x19, - 0x68, 0x1D, - 0x68, 0x19, - 0x98, 0x15, - 0xF8, 0x1F, - 0xF8, 0x1F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sd_card_ok { - { 16, 16 }, bitmap_sd_card_ok_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x1F, + 0xE0, + 0x1F, + 0xF0, + 0x1F, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0x98, + 0x15, + 0x68, + 0x19, + 0x68, + 0x1D, + 0x68, + 0x19, + 0x98, + 0x15, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sd_card_ok{ + {16, 16}, + bitmap_sd_card_ok_data}; static constexpr uint8_t bitmap_icon_dir_data[] = { - 0x00, 0x00, - 0x3E, 0x00, - 0x41, 0x00, - 0xC1, 0x7F, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xAF, 0xEA, - 0x57, 0xF5, - 0xEF, 0xEF, - 0xF7, 0xF7, - 0xEE, 0x6F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_dir { - { 16, 16 }, bitmap_icon_dir_data -}; + 0x00, + 0x00, + 0x3E, + 0x00, + 0x41, + 0x00, + 0xC1, + 0x7F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xAF, + 0xEA, + 0x57, + 0xF5, + 0xEF, + 0xEF, + 0xF7, + 0xF7, + 0xEE, + 0x6F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_dir{ + {16, 16}, + bitmap_icon_dir_data}; static constexpr uint8_t bitmap_icon_memory_data[] = { - 0x54, 0x15, - 0x54, 0x15, - 0xFF, 0x7F, - 0xFC, 0x1F, - 0xFF, 0x7F, - 0xCC, 0x19, - 0xAF, 0x7A, - 0x6C, 0x1B, - 0xEF, 0x7B, - 0xEC, 0x1B, - 0xFF, 0x7F, - 0xFC, 0x1F, - 0xFF, 0x7F, - 0x54, 0x15, - 0x54, 0x15, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_memory { - { 16, 16 }, bitmap_icon_memory_data -}; + 0x54, + 0x15, + 0x54, + 0x15, + 0xFF, + 0x7F, + 0xFC, + 0x1F, + 0xFF, + 0x7F, + 0xCC, + 0x19, + 0xAF, + 0x7A, + 0x6C, + 0x1B, + 0xEF, + 0x7B, + 0xEC, + 0x1B, + 0xFF, + 0x7F, + 0xFC, + 0x1F, + 0xFF, + 0x7F, + 0x54, + 0x15, + 0x54, + 0x15, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_memory{ + {16, 16}, + bitmap_icon_memory_data}; static constexpr uint8_t bitmap_icon_remote_data[] = { - 0x20, 0x00, - 0x20, 0x00, - 0x20, 0x00, - 0x20, 0x00, - 0xE0, 0x07, - 0xF0, 0x0F, - 0x30, 0x0C, - 0x30, 0x0C, - 0xF0, 0x0F, - 0xF0, 0x0F, - 0x70, 0x0D, - 0xB0, 0x0E, - 0x70, 0x0D, - 0xB0, 0x0E, - 0xF0, 0x0F, - 0xE0, 0x07, -}; -static constexpr Bitmap bitmap_icon_remote { - { 16, 16 }, bitmap_icon_remote_data -}; + 0x20, + 0x00, + 0x20, + 0x00, + 0x20, + 0x00, + 0x20, + 0x00, + 0xE0, + 0x07, + 0xF0, + 0x0F, + 0x30, + 0x0C, + 0x30, + 0x0C, + 0xF0, + 0x0F, + 0xF0, + 0x0F, + 0x70, + 0x0D, + 0xB0, + 0x0E, + 0x70, + 0x0D, + 0xB0, + 0x0E, + 0xF0, + 0x0F, + 0xE0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_remote{ + {16, 16}, + bitmap_icon_remote_data}; static constexpr uint8_t bitmap_icon_save_data[] = { - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x4E, 0x05, - 0x91, 0x03, - 0x3F, 0x19, - 0x01, 0x20, - 0xF9, 0xFF, - 0xF9, 0xFF, - 0xFD, 0x7F, - 0xFD, 0x7F, - 0xFF, 0x3F, - 0xFF, 0x3F, - 0xFF, 0x1F, - 0xFF, 0x1F, -}; -static constexpr Bitmap bitmap_icon_save { - { 16, 16 }, bitmap_icon_save_data -}; + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x4E, + 0x05, + 0x91, + 0x03, + 0x3F, + 0x19, + 0x01, + 0x20, + 0xF9, + 0xFF, + 0xF9, + 0xFF, + 0xFD, + 0x7F, + 0xFD, + 0x7F, + 0xFF, + 0x3F, + 0xFF, + 0x3F, + 0xFF, + 0x1F, + 0xFF, + 0x1F, +}; +static constexpr Bitmap bitmap_icon_save{ + {16, 16}, + bitmap_icon_save_data}; static constexpr uint8_t bitmap_icon_back_data[] = { - 0x00, 0x00, - 0x30, 0x00, - 0x38, 0x00, - 0x1C, 0x00, - 0x0E, 0x00, - 0xFF, 0x3F, - 0xFF, 0x7F, - 0x0E, 0xE0, - 0x1C, 0xC0, - 0x38, 0xC0, - 0x30, 0xC0, - 0x00, 0xE0, - 0x00, 0x7F, - 0x00, 0x3F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_back { - { 16, 16 }, bitmap_icon_back_data -}; + 0x00, + 0x00, + 0x30, + 0x00, + 0x38, + 0x00, + 0x1C, + 0x00, + 0x0E, + 0x00, + 0xFF, + 0x3F, + 0xFF, + 0x7F, + 0x0E, + 0xE0, + 0x1C, + 0xC0, + 0x38, + 0xC0, + 0x30, + 0xC0, + 0x00, + 0xE0, + 0x00, + 0x7F, + 0x00, + 0x3F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_back{ + {16, 16}, + bitmap_icon_back_data}; static constexpr uint8_t bitmap_icon_bht_data[] = { - 0x00, 0x00, - 0xE0, 0x07, - 0xF8, 0x08, - 0x9C, 0x07, - 0x0C, 0x00, - 0x8E, 0x0A, - 0x46, 0x12, - 0x26, 0x22, - 0x06, 0x02, - 0x06, 0x00, - 0x06, 0x00, - 0x06, 0x00, - 0x06, 0x00, - 0x06, 0x00, - 0x06, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_bht { - { 16, 16 }, bitmap_icon_bht_data -}; + 0x00, + 0x00, + 0xE0, + 0x07, + 0xF8, + 0x08, + 0x9C, + 0x07, + 0x0C, + 0x00, + 0x8E, + 0x0A, + 0x46, + 0x12, + 0x26, + 0x22, + 0x06, + 0x02, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_bht{ + {16, 16}, + bitmap_icon_bht_data}; static constexpr uint8_t bitmap_play_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x0C, 0x00, - 0x3C, 0x00, - 0xFC, 0x00, - 0xFC, 0x03, - 0xFC, 0x0F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x0F, - 0xFC, 0x03, - 0xFC, 0x00, - 0x3C, 0x00, - 0x0C, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_play { - { 16, 16 }, bitmap_play_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x00, + 0x3C, + 0x00, + 0xFC, + 0x00, + 0xFC, + 0x03, + 0xFC, + 0x0F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x0F, + 0xFC, + 0x03, + 0xFC, + 0x00, + 0x3C, + 0x00, + 0x0C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_play{ + {16, 16}, + bitmap_play_data}; static constexpr uint8_t bitmap_icon_setup_data[] = { - 0xC0, 0x01, - 0xC0, 0x01, - 0xE4, 0x13, - 0xFE, 0x3F, - 0xFF, 0x7F, - 0x3E, 0x3E, - 0x1C, 0x1C, - 0x1C, 0x1C, - 0x1C, 0x1C, - 0x3E, 0x3E, - 0xFF, 0x7F, - 0xFE, 0x3F, - 0xE4, 0x13, - 0xC0, 0x01, - 0xC0, 0x01, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_setup { - { 16, 16 }, bitmap_icon_setup_data -}; + 0xC0, + 0x01, + 0xC0, + 0x01, + 0xE4, + 0x13, + 0xFE, + 0x3F, + 0xFF, + 0x7F, + 0x3E, + 0x3E, + 0x1C, + 0x1C, + 0x1C, + 0x1C, + 0x1C, + 0x1C, + 0x3E, + 0x3E, + 0xFF, + 0x7F, + 0xFE, + 0x3F, + 0xE4, + 0x13, + 0xC0, + 0x01, + 0xC0, + 0x01, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_setup{ + {16, 16}, + bitmap_icon_setup_data}; static constexpr uint8_t bitmap_sig_saw_down_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x0E, 0x80, 0x00, 0x60, - 0x1E, 0x80, 0x01, 0x60, - 0x3E, 0x80, 0x03, 0x60, - 0x76, 0x80, 0x07, 0x60, - 0xE6, 0x80, 0x0F, 0x60, - 0xC6, 0x81, 0x1D, 0x60, - 0x86, 0x83, 0x39, 0x60, - 0x06, 0x87, 0x71, 0x60, - 0x06, 0x8E, 0xE1, 0x60, - 0x06, 0x9C, 0xC1, 0x61, - 0x06, 0xB8, 0x81, 0x63, - 0x06, 0xF0, 0x01, 0x67, - 0x06, 0xE0, 0x01, 0x6E, - 0x06, 0xC0, 0x01, 0x7C, - 0x06, 0x80, 0x01, 0x78, - 0x06, 0x00, 0x01, 0x70, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_saw_down { - { 32, 32 }, bitmap_sig_saw_down_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0E, + 0x80, + 0x00, + 0x60, + 0x1E, + 0x80, + 0x01, + 0x60, + 0x3E, + 0x80, + 0x03, + 0x60, + 0x76, + 0x80, + 0x07, + 0x60, + 0xE6, + 0x80, + 0x0F, + 0x60, + 0xC6, + 0x81, + 0x1D, + 0x60, + 0x86, + 0x83, + 0x39, + 0x60, + 0x06, + 0x87, + 0x71, + 0x60, + 0x06, + 0x8E, + 0xE1, + 0x60, + 0x06, + 0x9C, + 0xC1, + 0x61, + 0x06, + 0xB8, + 0x81, + 0x63, + 0x06, + 0xF0, + 0x01, + 0x67, + 0x06, + 0xE0, + 0x01, + 0x6E, + 0x06, + 0xC0, + 0x01, + 0x7C, + 0x06, + 0x80, + 0x01, + 0x78, + 0x06, + 0x00, + 0x01, + 0x70, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_saw_down{ + {32, 32}, + bitmap_sig_saw_down_data}; static constexpr uint8_t bitmap_icon_load_data[] = { - 0x00, 0x01, - 0x80, 0x03, - 0x40, 0x05, - 0x00, 0x01, - 0x0E, 0x01, - 0x11, 0x01, - 0x7F, 0x1D, - 0x01, 0x20, - 0xF9, 0xFF, - 0xF9, 0xFF, - 0xFD, 0x7F, - 0xFD, 0x7F, - 0xFF, 0x3F, - 0xFF, 0x3F, - 0xFF, 0x1F, - 0xFF, 0x1F, -}; -static constexpr Bitmap bitmap_icon_load { - { 16, 16 }, bitmap_icon_load_data -}; + 0x00, + 0x01, + 0x80, + 0x03, + 0x40, + 0x05, + 0x00, + 0x01, + 0x0E, + 0x01, + 0x11, + 0x01, + 0x7F, + 0x1D, + 0x01, + 0x20, + 0xF9, + 0xFF, + 0xF9, + 0xFF, + 0xFD, + 0x7F, + 0xFD, + 0x7F, + 0xFF, + 0x3F, + 0xFF, + 0x3F, + 0xFF, + 0x1F, + 0xFF, + 0x1F, +}; +static constexpr Bitmap bitmap_icon_load{ + {16, 16}, + bitmap_icon_load_data}; static constexpr uint8_t bitmap_icon_speaker_mute_data[] = { - 0x00, 0x00, - 0x40, 0x00, - 0x60, 0x00, - 0x70, 0x00, - 0x78, 0x00, - 0x7F, 0x22, - 0x7F, 0x36, - 0x7F, 0x1C, - 0x7F, 0x08, - 0x7F, 0x1C, - 0x7F, 0x36, - 0x7F, 0x22, - 0x78, 0x00, - 0x70, 0x00, - 0x60, 0x00, - 0x40, 0x00, -}; -static constexpr Bitmap bitmap_icon_speaker_mute { - { 16, 16 }, bitmap_icon_speaker_mute_data -}; + 0x00, + 0x00, + 0x40, + 0x00, + 0x60, + 0x00, + 0x70, + 0x00, + 0x78, + 0x00, + 0x7F, + 0x22, + 0x7F, + 0x36, + 0x7F, + 0x1C, + 0x7F, + 0x08, + 0x7F, + 0x1C, + 0x7F, + 0x36, + 0x7F, + 0x22, + 0x78, + 0x00, + 0x70, + 0x00, + 0x60, + 0x00, + 0x40, + 0x00, +}; +static constexpr Bitmap bitmap_icon_speaker_mute{ + {16, 16}, + bitmap_icon_speaker_mute_data}; static constexpr uint8_t bitmap_icon_modem_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xF8, 0x1F, - 0x04, 0x20, - 0x02, 0x40, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xAB, 0xDF, - 0xAB, 0xDF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_modem { - { 16, 16 }, bitmap_icon_modem_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF8, + 0x1F, + 0x04, + 0x20, + 0x02, + 0x40, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xAB, + 0xDF, + 0xAB, + 0xDF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_modem{ + {16, 16}, + bitmap_icon_modem_data}; static constexpr uint8_t bitmap_bulb_off_data[] = { - 0x00, 0x3C, 0x00, - 0x00, 0xC3, 0x00, - 0x80, 0x00, 0x01, - 0x40, 0x00, 0x02, - 0x20, 0x00, 0x04, - 0x20, 0x00, 0x04, - 0x10, 0x00, 0x08, - 0x10, 0x42, 0x08, - 0x10, 0x42, 0x08, - 0x10, 0x24, 0x08, - 0x10, 0x24, 0x08, - 0x20, 0x24, 0x04, - 0x20, 0x2C, 0x04, - 0x40, 0x34, 0x02, - 0x80, 0x3C, 0x01, - 0x00, 0xFF, 0x00, - 0x00, 0xE3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0x42, 0x00, - 0x00, 0x3C, 0x00, -}; -static constexpr Bitmap bitmap_bulb_off { - { 24, 24 }, bitmap_bulb_off_data -}; + 0x00, + 0x3C, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x80, + 0x00, + 0x01, + 0x40, + 0x00, + 0x02, + 0x20, + 0x00, + 0x04, + 0x20, + 0x00, + 0x04, + 0x10, + 0x00, + 0x08, + 0x10, + 0x42, + 0x08, + 0x10, + 0x42, + 0x08, + 0x10, + 0x24, + 0x08, + 0x10, + 0x24, + 0x08, + 0x20, + 0x24, + 0x04, + 0x20, + 0x2C, + 0x04, + 0x40, + 0x34, + 0x02, + 0x80, + 0x3C, + 0x01, + 0x00, + 0xFF, + 0x00, + 0x00, + 0xE3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0x42, + 0x00, + 0x00, + 0x3C, + 0x00, +}; +static constexpr Bitmap bitmap_bulb_off{ + {24, 24}, + bitmap_bulb_off_data}; static constexpr uint8_t bitmap_icon_sleep_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x04, - 0x00, 0x08, - 0x00, 0x18, - 0x00, 0x18, - 0x00, 0x38, - 0x00, 0x3C, - 0x00, 0x3C, - 0x00, 0x3E, - 0x84, 0x1F, - 0xF8, 0x1F, - 0xF0, 0x0F, - 0xC0, 0x03, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_sleep { - { 16, 16 }, bitmap_icon_sleep_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0x00, + 0x08, + 0x00, + 0x18, + 0x00, + 0x18, + 0x00, + 0x38, + 0x00, + 0x3C, + 0x00, + 0x3C, + 0x00, + 0x3E, + 0x84, + 0x1F, + 0xF8, + 0x1F, + 0xF0, + 0x0F, + 0xC0, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_sleep{ + {16, 16}, + bitmap_icon_sleep_data}; static constexpr uint8_t bitmap_icon_notepad_data[] = { - 0x0C, 0x00, - 0x1E, 0x00, - 0x2F, 0x00, - 0x47, 0x00, - 0xE2, 0x00, - 0xD4, 0x01, - 0xB8, 0x03, - 0x70, 0x07, - 0xE0, 0x0E, - 0xC0, 0x1D, - 0x80, 0x3B, - 0x00, 0x4F, - 0x00, 0x46, - 0x00, 0x84, - 0x00, 0xD8, - 0x00, 0xE0, -}; -static constexpr Bitmap bitmap_icon_notepad { - { 16, 16 }, bitmap_icon_notepad_data -}; + 0x0C, + 0x00, + 0x1E, + 0x00, + 0x2F, + 0x00, + 0x47, + 0x00, + 0xE2, + 0x00, + 0xD4, + 0x01, + 0xB8, + 0x03, + 0x70, + 0x07, + 0xE0, + 0x0E, + 0xC0, + 0x1D, + 0x80, + 0x3B, + 0x00, + 0x4F, + 0x00, + 0x46, + 0x00, + 0x84, + 0x00, + 0xD8, + 0x00, + 0xE0, +}; +static constexpr Bitmap bitmap_icon_notepad{ + {16, 16}, + bitmap_icon_notepad_data}; static constexpr uint8_t bitmap_icon_copy_data[] = { - 0x00, 0x00, - 0xFC, 0x00, - 0x84, 0x01, - 0xC4, 0x0F, - 0x74, 0x18, - 0x44, 0x38, - 0x44, 0x78, - 0x74, 0x40, - 0x44, 0x44, - 0x44, 0x44, - 0x74, 0x5F, - 0x44, 0x44, - 0x44, 0x44, - 0x7C, 0x40, - 0xC0, 0x7F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_copy { - { 16, 16 }, bitmap_icon_copy_data -}; + 0x00, + 0x00, + 0xFC, + 0x00, + 0x84, + 0x01, + 0xC4, + 0x0F, + 0x74, + 0x18, + 0x44, + 0x38, + 0x44, + 0x78, + 0x74, + 0x40, + 0x44, + 0x44, + 0x44, + 0x44, + 0x74, + 0x5F, + 0x44, + 0x44, + 0x44, + 0x44, + 0x7C, + 0x40, + 0xC0, + 0x7F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_copy{ + {16, 16}, + bitmap_icon_copy_data}; static constexpr uint8_t bitmap_icon_cut_data[] = { - 0x00, 0x00, - 0x10, 0x10, - 0x30, 0x18, - 0x20, 0x08, - 0x60, 0x0C, - 0x40, 0x04, - 0xC0, 0x06, - 0x80, 0x00, - 0x80, 0x01, - 0x80, 0x01, - 0xC0, 0x03, - 0x78, 0x1E, - 0x44, 0x22, - 0x44, 0x22, - 0x44, 0x22, - 0x38, 0x1C, -}; -static constexpr Bitmap bitmap_icon_cut { - { 16, 16 }, bitmap_icon_cut_data -}; + 0x00, + 0x00, + 0x10, + 0x10, + 0x30, + 0x18, + 0x20, + 0x08, + 0x60, + 0x0C, + 0x40, + 0x04, + 0xC0, + 0x06, + 0x80, + 0x00, + 0x80, + 0x01, + 0x80, + 0x01, + 0xC0, + 0x03, + 0x78, + 0x1E, + 0x44, + 0x22, + 0x44, + 0x22, + 0x44, + 0x22, + 0x38, + 0x1C, +}; +static constexpr Bitmap bitmap_icon_cut{ + {16, 16}, + bitmap_icon_cut_data}; static constexpr uint8_t bitmap_target_calibrate_data[] = { - 0x02, 0x00, 0x00, 0x40, - 0x07, 0x00, 0x00, 0xE0, - 0x0E, 0x00, 0x00, 0x70, - 0x1C, 0x00, 0x00, 0x38, - 0x38, 0x00, 0x00, 0x1C, - 0x70, 0x00, 0x00, 0x0E, - 0xE0, 0x00, 0x00, 0x07, - 0xC0, 0x01, 0x80, 0x03, - 0x80, 0x03, 0xC0, 0x01, - 0x00, 0x07, 0xE0, 0x00, - 0x00, 0x0E, 0x70, 0x00, - 0x00, 0x1C, 0x38, 0x00, - 0x00, 0x38, 0x1C, 0x00, - 0x00, 0x70, 0x0E, 0x00, - 0x00, 0xE0, 0x07, 0x00, - 0x00, 0xC0, 0x03, 0x00, - 0x00, 0xC0, 0x03, 0x00, - 0x00, 0xE0, 0x07, 0x00, - 0x00, 0x70, 0x0E, 0x00, - 0x00, 0x38, 0x1C, 0x00, - 0x00, 0x1C, 0x38, 0x00, - 0x00, 0x0E, 0x70, 0x00, - 0x00, 0x07, 0xE0, 0x00, - 0x80, 0x03, 0xC0, 0x01, - 0xC0, 0x01, 0x80, 0x03, - 0xE0, 0x00, 0x00, 0x07, - 0x70, 0x00, 0x00, 0x0E, - 0x38, 0x00, 0x00, 0x1C, - 0x1C, 0x00, 0x00, 0x38, - 0x0E, 0x00, 0x00, 0x70, - 0x07, 0x00, 0x00, 0xE0, - 0x02, 0x00, 0x00, 0x40, -}; -static constexpr Bitmap bitmap_target_calibrate { - { 32, 32 }, bitmap_target_calibrate_data -}; + 0x02, + 0x00, + 0x00, + 0x40, + 0x07, + 0x00, + 0x00, + 0xE0, + 0x0E, + 0x00, + 0x00, + 0x70, + 0x1C, + 0x00, + 0x00, + 0x38, + 0x38, + 0x00, + 0x00, + 0x1C, + 0x70, + 0x00, + 0x00, + 0x0E, + 0xE0, + 0x00, + 0x00, + 0x07, + 0xC0, + 0x01, + 0x80, + 0x03, + 0x80, + 0x03, + 0xC0, + 0x01, + 0x00, + 0x07, + 0xE0, + 0x00, + 0x00, + 0x0E, + 0x70, + 0x00, + 0x00, + 0x1C, + 0x38, + 0x00, + 0x00, + 0x38, + 0x1C, + 0x00, + 0x00, + 0x70, + 0x0E, + 0x00, + 0x00, + 0xE0, + 0x07, + 0x00, + 0x00, + 0xC0, + 0x03, + 0x00, + 0x00, + 0xC0, + 0x03, + 0x00, + 0x00, + 0xE0, + 0x07, + 0x00, + 0x00, + 0x70, + 0x0E, + 0x00, + 0x00, + 0x38, + 0x1C, + 0x00, + 0x00, + 0x1C, + 0x38, + 0x00, + 0x00, + 0x0E, + 0x70, + 0x00, + 0x00, + 0x07, + 0xE0, + 0x00, + 0x80, + 0x03, + 0xC0, + 0x01, + 0xC0, + 0x01, + 0x80, + 0x03, + 0xE0, + 0x00, + 0x00, + 0x07, + 0x70, + 0x00, + 0x00, + 0x0E, + 0x38, + 0x00, + 0x00, + 0x1C, + 0x1C, + 0x00, + 0x00, + 0x38, + 0x0E, + 0x00, + 0x00, + 0x70, + 0x07, + 0x00, + 0x00, + 0xE0, + 0x02, + 0x00, + 0x00, + 0x40, +}; +static constexpr Bitmap bitmap_target_calibrate{ + {32, 32}, + bitmap_target_calibrate_data}; static constexpr uint8_t bitmap_icon_controls_data[] = { - 0x8C, 0x31, - 0x5A, 0x6B, - 0xDE, 0x7B, - 0x8C, 0x31, - 0x00, 0x00, - 0x8C, 0x31, - 0x5A, 0x7B, - 0xDE, 0x7B, - 0x8C, 0x31, - 0x00, 0x00, - 0x8C, 0x31, - 0xDA, 0x7B, - 0xDE, 0x7B, - 0x8C, 0x31, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_controls { - { 16, 16 }, bitmap_icon_controls_data -}; + 0x8C, + 0x31, + 0x5A, + 0x6B, + 0xDE, + 0x7B, + 0x8C, + 0x31, + 0x00, + 0x00, + 0x8C, + 0x31, + 0x5A, + 0x7B, + 0xDE, + 0x7B, + 0x8C, + 0x31, + 0x00, + 0x00, + 0x8C, + 0x31, + 0xDA, + 0x7B, + 0xDE, + 0x7B, + 0x8C, + 0x31, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_controls{ + {16, 16}, + bitmap_icon_controls_data}; static constexpr uint8_t bitmap_icon_pocsag_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xFC, 0x3F, - 0xFE, 0x7F, - 0x02, 0x40, - 0xBA, 0x45, - 0x02, 0x40, - 0xFE, 0x7F, - 0xFE, 0x7F, - 0x92, 0x7C, - 0x92, 0x7C, - 0xFC, 0x3F, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_pocsag { - { 16, 16 }, bitmap_icon_pocsag_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFC, + 0x3F, + 0xFE, + 0x7F, + 0x02, + 0x40, + 0xBA, + 0x45, + 0x02, + 0x40, + 0xFE, + 0x7F, + 0xFE, + 0x7F, + 0x92, + 0x7C, + 0x92, + 0x7C, + 0xFC, + 0x3F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_pocsag{ + {16, 16}, + bitmap_icon_pocsag_data}; static constexpr uint8_t bitmap_stop_data[] = { - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x8B, 0xCD, - 0xDD, 0xAA, - 0xDB, 0xCA, - 0xDB, 0xEA, - 0xDD, 0xED, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_stop { - { 16, 16 }, bitmap_stop_data -}; + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x8B, + 0xCD, + 0xDD, + 0xAA, + 0xDB, + 0xCA, + 0xDB, + 0xEA, + 0xDD, + 0xED, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_stop{ + {16, 16}, + bitmap_stop_data}; static constexpr uint8_t bitmap_icon_speaker_data[] = { - 0x00, 0x00, - 0x40, 0x10, - 0x60, 0x20, - 0x70, 0x44, - 0x78, 0x48, - 0x7F, 0x91, - 0x7F, 0x92, - 0x7F, 0x92, - 0x7F, 0x92, - 0x7F, 0x92, - 0x7F, 0x92, - 0x7F, 0x91, - 0x78, 0x48, - 0x70, 0x44, - 0x60, 0x20, - 0x40, 0x10, -}; -static constexpr Bitmap bitmap_icon_speaker { - { 16, 16 }, bitmap_icon_speaker_data -}; + 0x00, + 0x00, + 0x40, + 0x10, + 0x60, + 0x20, + 0x70, + 0x44, + 0x78, + 0x48, + 0x7F, + 0x91, + 0x7F, + 0x92, + 0x7F, + 0x92, + 0x7F, + 0x92, + 0x7F, + 0x92, + 0x7F, + 0x92, + 0x7F, + 0x91, + 0x78, + 0x48, + 0x70, + 0x44, + 0x60, + 0x20, + 0x40, + 0x10, +}; +static constexpr Bitmap bitmap_icon_speaker{ + {16, 16}, + bitmap_icon_speaker_data}; static constexpr uint8_t bitmap_icon_replay_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x0C, 0x00, - 0x3C, 0x00, - 0xFC, 0x00, - 0xFC, 0x03, - 0xFC, 0x0F, - 0xFC, 0x3F, - 0xFC, 0x3F, - 0xFC, 0x0F, - 0xFC, 0x03, - 0xFC, 0x00, - 0x3C, 0x00, - 0x0C, 0x00, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_replay { - { 16, 16 }, bitmap_icon_replay_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x00, + 0x3C, + 0x00, + 0xFC, + 0x00, + 0xFC, + 0x03, + 0xFC, + 0x0F, + 0xFC, + 0x3F, + 0xFC, + 0x3F, + 0xFC, + 0x0F, + 0xFC, + 0x03, + 0xFC, + 0x00, + 0x3C, + 0x00, + 0x0C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_replay{ + {16, 16}, + bitmap_icon_replay_data}; static constexpr uint8_t bitmap_icon_rename_numeric_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x0E, - 0x00, 0x04, - 0xFF, 0xF5, - 0x01, 0x84, - 0xC9, 0x85, - 0x0D, 0x85, - 0xC9, 0x85, - 0x49, 0x84, - 0xDD, 0x85, - 0x01, 0x84, - 0xFF, 0xF5, - 0x00, 0x04, - 0x00, 0x0E, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_rename_numeric { - { 16, 16 }, bitmap_icon_rename_numeric_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0E, + 0x00, + 0x04, + 0xFF, + 0xF5, + 0x01, + 0x84, + 0xC9, + 0x85, + 0x0D, + 0x85, + 0xC9, + 0x85, + 0x49, + 0x84, + 0xDD, + 0x85, + 0x01, + 0x84, + 0xFF, + 0xF5, + 0x00, + 0x04, + 0x00, + 0x0E, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_rename_numeric{ + {16, 16}, + bitmap_icon_rename_numeric_data}; static constexpr uint8_t bitmap_icon_transmit_data[] = { - 0x80, 0x01, - 0xC0, 0x03, - 0xE0, 0x07, - 0xB0, 0x0D, - 0x98, 0x19, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x80, 0x01, - 0x83, 0xC1, - 0x03, 0xC0, - 0xFF, 0xFF, - 0xFF, 0xFF, -}; -static constexpr Bitmap bitmap_icon_transmit { - { 16, 16 }, bitmap_icon_transmit_data -}; + 0x80, + 0x01, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xB0, + 0x0D, + 0x98, + 0x19, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x83, + 0xC1, + 0x03, + 0xC0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, +}; +static constexpr Bitmap bitmap_icon_transmit{ + {16, 16}, + bitmap_icon_transmit_data}; static constexpr uint8_t bitmap_icon_looking_data[] = { - 0xF8, 0x01, - 0xFC, 0x03, - 0x0E, 0x07, - 0x07, 0x0E, - 0xF3, 0x0C, - 0x9F, 0x0F, - 0x9F, 0x0F, - 0xF3, 0x0C, - 0x07, 0x0E, - 0x0E, 0x07, - 0xFC, 0x1F, - 0xF8, 0x3D, - 0x00, 0x7C, - 0x00, 0xF8, - 0x00, 0xF0, - 0x00, 0x60, -}; -static constexpr Bitmap bitmap_icon_looking { - { 16, 16 }, bitmap_icon_looking_data -}; + 0xF8, + 0x01, + 0xFC, + 0x03, + 0x0E, + 0x07, + 0x07, + 0x0E, + 0xF3, + 0x0C, + 0x9F, + 0x0F, + 0x9F, + 0x0F, + 0xF3, + 0x0C, + 0x07, + 0x0E, + 0x0E, + 0x07, + 0xFC, + 0x1F, + 0xF8, + 0x3D, + 0x00, + 0x7C, + 0x00, + 0xF8, + 0x00, + 0xF0, + 0x00, + 0x60, +}; +static constexpr Bitmap bitmap_icon_looking{ + {16, 16}, + bitmap_icon_looking_data}; static constexpr uint8_t bitmap_icon_delete_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0x0C, 0x30, - 0x1C, 0x38, - 0x38, 0x1C, - 0x70, 0x0E, - 0xE0, 0x07, - 0xC0, 0x03, - 0xC0, 0x03, - 0xE0, 0x07, - 0x70, 0x0E, - 0x38, 0x1C, - 0x1C, 0x38, - 0x0C, 0x30, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_delete { - { 16, 16 }, bitmap_icon_delete_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x30, + 0x1C, + 0x38, + 0x38, + 0x1C, + 0x70, + 0x0E, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0x70, + 0x0E, + 0x38, + 0x1C, + 0x1C, + 0x38, + 0x0C, + 0x30, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_delete{ + {16, 16}, + bitmap_icon_delete_data}; static constexpr uint8_t bitmap_icon_adsb_data[] = { - 0x80, 0x01, - 0xC0, 0x03, - 0xC0, 0x03, - 0xC0, 0x03, - 0xC0, 0x03, - 0xE0, 0x07, - 0xF8, 0x1F, - 0xFE, 0x7F, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xC0, 0x03, - 0xC0, 0x03, - 0xC0, 0x03, - 0xE0, 0x07, - 0xF0, 0x0F, - 0xF8, 0x1F, -}; -static constexpr Bitmap bitmap_icon_adsb { - { 16, 16 }, bitmap_icon_adsb_data -}; + 0x80, + 0x01, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xF8, + 0x1F, + 0xFE, + 0x7F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xF0, + 0x0F, + 0xF8, + 0x1F, +}; +static constexpr Bitmap bitmap_icon_adsb{ + {16, 16}, + bitmap_icon_adsb_data}; static constexpr uint8_t bitmap_record_data[] = { - 0xC0, 0x07, - 0xF0, 0x1F, - 0xF8, 0x3F, - 0xFC, 0x7F, - 0xFC, 0x7F, - 0x66, 0xCC, - 0x56, 0xF7, - 0x66, 0xF6, - 0x56, 0xF7, - 0x56, 0xCC, - 0xFC, 0x7F, - 0xFC, 0x7F, - 0xF8, 0x3F, - 0xF0, 0x1F, - 0xC0, 0x07, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_record { - { 16, 16 }, bitmap_record_data -}; + 0xC0, + 0x07, + 0xF0, + 0x1F, + 0xF8, + 0x3F, + 0xFC, + 0x7F, + 0xFC, + 0x7F, + 0x66, + 0xCC, + 0x56, + 0xF7, + 0x66, + 0xF6, + 0x56, + 0xF7, + 0x56, + 0xCC, + 0xFC, + 0x7F, + 0xFC, + 0x7F, + 0xF8, + 0x3F, + 0xF0, + 0x1F, + 0xC0, + 0x07, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_record{ + {16, 16}, + bitmap_record_data}; static constexpr uint8_t bitmap_icon_options_touch_data[] = { - 0xC7, 0xF1, - 0x97, 0xF4, - 0x27, 0xF2, - 0x8F, 0xF8, - 0x5F, 0xFD, - 0x47, 0xFD, - 0x53, 0xC1, - 0x4B, 0x9F, - 0x43, 0xB5, - 0x6F, 0xA0, - 0x2F, 0xA0, - 0x20, 0x20, - 0x60, 0x20, - 0x40, 0x10, - 0xC0, 0x1F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_options_touch { - { 16, 16 }, bitmap_icon_options_touch_data -}; + 0xC7, + 0xF1, + 0x97, + 0xF4, + 0x27, + 0xF2, + 0x8F, + 0xF8, + 0x5F, + 0xFD, + 0x47, + 0xFD, + 0x53, + 0xC1, + 0x4B, + 0x9F, + 0x43, + 0xB5, + 0x6F, + 0xA0, + 0x2F, + 0xA0, + 0x20, + 0x20, + 0x60, + 0x20, + 0x40, + 0x10, + 0xC0, + 0x1F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_options_touch{ + {16, 16}, + bitmap_icon_options_touch_data}; static constexpr uint8_t bitmap_icon_peripherals_data[] = { - 0x54, 0x01, - 0x54, 0x01, - 0xFF, 0x07, - 0x7C, 0x01, - 0xBF, 0x07, - 0xDC, 0x18, - 0x6F, 0x10, - 0x2C, 0x21, - 0xAF, 0x20, - 0x34, 0x20, - 0x54, 0x10, - 0xC0, 0x38, - 0x00, 0x77, - 0x00, 0xE0, - 0x00, 0xC0, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_peripherals { - { 16, 16 }, bitmap_icon_peripherals_data -}; + 0x54, + 0x01, + 0x54, + 0x01, + 0xFF, + 0x07, + 0x7C, + 0x01, + 0xBF, + 0x07, + 0xDC, + 0x18, + 0x6F, + 0x10, + 0x2C, + 0x21, + 0xAF, + 0x20, + 0x34, + 0x20, + 0x54, + 0x10, + 0xC0, + 0x38, + 0x00, + 0x77, + 0x00, + 0xE0, + 0x00, + 0xC0, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_peripherals{ + {16, 16}, + bitmap_icon_peripherals_data}; static constexpr uint8_t bitmap_icon_capture_data[] = { - 0xE0, 0x07, - 0xF8, 0x1F, - 0xFC, 0x3F, - 0xFE, 0x7F, - 0xFE, 0x7F, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFE, 0x7F, - 0xFE, 0x7F, - 0xFC, 0x3F, - 0xF8, 0x1F, - 0xE0, 0x07, -}; -static constexpr Bitmap bitmap_icon_capture { - { 16, 16 }, bitmap_icon_capture_data -}; + 0xE0, + 0x07, + 0xF8, + 0x1F, + 0xFC, + 0x3F, + 0xFE, + 0x7F, + 0xFE, + 0x7F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFE, + 0x7F, + 0xFE, + 0x7F, + 0xFC, + 0x3F, + 0xF8, + 0x1F, + 0xE0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_capture{ + {16, 16}, + bitmap_icon_capture_data}; static constexpr uint8_t bitmap_icon_nrf_data[] = { - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0x00, 0x01, - 0xF8, 0x3F, - 0xFC, 0x7F, - 0xFC, 0x7F, - 0xDC, 0x7F, - 0x8C, 0x6B, - 0xDC, 0x7F, - 0xFC, 0x7F, - 0xFC, 0x7F, - 0xF8, 0x3F, -}; -static constexpr Bitmap bitmap_icon_nrf { - { 16, 16 }, bitmap_icon_nrf_data -}; + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x01, + 0xF8, + 0x3F, + 0xFC, + 0x7F, + 0xFC, + 0x7F, + 0xDC, + 0x7F, + 0x8C, + 0x6B, + 0xDC, + 0x7F, + 0xFC, + 0x7F, + 0xFC, + 0x7F, + 0xF8, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_nrf{ + {16, 16}, + bitmap_icon_nrf_data}; static constexpr uint8_t bitmap_sig_noise_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, - 0x00, 0x30, 0x80, 0x00, - 0x00, 0x30, 0x80, 0x01, - 0x40, 0x30, 0xC0, 0x03, - 0xC0, 0x30, 0xC0, 0x03, - 0xC0, 0x39, 0xC0, 0x72, - 0xC0, 0x7B, 0x60, 0x76, - 0x60, 0x6E, 0x60, 0x1E, - 0x60, 0xCE, 0x6C, 0x0C, - 0x66, 0xC4, 0x6E, 0x0C, - 0x66, 0xC0, 0x3E, 0x00, - 0x2C, 0xC0, 0x3B, 0x00, - 0x3C, 0xC0, 0x39, 0x00, - 0x3C, 0xC0, 0x18, 0x00, - 0x18, 0x40, 0x10, 0x00, - 0x18, 0x40, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_noise { - { 32, 32 }, bitmap_sig_noise_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x20, + 0x00, + 0x00, + 0x00, + 0x30, + 0x80, + 0x00, + 0x00, + 0x30, + 0x80, + 0x01, + 0x40, + 0x30, + 0xC0, + 0x03, + 0xC0, + 0x30, + 0xC0, + 0x03, + 0xC0, + 0x39, + 0xC0, + 0x72, + 0xC0, + 0x7B, + 0x60, + 0x76, + 0x60, + 0x6E, + 0x60, + 0x1E, + 0x60, + 0xCE, + 0x6C, + 0x0C, + 0x66, + 0xC4, + 0x6E, + 0x0C, + 0x66, + 0xC0, + 0x3E, + 0x00, + 0x2C, + 0xC0, + 0x3B, + 0x00, + 0x3C, + 0xC0, + 0x39, + 0x00, + 0x3C, + 0xC0, + 0x18, + 0x00, + 0x18, + 0x40, + 0x10, + 0x00, + 0x18, + 0x40, + 0x10, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_noise{ + {32, 32}, + bitmap_sig_noise_data}; static constexpr uint8_t bitmap_icon_ais_data[] = { - 0x00, 0x01, - 0x80, 0x01, - 0xC0, 0x01, - 0xC0, 0x0D, - 0xE0, 0x3D, - 0xF0, 0x3D, - 0xF8, 0x7D, - 0xFC, 0x7D, - 0xFC, 0x7D, - 0xFE, 0x7D, - 0xFF, 0x7D, - 0x00, 0x00, - 0xF8, 0x7F, - 0xF8, 0x3F, - 0xF0, 0x0F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_ais { - { 16, 16 }, bitmap_icon_ais_data -}; + 0x00, + 0x01, + 0x80, + 0x01, + 0xC0, + 0x01, + 0xC0, + 0x0D, + 0xE0, + 0x3D, + 0xF0, + 0x3D, + 0xF8, + 0x7D, + 0xFC, + 0x7D, + 0xFC, + 0x7D, + 0xFE, + 0x7D, + 0xFF, + 0x7D, + 0x00, + 0x00, + 0xF8, + 0x7F, + 0xF8, + 0x3F, + 0xF0, + 0x0F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_ais{ + {16, 16}, + bitmap_icon_ais_data}; static constexpr uint8_t bitmap_titlebar_image_data[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0xC7, 0x8B, 0x9E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xA2, 0x28, 0x48, 0xA6, 0x81, 0x31, 0x67, 0x36, 0x73, 0x63, - 0xBE, 0x2F, 0x38, 0x9E, 0x87, 0xBB, 0x6D, 0x36, 0x1B, 0x77, - 0xA2, 0x28, 0x48, 0xA6, 0x81, 0xBF, 0x6D, 0x36, 0x1B, 0x7F, - 0xA2, 0xC8, 0x8B, 0xA6, 0x81, 0xB5, 0xCF, 0xF3, 0x7B, 0x6B, - 0x00, 0x00, 0x00, 0x00, 0x80, 0xB1, 0x8D, 0x31, 0x1B, 0x63, - 0xEE, 0xEE, 0xEE, 0xEE, 0x8A, 0xB1, 0x8D, 0x31, 0x1B, 0x63, - 0xAE, 0x46, 0xEE, 0x2E, 0x86, 0xB1, 0x8D, 0x31, 0x73, 0x63, - 0xE2, 0x4A, 0x2A, 0xEA, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_titlebar_image { - { 80, 16 }, bitmap_titlebar_image_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x22, + 0xC7, + 0x8B, + 0x9E, + 0x0F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xA2, + 0x28, + 0x48, + 0xA6, + 0x81, + 0x31, + 0x67, + 0x36, + 0x73, + 0x63, + 0xBE, + 0x2F, + 0x38, + 0x9E, + 0x87, + 0xBB, + 0x6D, + 0x36, + 0x1B, + 0x77, + 0xA2, + 0x28, + 0x48, + 0xA6, + 0x81, + 0xBF, + 0x6D, + 0x36, + 0x1B, + 0x7F, + 0xA2, + 0xC8, + 0x8B, + 0xA6, + 0x81, + 0xB5, + 0xCF, + 0xF3, + 0x7B, + 0x6B, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0xB1, + 0x8D, + 0x31, + 0x1B, + 0x63, + 0xEE, + 0xEE, + 0xEE, + 0xEE, + 0x8A, + 0xB1, + 0x8D, + 0x31, + 0x1B, + 0x63, + 0xAE, + 0x46, + 0xEE, + 0x2E, + 0x86, + 0xB1, + 0x8D, + 0x31, + 0x73, + 0x63, + 0xE2, + 0x4A, + 0x2A, + 0xEA, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_titlebar_image{ + {80, 16}, + bitmap_titlebar_image_data}; static constexpr uint8_t bitmap_icon_btle_data[] = { - 0xE0, 0x03, - 0x30, 0x07, - 0x38, 0x0E, - 0x3C, 0x1C, - 0x24, 0x19, - 0x0C, 0x13, - 0x1C, 0x19, - 0x3C, 0x1C, - 0x3C, 0x1C, - 0x1C, 0x19, - 0x0C, 0x13, - 0x24, 0x19, - 0x3C, 0x1C, - 0x38, 0x0E, - 0x30, 0x07, - 0xE0, 0x03, -}; -static constexpr Bitmap bitmap_icon_btle { - { 16, 16 }, bitmap_icon_btle_data -}; + 0xE0, + 0x03, + 0x30, + 0x07, + 0x38, + 0x0E, + 0x3C, + 0x1C, + 0x24, + 0x19, + 0x0C, + 0x13, + 0x1C, + 0x19, + 0x3C, + 0x1C, + 0x3C, + 0x1C, + 0x1C, + 0x19, + 0x0C, + 0x13, + 0x24, + 0x19, + 0x3C, + 0x1C, + 0x38, + 0x0E, + 0x30, + 0x07, + 0xE0, + 0x03, +}; +static constexpr Bitmap bitmap_icon_btle{ + {16, 16}, + bitmap_icon_btle_data}; static constexpr uint8_t bitmap_sd_card_error_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xC0, 0x1F, - 0xE0, 0x1F, - 0xF0, 0x1F, - 0xF8, 0x1F, - 0xC8, 0x13, - 0x98, 0x19, - 0x38, 0x1C, - 0x78, 0x1E, - 0x38, 0x1C, - 0x98, 0x19, - 0xC8, 0x13, - 0xF8, 0x1F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sd_card_error { - { 16, 16 }, bitmap_sd_card_error_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x1F, + 0xE0, + 0x1F, + 0xF0, + 0x1F, + 0xF8, + 0x1F, + 0xC8, + 0x13, + 0x98, + 0x19, + 0x38, + 0x1C, + 0x78, + 0x1E, + 0x38, + 0x1C, + 0x98, + 0x19, + 0xC8, + 0x13, + 0xF8, + 0x1F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sd_card_error{ + {16, 16}, + bitmap_sd_card_error_data}; static constexpr uint8_t bitmap_sig_saw_up_data[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x01, 0x70, - 0x06, 0x80, 0x01, 0x78, - 0x06, 0xC0, 0x01, 0x7C, - 0x06, 0xE0, 0x01, 0x6E, - 0x06, 0xF0, 0x01, 0x67, - 0x06, 0xB8, 0x81, 0x63, - 0x06, 0x9C, 0xC1, 0x61, - 0x06, 0x8E, 0xE1, 0x60, - 0x06, 0x87, 0x71, 0x60, - 0x86, 0x83, 0x39, 0x60, - 0xC6, 0x81, 0x1D, 0x60, - 0xE6, 0x80, 0x0F, 0x60, - 0x76, 0x80, 0x07, 0x60, - 0x3E, 0x80, 0x03, 0x60, - 0x1E, 0x80, 0x01, 0x60, - 0x0E, 0x80, 0x00, 0x60, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sig_saw_up { - { 32, 32 }, bitmap_sig_saw_up_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x00, + 0x01, + 0x70, + 0x06, + 0x80, + 0x01, + 0x78, + 0x06, + 0xC0, + 0x01, + 0x7C, + 0x06, + 0xE0, + 0x01, + 0x6E, + 0x06, + 0xF0, + 0x01, + 0x67, + 0x06, + 0xB8, + 0x81, + 0x63, + 0x06, + 0x9C, + 0xC1, + 0x61, + 0x06, + 0x8E, + 0xE1, + 0x60, + 0x06, + 0x87, + 0x71, + 0x60, + 0x86, + 0x83, + 0x39, + 0x60, + 0xC6, + 0x81, + 0x1D, + 0x60, + 0xE6, + 0x80, + 0x0F, + 0x60, + 0x76, + 0x80, + 0x07, + 0x60, + 0x3E, + 0x80, + 0x03, + 0x60, + 0x1E, + 0x80, + 0x01, + 0x60, + 0x0E, + 0x80, + 0x00, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sig_saw_up{ + {32, 32}, + bitmap_sig_saw_up_data}; static constexpr uint8_t bitmap_icon_microphone_data[] = { - 0xC0, 0x03, - 0xE0, 0x07, - 0xE0, 0x07, - 0xE0, 0x07, - 0xE8, 0x17, - 0xE8, 0x17, - 0xE8, 0x17, - 0xE8, 0x17, - 0xE8, 0x17, - 0xC8, 0x13, - 0x18, 0x18, - 0xF0, 0x0F, - 0xC0, 0x03, - 0x80, 0x01, - 0x80, 0x01, - 0xE0, 0x07, -}; -static constexpr Bitmap bitmap_icon_microphone { - { 16, 16 }, bitmap_icon_microphone_data -}; + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xE0, + 0x07, + 0xE0, + 0x07, + 0xE8, + 0x17, + 0xE8, + 0x17, + 0xE8, + 0x17, + 0xE8, + 0x17, + 0xE8, + 0x17, + 0xC8, + 0x13, + 0x18, + 0x18, + 0xF0, + 0x0F, + 0xC0, + 0x03, + 0x80, + 0x01, + 0x80, + 0x01, + 0xE0, + 0x07, +}; +static constexpr Bitmap bitmap_icon_microphone{ + {16, 16}, + bitmap_icon_microphone_data}; static constexpr uint8_t bitmap_bulb_on_data[] = { - 0x04, 0x3C, 0x20, - 0x08, 0xFF, 0x10, - 0x90, 0xFF, 0x09, - 0xC0, 0xFF, 0x03, - 0xE0, 0xFF, 0x07, - 0xE0, 0xFF, 0x07, - 0xF0, 0xE7, 0x0F, - 0xF0, 0xBD, 0x0F, - 0xF7, 0xBD, 0xEF, - 0xF0, 0xDB, 0x0F, - 0xF0, 0xDB, 0x0F, - 0xE0, 0xDB, 0x07, - 0xE0, 0xCB, 0x07, - 0xC0, 0xD3, 0x03, - 0x90, 0xCB, 0x09, - 0x08, 0xFD, 0x10, - 0x04, 0xE3, 0x20, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0x42, 0x00, - 0x00, 0x3C, 0x00, -}; -static constexpr Bitmap bitmap_bulb_on { - { 24, 24 }, bitmap_bulb_on_data -}; + 0x04, + 0x3C, + 0x20, + 0x08, + 0xFF, + 0x10, + 0x90, + 0xFF, + 0x09, + 0xC0, + 0xFF, + 0x03, + 0xE0, + 0xFF, + 0x07, + 0xE0, + 0xFF, + 0x07, + 0xF0, + 0xE7, + 0x0F, + 0xF0, + 0xBD, + 0x0F, + 0xF7, + 0xBD, + 0xEF, + 0xF0, + 0xDB, + 0x0F, + 0xF0, + 0xDB, + 0x0F, + 0xE0, + 0xDB, + 0x07, + 0xE0, + 0xCB, + 0x07, + 0xC0, + 0xD3, + 0x03, + 0x90, + 0xCB, + 0x09, + 0x08, + 0xFD, + 0x10, + 0x04, + 0xE3, + 0x20, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0x42, + 0x00, + 0x00, + 0x3C, + 0x00, +}; +static constexpr Bitmap bitmap_bulb_on{ + {24, 24}, + bitmap_bulb_on_data}; static constexpr uint8_t bitmap_sd_card_unknown_data[] = { - 0x00, 0x00, - 0x00, 0x00, - 0xC0, 0x1F, - 0xE0, 0x1F, - 0xF0, 0x1F, - 0xF8, 0x1F, - 0x38, 0x1C, - 0x98, 0x19, - 0xF8, 0x1C, - 0x78, 0x1E, - 0x78, 0x1E, - 0xF8, 0x1F, - 0x78, 0x1E, - 0xF8, 0x1F, - 0x00, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_sd_card_unknown { - { 16, 16 }, bitmap_sd_card_unknown_data -}; + 0x00, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x1F, + 0xE0, + 0x1F, + 0xF0, + 0x1F, + 0xF8, + 0x1F, + 0x38, + 0x1C, + 0x98, + 0x19, + 0xF8, + 0x1C, + 0x78, + 0x1E, + 0x78, + 0x1E, + 0xF8, + 0x1F, + 0x78, + 0x1E, + 0xF8, + 0x1F, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_sd_card_unknown{ + {16, 16}, + bitmap_sd_card_unknown_data}; static constexpr uint8_t bitmap_icon_file_text_data[] = { - 0xFC, 0x03, - 0x04, 0x06, - 0x04, 0x0E, - 0x04, 0x1E, - 0xF4, 0x3E, - 0x04, 0x20, - 0xF4, 0x2F, - 0x04, 0x20, - 0xF4, 0x2F, - 0x04, 0x20, - 0xF4, 0x2F, - 0x04, 0x20, - 0xF4, 0x2F, - 0x04, 0x20, - 0x04, 0x20, - 0xFC, 0x3F, -}; -static constexpr Bitmap bitmap_icon_file_text { - { 16, 16 }, bitmap_icon_file_text_data -}; + 0xFC, + 0x03, + 0x04, + 0x06, + 0x04, + 0x0E, + 0x04, + 0x1E, + 0xF4, + 0x3E, + 0x04, + 0x20, + 0xF4, + 0x2F, + 0x04, + 0x20, + 0xF4, + 0x2F, + 0x04, + 0x20, + 0xF4, + 0x2F, + 0x04, + 0x20, + 0xF4, + 0x2F, + 0x04, + 0x20, + 0x04, + 0x20, + 0xFC, + 0x3F, +}; +static constexpr Bitmap bitmap_icon_file_text{ + {16, 16}, + bitmap_icon_file_text_data}; static constexpr uint8_t bitmap_icon_ert_data[] = { - 0x00, 0x00, - 0x00, 0x0F, - 0x80, 0x7F, - 0xC0, 0x0F, - 0xFC, 0x0F, - 0xC2, 0x0F, - 0x82, 0x7F, - 0x01, 0x0F, - 0x01, 0x00, - 0x21, 0x05, - 0x53, 0x09, - 0x56, 0x09, - 0x50, 0x05, - 0x50, 0x05, - 0x20, 0xAD, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_ert { - { 16, 16 }, bitmap_icon_ert_data -}; + 0x00, + 0x00, + 0x00, + 0x0F, + 0x80, + 0x7F, + 0xC0, + 0x0F, + 0xFC, + 0x0F, + 0xC2, + 0x0F, + 0x82, + 0x7F, + 0x01, + 0x0F, + 0x01, + 0x00, + 0x21, + 0x05, + 0x53, + 0x09, + 0x56, + 0x09, + 0x50, + 0x05, + 0x50, + 0x05, + 0x20, + 0xAD, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_ert{ + {16, 16}, + bitmap_icon_ert_data}; static constexpr uint8_t bitmap_icon_peripherals_details_data[] = { - 0x54, 0x01, - 0x54, 0x01, - 0xFF, 0x07, - 0xFC, 0x01, - 0x3F, 0x00, - 0xBC, 0x3F, - 0xBF, 0x60, - 0xBC, 0xEE, - 0xBF, 0x80, - 0x94, 0xBE, - 0x94, 0x80, - 0x80, 0xBE, - 0x80, 0x80, - 0x80, 0xBE, - 0x80, 0x80, - 0x80, 0xFF, -}; -static constexpr Bitmap bitmap_icon_peripherals_details { - { 16, 16 }, bitmap_icon_peripherals_details_data -}; + 0x54, + 0x01, + 0x54, + 0x01, + 0xFF, + 0x07, + 0xFC, + 0x01, + 0x3F, + 0x00, + 0xBC, + 0x3F, + 0xBF, + 0x60, + 0xBC, + 0xEE, + 0xBF, + 0x80, + 0x94, + 0xBE, + 0x94, + 0x80, + 0x80, + 0xBE, + 0x80, + 0x80, + 0x80, + 0xBE, + 0x80, + 0x80, + 0x80, + 0xFF, +}; +static constexpr Bitmap bitmap_icon_peripherals_details{ + {16, 16}, + bitmap_icon_peripherals_details_data}; static constexpr uint8_t bitmap_icon_paste_data[] = { - 0x00, 0x00, - 0xE0, 0x00, - 0x18, 0x03, - 0xE4, 0x04, - 0x04, 0x04, - 0x04, 0x04, - 0x84, 0x3F, - 0x84, 0x20, - 0x84, 0x2E, - 0x84, 0x20, - 0x84, 0x2E, - 0x84, 0x20, - 0x84, 0x2E, - 0xF8, 0x20, - 0x80, 0x3F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_paste { - { 16, 16 }, bitmap_icon_paste_data -}; + 0x00, + 0x00, + 0xE0, + 0x00, + 0x18, + 0x03, + 0xE4, + 0x04, + 0x04, + 0x04, + 0x04, + 0x04, + 0x84, + 0x3F, + 0x84, + 0x20, + 0x84, + 0x2E, + 0x84, + 0x20, + 0x84, + 0x2E, + 0x84, + 0x20, + 0x84, + 0x2E, + 0xF8, + 0x20, + 0x80, + 0x3F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_paste{ + {16, 16}, + bitmap_icon_paste_data}; static constexpr uint8_t bitmap_bulb_ignore_data[] = { - 0x00, 0x3C, 0x00, - 0x00, 0xC3, 0x00, - 0x80, 0x00, 0x01, - 0x40, 0x3C, 0x02, - 0x20, 0x7E, 0x04, - 0x20, 0xE7, 0x04, - 0x10, 0xC3, 0x08, - 0x10, 0xE3, 0x08, - 0x10, 0x70, 0x08, - 0x10, 0x38, 0x08, - 0x10, 0x18, 0x08, - 0x20, 0x18, 0x04, - 0x20, 0x00, 0x04, - 0x40, 0x18, 0x02, - 0x80, 0x18, 0x01, - 0x00, 0xC3, 0x00, - 0x00, 0xFF, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0xC3, 0x00, - 0x00, 0xBD, 0x00, - 0x00, 0x42, 0x00, - 0x00, 0x3C, 0x00, -}; -static constexpr Bitmap bitmap_bulb_ignore { - { 24, 24 }, bitmap_bulb_ignore_data -}; + 0x00, + 0x3C, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x80, + 0x00, + 0x01, + 0x40, + 0x3C, + 0x02, + 0x20, + 0x7E, + 0x04, + 0x20, + 0xE7, + 0x04, + 0x10, + 0xC3, + 0x08, + 0x10, + 0xE3, + 0x08, + 0x10, + 0x70, + 0x08, + 0x10, + 0x38, + 0x08, + 0x10, + 0x18, + 0x08, + 0x20, + 0x18, + 0x04, + 0x20, + 0x00, + 0x04, + 0x40, + 0x18, + 0x02, + 0x80, + 0x18, + 0x01, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xFF, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0xC3, + 0x00, + 0x00, + 0xBD, + 0x00, + 0x00, + 0x42, + 0x00, + 0x00, + 0x3C, + 0x00, +}; +static constexpr Bitmap bitmap_bulb_ignore{ + {24, 24}, + bitmap_bulb_ignore_data}; static constexpr uint8_t bitmap_icon_gps_sim_data[] = { - 0xC0, 0x07, - 0xE0, 0x0F, - 0x70, 0x1F, - 0x78, 0x3E, - 0x78, 0x3C, - 0x78, 0x38, - 0x78, 0x30, - 0x78, 0x38, - 0x78, 0x3C, - 0x70, 0x1E, - 0x70, 0x1F, - 0xE0, 0x0F, - 0xC0, 0x07, - 0x80, 0x03, - 0x20, 0x09, - 0x50, 0x14, -}; -static constexpr Bitmap bitmap_icon_gps_sim { - { 16, 16 }, bitmap_icon_gps_sim_data -}; + 0xC0, + 0x07, + 0xE0, + 0x0F, + 0x70, + 0x1F, + 0x78, + 0x3E, + 0x78, + 0x3C, + 0x78, + 0x38, + 0x78, + 0x30, + 0x78, + 0x38, + 0x78, + 0x3C, + 0x70, + 0x1E, + 0x70, + 0x1F, + 0xE0, + 0x0F, + 0xC0, + 0x07, + 0x80, + 0x03, + 0x20, + 0x09, + 0x50, + 0x14, +}; +static constexpr Bitmap bitmap_icon_gps_sim{ + {16, 16}, + bitmap_icon_gps_sim_data}; static constexpr uint8_t bitmap_icon_options_datetime_data[] = { - 0x0C, 0x06, - 0xFF, 0x1F, - 0x49, 0x12, - 0x49, 0x12, - 0xFF, 0x1F, - 0x49, 0x00, - 0x49, 0x1C, - 0x7F, 0x63, - 0x09, 0x49, - 0x89, 0x88, - 0xBE, 0xB8, - 0x80, 0x80, - 0x00, 0x41, - 0x00, 0x63, - 0x00, 0x1C, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_options_datetime { - { 16, 16 }, bitmap_icon_options_datetime_data -}; + 0x0C, + 0x06, + 0xFF, + 0x1F, + 0x49, + 0x12, + 0x49, + 0x12, + 0xFF, + 0x1F, + 0x49, + 0x00, + 0x49, + 0x1C, + 0x7F, + 0x63, + 0x09, + 0x49, + 0x89, + 0x88, + 0xBE, + 0xB8, + 0x80, + 0x80, + 0x00, + 0x41, + 0x00, + 0x63, + 0x00, + 0x1C, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_options_datetime{ + {16, 16}, + bitmap_icon_options_datetime_data}; static constexpr uint8_t bitmap_target_verify_data[] = { - 0x00, 0xE0, 0x07, 0x00, - 0x00, 0xFC, 0x3F, 0x00, - 0x00, 0x1F, 0xF8, 0x00, - 0xC0, 0x03, 0xC0, 0x03, - 0xE0, 0x00, 0x00, 0x07, - 0x70, 0x00, 0x00, 0x0E, - 0x38, 0x00, 0x00, 0x1C, - 0x18, 0x00, 0x00, 0x18, - 0x0C, 0x00, 0x00, 0x30, - 0x0C, 0x00, 0x00, 0x30, - 0x06, 0x00, 0x00, 0x60, - 0x06, 0x00, 0x00, 0x60, - 0x06, 0x00, 0x00, 0x60, - 0x03, 0x80, 0x01, 0xC0, - 0x03, 0x80, 0x01, 0xC0, - 0x03, 0xE0, 0x07, 0xC0, - 0x03, 0xE0, 0x07, 0xC0, - 0x03, 0x80, 0x01, 0xC0, - 0x03, 0x80, 0x01, 0xC0, - 0x06, 0x00, 0x00, 0x60, - 0x06, 0x00, 0x00, 0x60, - 0x06, 0x00, 0x00, 0x60, - 0x0C, 0x00, 0x00, 0x30, - 0x0C, 0x00, 0x00, 0x30, - 0x18, 0x00, 0x00, 0x18, - 0x38, 0x00, 0x00, 0x1C, - 0x70, 0x00, 0x00, 0x0E, - 0xE0, 0x00, 0x00, 0x07, - 0xC0, 0x03, 0xC0, 0x03, - 0x00, 0x1F, 0xF8, 0x00, - 0x00, 0xFC, 0x3F, 0x00, - 0x00, 0xE0, 0x07, 0x00, -}; -static constexpr Bitmap bitmap_target_verify { - { 32, 32 }, bitmap_target_verify_data -}; + 0x00, + 0xE0, + 0x07, + 0x00, + 0x00, + 0xFC, + 0x3F, + 0x00, + 0x00, + 0x1F, + 0xF8, + 0x00, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0xE0, + 0x00, + 0x00, + 0x07, + 0x70, + 0x00, + 0x00, + 0x0E, + 0x38, + 0x00, + 0x00, + 0x1C, + 0x18, + 0x00, + 0x00, + 0x18, + 0x0C, + 0x00, + 0x00, + 0x30, + 0x0C, + 0x00, + 0x00, + 0x30, + 0x06, + 0x00, + 0x00, + 0x60, + 0x06, + 0x00, + 0x00, + 0x60, + 0x06, + 0x00, + 0x00, + 0x60, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x06, + 0x00, + 0x00, + 0x60, + 0x06, + 0x00, + 0x00, + 0x60, + 0x06, + 0x00, + 0x00, + 0x60, + 0x0C, + 0x00, + 0x00, + 0x30, + 0x0C, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x00, + 0x18, + 0x38, + 0x00, + 0x00, + 0x1C, + 0x70, + 0x00, + 0x00, + 0x0E, + 0xE0, + 0x00, + 0x00, + 0x07, + 0xC0, + 0x03, + 0xC0, + 0x03, + 0x00, + 0x1F, + 0xF8, + 0x00, + 0x00, + 0xFC, + 0x3F, + 0x00, + 0x00, + 0xE0, + 0x07, + 0x00, +}; +static constexpr Bitmap bitmap_target_verify{ + {32, 32}, + bitmap_target_verify_data}; static constexpr uint8_t bitmap_icon_new_category_data[] = { - 0x00, 0x18, - 0x3E, 0x18, - 0x41, 0x7E, - 0xC1, 0x7E, - 0xFF, 0x18, - 0xFF, 0xDB, - 0xFF, 0xC3, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xFF, 0xFF, - 0xAF, 0xEA, - 0x57, 0xF5, - 0xEF, 0xEF, - 0xF7, 0xF7, - 0xEE, 0x6F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_new_category { - { 16, 16 }, bitmap_icon_new_category_data -}; + 0x00, + 0x18, + 0x3E, + 0x18, + 0x41, + 0x7E, + 0xC1, + 0x7E, + 0xFF, + 0x18, + 0xFF, + 0xDB, + 0xFF, + 0xC3, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xAF, + 0xEA, + 0x57, + 0xF5, + 0xEF, + 0xEF, + 0xF7, + 0xF7, + 0xEE, + 0x6F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_new_category{ + {16, 16}, + bitmap_icon_new_category_data}; static constexpr uint8_t bitmap_target_data[] = { - 0x80, 0x00, - 0x80, 0x00, - 0xE0, 0x03, - 0x90, 0x04, - 0x88, 0x08, - 0x04, 0x10, - 0x04, 0x10, - 0x1F, 0x7C, - 0x04, 0x10, - 0x04, 0x10, - 0x88, 0x08, - 0x90, 0x04, - 0xE0, 0x03, - 0x80, 0x00, - 0x80, 0x00, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_target { - { 16, 16 }, bitmap_target_data -}; + 0x80, + 0x00, + 0x80, + 0x00, + 0xE0, + 0x03, + 0x90, + 0x04, + 0x88, + 0x08, + 0x04, + 0x10, + 0x04, + 0x10, + 0x1F, + 0x7C, + 0x04, + 0x10, + 0x04, + 0x10, + 0x88, + 0x08, + 0x90, + 0x04, + 0xE0, + 0x03, + 0x80, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_target{ + {16, 16}, + bitmap_target_data}; static constexpr uint8_t bitmap_icon_new_dir_data[] = { - 0x00, 0x00, - 0x1E, 0x00, - 0x21, 0x00, - 0xE1, 0x7F, - 0x01, 0xC0, - 0x81, 0x81, - 0x81, 0x81, - 0x81, 0x81, - 0xF1, 0x8F, - 0xF1, 0x8F, - 0x81, 0x81, - 0x81, 0x81, - 0x81, 0x81, - 0x03, 0xC0, - 0xFE, 0x7F, - 0x00, 0x00, -}; -static constexpr Bitmap bitmap_icon_new_dir { - { 16, 16 }, bitmap_icon_new_dir_data -}; - + 0x00, + 0x00, + 0x1E, + 0x00, + 0x21, + 0x00, + 0xE1, + 0x7F, + 0x01, + 0xC0, + 0x81, + 0x81, + 0x81, + 0x81, + 0x81, + 0x81, + 0xF1, + 0x8F, + 0xF1, + 0x8F, + 0x81, + 0x81, + 0x81, + 0x81, + 0x81, + 0x81, + 0x03, + 0xC0, + 0xFE, + 0x7F, + 0x00, + 0x00, +}; +static constexpr Bitmap bitmap_icon_new_dir{ + {16, 16}, + bitmap_icon_new_dir_data}; } /* namespace ui */ -#endif/*__BITMAP_HPP__*/ +#endif /*__BITMAP_HPP__*/ diff --git a/firmware/application/bitmaps/bmp_modal_warning.hpp b/firmware/application/bitmaps/bmp_modal_warning.hpp index 7c49d21ef..4af35e884 100644 --- a/firmware/application/bitmaps/bmp_modal_warning.hpp +++ b/firmware/application/bitmaps/bmp_modal_warning.hpp @@ -1,73 +1,72 @@ unsigned char modal_warning_bmp[] = { - 0x42, 0x4d, 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, - 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x02, - 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0x00, 0x00, 0x5a, - 0xff, 0x00, 0x78, 0x78, 0x78, 0x00, 0x00, 0x7c, 0xff, 0x00, 0x00, 0xa9, - 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x03, 0x24, 0x66, 0x02, 0x30, - 0x00, 0x00, 0x02, 0x36, 0x24, 0x66, 0x02, 0x63, 0x00, 0x00, 0x02, 0x66, - 0x02, 0x63, 0x20, 0x00, 0x02, 0x36, 0x02, 0x66, 0x00, 0x00, 0x02, 0x36, - 0x02, 0x60, 0x20, 0x00, 0x02, 0x06, 0x02, 0x63, 0x00, 0x00, 0x02, 0x06, - 0x02, 0x60, 0x0c, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30, 0x0c, 0x00, - 0x02, 0x06, 0x02, 0x60, 0x00, 0x00, 0x02, 0x03, 0x02, 0x66, 0x0c, 0x00, - 0x00, 0x08, 0x06, 0x11, 0x11, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x30, - 0x00, 0x00, 0x02, 0x00, 0x02, 0x66, 0x0c, 0x00, 0x00, 0x08, 0x06, 0x21, - 0x21, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x36, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x42, 0x42, 0x60, - 0x0a, 0x00, 0x00, 0x06, 0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x06, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x54, 0x45, 0x60, - 0x0a, 0x00, 0x00, 0x06, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x03, 0x66, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30, - 0x0a, 0x00, 0x00, 0x06, 0x66, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x66, 0x00, 0x1c, 0x00, 0x00, 0x06, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x36, 0x60, 0x18, 0x00, 0x00, 0x08, - 0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x60, - 0x0a, 0x00, 0x02, 0x36, 0x02, 0x63, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x66, 0x08, 0x00, - 0x00, 0x08, 0x03, 0x61, 0x16, 0x30, 0x08, 0x00, 0x00, 0x08, 0x66, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x08, - 0x06, 0x11, 0x11, 0x60, 0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x02, 0x36, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, - 0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x63, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, - 0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x02, 0x03, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, - 0x11, 0x60, 0x06, 0x00, 0x02, 0x66, 0x02, 0x30, 0x06, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, 0x11, 0x60, - 0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, - 0x36, 0x60, 0x00, 0x00, 0x06, 0x11, 0x11, 0x60, 0x00, 0x00, 0x06, 0x63, - 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x06, 0x60, 0x00, 0x00, - 0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x06, 0x60, 0x08, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x18, 0x03, 0x66, 0x00, 0x00, 0x06, 0x12, 0x12, 0x60, - 0x00, 0x00, 0x66, 0x30, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, - 0x66, 0x00, 0x00, 0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x66, 0x0a, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x36, 0x60, 0x00, 0x06, 0x22, 0x22, - 0x60, 0x00, 0x06, 0x63, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, - 0x06, 0x60, 0x00, 0x06, 0x24, 0x24, 0x60, 0x00, 0x06, 0x60, 0x0a, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x03, 0x66, 0x00, 0x06, 0x42, 0x42, - 0x60, 0x00, 0x66, 0x30, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, - 0x66, 0x00, 0x06, 0x54, 0x45, 0x60, 0x00, 0x66, 0x0c, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x10, 0x36, 0x60, 0x03, 0x66, 0x66, 0x30, 0x06, 0x63, - 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x02, 0x60, 0x08, 0x00, - 0x02, 0x06, 0x02, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x03, - 0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x02, 0x30, 0x0c, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x0e, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x0c, 0x36, 0x60, 0x00, 0x00, 0x06, 0x63, 0x0e, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, - 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x00, 0x00, - 0x66, 0x30, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x66, 0x00, - 0x00, 0x66, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x36, 0x60, - 0x06, 0x63, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x06, 0x60, - 0x06, 0x60, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x03, 0x66, - 0x66, 0x30, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x66, 0x02, 0x66, - 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x36, 0x02, 0x63, 0x12, 0x00, - 0x00, 0x01 -}; + 0x42, 0x4d, 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, + 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x02, + 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0x00, 0x00, 0x5a, + 0xff, 0x00, 0x78, 0x78, 0x78, 0x00, 0x00, 0x7c, 0xff, 0x00, 0x00, 0xa9, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x03, 0x24, 0x66, 0x02, 0x30, + 0x00, 0x00, 0x02, 0x36, 0x24, 0x66, 0x02, 0x63, 0x00, 0x00, 0x02, 0x66, + 0x02, 0x63, 0x20, 0x00, 0x02, 0x36, 0x02, 0x66, 0x00, 0x00, 0x02, 0x36, + 0x02, 0x60, 0x20, 0x00, 0x02, 0x06, 0x02, 0x63, 0x00, 0x00, 0x02, 0x06, + 0x02, 0x60, 0x0c, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30, 0x0c, 0x00, + 0x02, 0x06, 0x02, 0x60, 0x00, 0x00, 0x02, 0x03, 0x02, 0x66, 0x0c, 0x00, + 0x00, 0x08, 0x06, 0x11, 0x11, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x30, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x66, 0x0c, 0x00, 0x00, 0x08, 0x06, 0x21, + 0x21, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x36, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x42, 0x42, 0x60, + 0x0a, 0x00, 0x00, 0x06, 0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x06, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x54, 0x45, 0x60, + 0x0a, 0x00, 0x00, 0x06, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x03, 0x66, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30, + 0x0a, 0x00, 0x00, 0x06, 0x66, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x66, 0x00, 0x1c, 0x00, 0x00, 0x06, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x36, 0x60, 0x18, 0x00, 0x00, 0x08, + 0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x60, + 0x0a, 0x00, 0x02, 0x36, 0x02, 0x63, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x66, 0x08, 0x00, + 0x00, 0x08, 0x03, 0x61, 0x16, 0x30, 0x08, 0x00, 0x00, 0x08, 0x66, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x08, + 0x06, 0x11, 0x11, 0x60, 0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x02, 0x36, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, + 0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x63, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, + 0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x02, 0x03, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, + 0x11, 0x60, 0x06, 0x00, 0x02, 0x66, 0x02, 0x30, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, 0x11, 0x60, + 0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, + 0x36, 0x60, 0x00, 0x00, 0x06, 0x11, 0x11, 0x60, 0x00, 0x00, 0x06, 0x63, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x06, 0x60, 0x00, 0x00, + 0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x06, 0x60, 0x08, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x18, 0x03, 0x66, 0x00, 0x00, 0x06, 0x12, 0x12, 0x60, + 0x00, 0x00, 0x66, 0x30, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, + 0x66, 0x00, 0x00, 0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x66, 0x0a, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x36, 0x60, 0x00, 0x06, 0x22, 0x22, + 0x60, 0x00, 0x06, 0x63, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, + 0x06, 0x60, 0x00, 0x06, 0x24, 0x24, 0x60, 0x00, 0x06, 0x60, 0x0a, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x03, 0x66, 0x00, 0x06, 0x42, 0x42, + 0x60, 0x00, 0x66, 0x30, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10, + 0x66, 0x00, 0x06, 0x54, 0x45, 0x60, 0x00, 0x66, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x10, 0x36, 0x60, 0x03, 0x66, 0x66, 0x30, 0x06, 0x63, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x02, 0x60, 0x08, 0x00, + 0x02, 0x06, 0x02, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x03, + 0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x02, 0x30, 0x0c, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x0e, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0c, 0x36, 0x60, 0x00, 0x00, 0x06, 0x63, 0x0e, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, + 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x00, 0x00, + 0x66, 0x30, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x66, 0x00, + 0x00, 0x66, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x36, 0x60, + 0x06, 0x63, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x06, 0x60, + 0x06, 0x60, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x03, 0x66, + 0x66, 0x30, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x66, 0x02, 0x66, + 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x36, 0x02, 0x63, 0x12, 0x00, + 0x00, 0x01}; unsigned int modal_warning_bmp_len = 830; diff --git a/firmware/application/bitmaps/bmp_splash.hpp b/firmware/application/bitmaps/bmp_splash.hpp index cfb8ad6e7..d2191767f 100644 --- a/firmware/application/bitmaps/bmp_splash.hpp +++ b/firmware/application/bitmaps/bmp_splash.hpp @@ -1,322 +1,321 @@ const unsigned char splash_bmp[] = { - 0x42, 0x4d, 0xf4, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x32, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x82, 0x0e, - 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x0f, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf6, 0x00, 0x00, 0x14, - 0xc9, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x16, 0x18, 0x29, 0x00, 0xfd, 0xfd, - 0xfd, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0xe7, 0xe7, 0xe7, 0x00, 0xda, 0xda, - 0xda, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xba, 0xba, - 0xba, 0x00, 0xad, 0xad, 0xad, 0x00, 0x96, 0x96, 0x96, 0x00, 0x5c, 0x5c, - 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, - 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0x00, 0x18, - 0xee, 0x44, 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, - 0x08, 0xee, 0x00, 0x14, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0xea, 0x44, 0x44, - 0x44, 0xde, 0x12, 0xee, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0xee, - 0x00, 0x08, 0xc4, 0x44, 0x45, 0xce, 0x10, 0xee, 0x00, 0x08, 0x34, 0x44, - 0x44, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x44, 0x44, 0x6d, 0xee, 0xee, - 0xee, 0xe3, 0xd5, 0x00, 0x1a, 0x44, 0x00, 0x1c, 0x45, 0xce, 0xee, 0x44, - 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, 0x08, 0xee, - 0x00, 0x0a, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x34, - 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98, - 0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xed, 0x79, 0x99, - 0x99, 0x73, 0x10, 0xee, 0x00, 0x0a, 0xed, 0x47, 0x99, 0x95, 0xae, 0x00, - 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, 0x00, 0x08, - 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, - 0xee, 0xee, 0xee, 0xb4, 0x56, 0x89, 0x18, 0x99, 0x00, 0x2e, 0x97, 0xae, - 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98, - 0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x34, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, - 0x99, 0x99, 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, - 0xb9, 0x99, 0x99, 0x8d, 0x10, 0xee, 0x00, 0x0a, 0xe7, 0x69, 0x99, 0x74, - 0xde, 0x00, 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, - 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, - 0x99, 0x7c, 0xee, 0xee, 0xea, 0x47, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae, - 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, 0x99, 0x99, - 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, - 0x99, 0x99, 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, - 0xd7, 0x99, 0x99, 0x98, 0x3e, 0x00, 0x0e, 0xee, 0x00, 0x08, 0xd4, 0x89, - 0x99, 0x5a, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, - 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, - 0x99, 0x7c, 0xee, 0xee, 0xb4, 0x79, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae, - 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, 0x99, 0x99, - 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x12, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00, - 0x08, 0x99, 0x00, 0x1c, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, - 0xee, 0xeb, 0x99, 0x99, 0x99, 0xce, 0x0c, 0xee, 0x00, 0x0a, 0xe3, 0x56, - 0x99, 0x97, 0x4d, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, - 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e, - 0x34, 0x99, 0x99, 0x7c, 0xee, 0xed, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16, - 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00, - 0x08, 0x99, 0x00, 0x10, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, - 0x00, 0x00, 0x00, 0x10, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, - 0x0a, 0x99, 0x00, 0x1c, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, - 0xee, 0xed, 0x79, 0x99, 0x99, 0x73, 0x0c, 0xee, 0x00, 0x0a, 0xec, 0x48, - 0x99, 0x95, 0xae, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, - 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e, - 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe7, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14, - 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, 0x0a, 0x99, - 0x00, 0x10, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, - 0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, 0x97, - 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, - 0xee, 0xb9, 0x99, 0x99, 0x9d, 0x00, 0x0c, 0xee, 0x00, 0x0a, 0x34, 0x69, - 0x99, 0x74, 0xde, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xae, - 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12, - 0x34, 0x99, 0x99, 0x7c, 0xee, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44, - 0x00, 0x2c, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, - 0x97, 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, - 0x00, 0x00, 0x00, 0x38, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, - 0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, - 0x3e, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x99, 0x74, 0x0c, 0x44, 0x00, 0x06, - 0x99, 0x99, 0x4b, 0x00, 0x0c, 0xee, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xae, - 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12, - 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, - 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, 0x99, 0x95, 0xbc, - 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, - 0x00, 0x30, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0x34, 0x79, 0x99, 0x84, - 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, - 0xee, 0xeb, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x00, 0x0a, - 0xed, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, - 0x99, 0x9d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, - 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, - 0xee, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, - 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, - 0xde, 0xee, 0xc4, 0x99, 0x99, 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, - 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xed, 0x79, 0x00, 0x14, 0x99, - 0x02, 0x94, 0x02, 0xce, 0x0a, 0xee, 0x00, 0x0c, 0x35, 0x59, 0x99, 0x99, - 0x98, 0x3e, 0x0e, 0xee, 0x00, 0x0a, 0x34, 0x99, 0x99, 0x99, 0x74, 0x00, - 0x16, 0x44, 0x00, 0x10, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x9d, - 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xc4, 0x99, 0x99, - 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, - 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, - 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, - 0x3e, 0xee, 0xee, 0xee, 0xc9, 0x00, 0x14, 0x99, 0x02, 0x74, 0x02, 0x3e, - 0x08, 0xee, 0x00, 0x0e, 0xe3, 0x84, 0x89, 0x99, 0x99, 0x99, 0xa3, 0x00, - 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x0e, 0x7c, 0xee, 0xd6, 0x99, - 0x99, 0x99, 0x64, 0x00, 0x18, 0x44, 0x00, 0x2e, 0x45, 0xce, 0xee, 0x49, - 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, - 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32, - 0xee, 0x49, 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, - 0x99, 0x99, 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee, - 0x37, 0x00, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0xee, 0x02, 0xeb, 0x02, 0x48, - 0x08, 0x99, 0x02, 0x9b, 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, - 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, - 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, 0x99, 0x99, - 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32, - 0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec, - 0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee, - 0xec, 0x00, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x02, 0xd4, - 0x02, 0x79, 0x0a, 0x99, 0x02, 0xde, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, - 0x00, 0x06, 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, - 0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec, - 0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x48, 0xee, 0x49, 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, - 0xee, 0xe3, 0x79, 0x99, 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, - 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99, 0x99, - 0x94, 0xce, 0x08, 0xee, 0x00, 0x12, 0xed, 0x46, 0x99, 0x98, 0x59, 0x99, - 0x99, 0x99, 0x8d, 0x00, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, - 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, - 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, 0xee, 0xe3, 0x79, 0x99, - 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, - 0xee, 0x49, 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, - 0xa9, 0x99, 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, - 0x00, 0x16, 0xc9, 0x99, 0x99, 0x9c, 0xee, 0xee, 0x3b, 0x99, 0x99, 0x74, - 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0x35, 0x59, 0x99, 0x95, 0x7d, 0x99, - 0x99, 0x99, 0x9a, 0x3e, 0x0a, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, - 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, - 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, 0xa9, 0x99, - 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, - 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, 0x4d, 0xee, 0xee, 0xee, - 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, - 0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xc4, 0x89, 0x99, 0x4c, - 0x08, 0xee, 0x00, 0x16, 0xe3, 0x84, 0x89, 0x99, 0x64, 0x3e, 0xd9, 0x99, - 0x99, 0x99, 0xa3, 0x00, 0x0a, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x64, - 0x16, 0x44, 0x00, 0x0c, 0x99, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x20, 0x99, - 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, - 0x4d, 0xee, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, - 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, - 0x99, 0x96, 0x5e, 0xee, 0xee, 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, - 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0xec, 0x99, 0x99, 0x99, - 0xbe, 0xe3, 0x46, 0x99, 0x96, 0x43, 0x08, 0xee, 0x00, 0x16, 0xeb, 0x48, - 0x99, 0x97, 0x4d, 0xee, 0xed, 0x99, 0x99, 0x99, 0x9c, 0x00, 0x0a, 0xee, - 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x39, 0x99, - 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x54, 0x00, 0x1c, 0x44, 0x00, 0x2c, - 0xae, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x5e, 0xee, 0xee, - 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, - 0x00, 0x2a, 0xee, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xde, 0xee, - 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x3e, 0x00, - 0x08, 0xee, 0x00, 0x14, 0xe3, 0x79, 0x99, 0x99, 0x8d, 0xea, 0x59, 0x99, - 0x84, 0xde, 0x08, 0xee, 0x00, 0x18, 0xd4, 0x79, 0x99, 0x84, 0xbe, 0xee, - 0xee, 0xb9, 0x99, 0x99, 0x99, 0xde, 0x08, 0xee, 0x00, 0x08, 0x34, 0x99, - 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, - 0x99, 0x99, 0x5d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x98, 0xd5, - 0x79, 0x99, 0x94, 0xde, 0xee, 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, - 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x99, - 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, - 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x0a, 0xee, 0x00, 0x32, 0xc9, 0x99, - 0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65, 0x3e, 0xee, 0xee, 0xee, 0xed, 0x46, - 0x99, 0x98, 0x48, 0x3e, 0xee, 0xee, 0x3a, 0x99, 0x99, 0x99, 0x83, 0x00, - 0x08, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, - 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x93, 0x00, 0x20, 0xee, - 0x00, 0x28, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, - 0xee, 0xe3, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x00, - 0x00, 0x10, 0xee, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, - 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, 0x79, 0x99, 0x84, 0x3e, 0x0a, 0xee, - 0x00, 0x10, 0x37, 0x99, 0x99, 0x99, 0x86, 0x99, 0x98, 0x4d, 0x08, 0xee, - 0x00, 0x2a, 0x35, 0x59, 0x99, 0x95, 0x53, 0xee, 0xee, 0xee, 0xe3, 0x89, - 0x99, 0x99, 0x9a, 0x3e, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, - 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, 0xee, 0x36, 0x99, 0x99, - 0x99, 0x54, 0x18, 0x44, 0x00, 0x14, 0x45, 0xce, 0xee, 0x49, 0x99, 0x99, - 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, - 0x79, 0x99, 0x84, 0x3e, 0x00, 0x00, 0x02, 0xee, 0x02, 0x59, 0x08, 0x99, - 0x02, 0x97, 0x02, 0x4d, 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, - 0x0c, 0xee, 0x02, 0xec, 0x0a, 0x99, 0x00, 0x16, 0x96, 0x53, 0xee, 0xee, - 0xee, 0xe3, 0x84, 0x89, 0x99, 0x64, 0xde, 0x00, 0x08, 0xee, 0x00, 0x18, - 0xd9, 0x99, 0x99, 0x99, 0xbe, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, - 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe6, 0x20, 0x99, - 0x00, 0x08, 0x97, 0xae, 0xee, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, - 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x02, 0xee, 0x00, 0x00, - 0x02, 0xee, 0x02, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, - 0x02, 0xec, 0x0a, 0x99, 0x02, 0x5b, 0x0c, 0xee, 0x02, 0xe3, 0x02, 0x89, - 0x08, 0x99, 0x00, 0x14, 0x84, 0xde, 0xee, 0xee, 0xee, 0xeb, 0x48, 0x99, - 0x97, 0x4c, 0x0a, 0xee, 0x00, 0x18, 0xed, 0x99, 0x99, 0x99, 0x9d, 0xee, - 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, - 0x99, 0x7c, 0xee, 0xed, 0x89, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xae, - 0xee, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, 0x02, 0xec, - 0x0a, 0x99, 0x02, 0x5b, 0x02, 0xee, 0x00, 0x00, 0x02, 0xee, 0x02, 0xd7, - 0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x0e, 0xe3, 0x79, - 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd8, 0x99, - 0x99, 0x99, 0x98, 0x48, 0x08, 0xee, 0x00, 0x0a, 0xd4, 0x79, 0x99, 0x84, - 0xbe, 0x00, 0x0c, 0xee, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98, 0xde, 0xee, - 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, - 0x99, 0x7c, 0xee, 0xe3, 0x20, 0x99, 0x00, 0x08, 0x97, 0xae, 0xee, 0xd7, - 0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x10, 0xe3, 0x79, - 0x99, 0x99, 0x99, 0x96, 0x4d, 0xee, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xec, - 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd9, 0x99, - 0x99, 0x99, 0x74, 0xce, 0x0e, 0xee, 0x00, 0x1e, 0xed, 0x99, 0x99, 0x99, - 0x85, 0x53, 0xee, 0xee, 0xee, 0xed, 0x46, 0x99, 0x99, 0x57, 0x3e, 0x00, - 0x0c, 0xee, 0x00, 0x16, 0x39, 0x99, 0x99, 0x99, 0xa3, 0xee, 0xee, 0x34, - 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c, - 0xee, 0xee, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xec, - 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xd9, 0x99, - 0x99, 0x99, 0x74, 0xce, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xe3, - 0xc8, 0x99, 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0a, 0xed, 0x79, - 0x99, 0x76, 0x4b, 0x00, 0x12, 0xee, 0x00, 0x1a, 0xc7, 0x99, 0x96, 0x45, - 0xde, 0xee, 0xee, 0xee, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00, 0x0e, 0xee, - 0x00, 0x16, 0xe3, 0x89, 0x99, 0x99, 0x9a, 0x3e, 0xee, 0x34, 0x99, 0x99, - 0x6d, 0x00, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xee, - 0xe3, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xe3, 0xc8, 0x99, - 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xed, 0x79, 0x99, 0x76, - 0x4b, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xee, 0xee, 0xed, 0x84, - 0x44, 0xc3, 0x12, 0xee, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xde, 0x12, 0xee, - 0x00, 0x1a, 0xed, 0xa4, 0x44, 0xd3, 0xee, 0xee, 0xee, 0xe3, 0xc8, 0x44, - 0x44, 0x44, 0xde, 0x00, 0x10, 0xee, 0x00, 0x14, 0xd4, 0x44, 0x44, 0x44, - 0xae, 0xee, 0x38, 0x44, 0x44, 0x4d, 0x16, 0xee, 0x00, 0x12, 0x38, 0x44, - 0x44, 0x4c, 0xee, 0xee, 0xee, 0xed, 0xc4, 0x00, 0x1c, 0x44, 0x00, 0x0e, - 0xae, 0xee, 0xee, 0xed, 0x84, 0x44, 0xc3, 0x00, 0x12, 0xee, 0x00, 0x0c, - 0xdc, 0x44, 0x4a, 0xde, 0xee, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, - 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, - 0xe6, 0xee, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x2e, 0xe0, 0x00, - 0x13, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, - 0x00, 0x26, 0x1e, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0x00, 0x00, - 0x13, 0xe3, 0x00, 0x00, 0x1e, 0xe2, 0x00, 0x00, 0x2e, 0x00, 0x24, 0xee, - 0x00, 0x06, 0xe2, 0x00, 0x02, 0x00, 0x08, 0xee, 0x00, 0x50, 0xe2, 0x00, - 0x00, 0x00, 0x01, 0xee, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xee, - 0xe2, 0x00, 0x02, 0xee, 0xee, 0xe0, 0x00, 0x13, 0xe2, 0x00, 0x02, 0xee, - 0x00, 0x01, 0x3e, 0xee, 0xee, 0xee, 0x30, 0x00, 0x1e, 0xe1, 0x00, 0x02, - 0xee, 0x30, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x30, 0x00, 0x1e, 0xe1, 0x00, - 0x02, 0xee, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, - 0x03, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x10, 0x10, 0x00, 0x00, - 0x00, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x03, - 0xe3, 0x01, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, - 0xe2, 0x01, 0x11, 0x00, 0x08, 0xee, 0x00, 0x68, 0x10, 0x10, 0x00, 0x11, - 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, - 0x11, 0xee, 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, - 0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, - 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, - 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, - 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01, - 0x10, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, 0xe2, 0x01, - 0x11, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x31, 0x11, 0x0e, - 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, - 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, 0x3e, 0xee, - 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x00, - 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, - 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x2e, 0xe0, 0x11, 0x03, 0xe1, - 0x11, 0x12, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, - 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, - 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x00, 0x2e, 0x20, 0xee, 0x00, 0x06, - 0xe2, 0x01, 0x12, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x30, - 0x11, 0x0e, 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0xee, 0xe2, 0x01, - 0x11, 0xee, 0xee, 0xe0, 0x11, 0x13, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x11, - 0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, - 0x10, 0x00, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, - 0xee, 0x00, 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x11, 0x00, 0x11, - 0x03, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, - 0xee, 0xee, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x1e, 0xee, 0x01, 0x08, 0x11, - 0x00, 0x10, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x10, 0x2e, 0x20, 0xee, - 0x00, 0x60, 0xe2, 0x01, 0x11, 0x10, 0x00, 0x03, 0xee, 0x11, 0x10, 0x2e, - 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x02, 0xee, 0xee, 0xe2, - 0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, - 0x11, 0x22, 0x22, 0x23, 0xee, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, - 0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13, - 0xee, 0x00, 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x00, 0x11, 0x11, - 0x03, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, - 0xee, 0xee, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x1e, 0xee, 0x01, 0x11, 0x00, - 0x01, 0x11, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x00, 0x00, 0x2e, 0x20, 0xee, - 0x00, 0x60, 0xe2, 0x01, 0x10, 0x01, 0x11, 0x12, 0x3e, 0x11, 0x10, 0x2e, - 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x10, 0x10, 0x11, 0x1e, 0xee, 0xee, 0xe2, - 0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x10, 0x10, 0x11, 0x11, 0xee, 0x01, - 0x11, 0x01, 0x00, 0x01, 0x2e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0xe3, - 0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x2e, - 0xee, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe1, 0x11, - 0x03, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, - 0xee, 0xee, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x01, 0x11, 0x03, - 0xe3, 0x11, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, - 0xe2, 0x01, 0x11, 0xee, 0x11, 0x10, 0x3e, 0x11, 0x10, 0x2e, 0x30, 0x11, - 0x0e, 0xe1, 0x11, 0x01, 0x22, 0x11, 0x12, 0xee, 0xee, 0xe2, 0x01, 0x11, - 0xee, 0xee, 0xe0, 0x11, 0x02, 0x22, 0x11, 0x11, 0xee, 0x01, 0x10, 0x22, - 0x21, 0x11, 0x1e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x10, - 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0xee, 0x00, - 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, - 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, - 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01, - 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, 0xe2, 0x01, - 0x11, 0xee, 0x01, 0x10, 0x3e, 0x11, 0x11, 0x2e, 0x30, 0x11, 0x0e, 0xe1, - 0x11, 0x12, 0xe3, 0x11, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, 0xee, - 0xe0, 0x11, 0x13, 0xe2, 0x11, 0x11, 0xee, 0x01, 0x11, 0x3e, 0x21, 0x11, - 0x1e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x08, 0xee, - 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, 0x00, 0x00, - 0x00, 0x54, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, 0x11, 0x12, - 0xe3, 0x01, 0x00, 0xee, 0x11, 0x11, 0x2e, 0xee, 0xee, 0xee, 0xe1, 0x11, - 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, 0x10, 0x1e, - 0xe2, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x2e, 0x1c, 0xee, 0x00, 0x60, - 0xe2, 0x01, 0x12, 0xee, 0x01, 0x00, 0x3e, 0x11, 0x11, 0x22, 0x20, 0x11, - 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x12, - 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, 0x11, 0x22, - 0x10, 0x11, 0x1e, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x11, - 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, - 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe2, - 0x11, 0x11, 0x00, 0x01, 0x02, 0xee, 0x21, 0x11, 0x10, 0x00, 0x00, 0x1e, - 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x08, 0x11, 0x00, 0x08, - 0x12, 0xee, 0xe3, 0x21, 0x08, 0x11, 0x02, 0x10, 0x02, 0x2e, 0x1c, 0xee, - 0x00, 0x76, 0xe2, 0x01, 0x11, 0x10, 0x01, 0x02, 0x3e, 0x11, 0x11, 0x11, - 0x01, 0x10, 0x0e, 0xe1, 0x11, 0x11, 0x10, 0x11, 0x00, 0xee, 0x10, 0x00, - 0x01, 0x11, 0x10, 0x1e, 0xe0, 0x11, 0x11, 0x10, 0x11, 0x01, 0xee, 0x01, - 0x11, 0x11, 0x01, 0x10, 0x1e, 0x30, 0x11, 0x11, 0x00, 0x10, 0x02, 0xe3, - 0x01, 0x11, 0x10, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, - 0xee, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x1e, 0xe1, 0x00, - 0x03, 0xee, 0x20, 0x00, 0x00, 0x00, 0x1e, 0xee, 0xe2, 0x00, 0x08, 0x00, - 0x00, 0x12, 0x0e, 0xe2, 0x00, 0x02, 0xe3, 0x10, 0x00, 0xee, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x08, 0x02, 0xee, 0xee, 0x30, 0x0a, 0x00, 0x02, 0x2e, - 0x1c, 0xee, 0x02, 0xe2, 0x08, 0x00, 0x00, 0x12, 0x03, 0xee, 0xe2, 0x00, - 0x00, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, 0x00, 0x06, 0x1e, 0xee, - 0x20, 0x00, 0x08, 0x00, 0x00, 0x1e, 0x0e, 0xee, 0x10, 0x00, 0x00, 0x00, - 0x2e, 0xee, 0x10, 0x00, 0x00, 0x00, 0x02, 0xee, 0xe3, 0x00, 0x08, 0x00, - 0x00, 0x06, 0x2e, 0xee, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x31, - 0x00, 0x0e, 0xe2, 0x00, 0x02, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, - 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x01 -}; + 0x42, 0x4d, 0xf4, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x82, 0x0e, + 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf6, 0x00, 0x00, 0x14, + 0xc9, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x16, 0x18, 0x29, 0x00, 0xfd, 0xfd, + 0xfd, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0xe7, 0xe7, 0xe7, 0x00, 0xda, 0xda, + 0xda, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xba, 0xba, + 0xba, 0x00, 0xad, 0xad, 0xad, 0x00, 0x96, 0x96, 0x96, 0x00, 0x5c, 0x5c, + 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, + 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0x00, 0x18, + 0xee, 0x44, 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, + 0x08, 0xee, 0x00, 0x14, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0xea, 0x44, 0x44, + 0x44, 0xde, 0x12, 0xee, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0xee, + 0x00, 0x08, 0xc4, 0x44, 0x45, 0xce, 0x10, 0xee, 0x00, 0x08, 0x34, 0x44, + 0x44, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x44, 0x44, 0x6d, 0xee, 0xee, + 0xee, 0xe3, 0xd5, 0x00, 0x1a, 0x44, 0x00, 0x1c, 0x45, 0xce, 0xee, 0x44, + 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, 0x08, 0xee, + 0x00, 0x0a, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x34, + 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98, + 0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xed, 0x79, 0x99, + 0x99, 0x73, 0x10, 0xee, 0x00, 0x0a, 0xed, 0x47, 0x99, 0x95, 0xae, 0x00, + 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, 0x00, 0x08, + 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, + 0xee, 0xee, 0xee, 0xb4, 0x56, 0x89, 0x18, 0x99, 0x00, 0x2e, 0x97, 0xae, + 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98, + 0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x34, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, + 0x99, 0x99, 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, + 0xb9, 0x99, 0x99, 0x8d, 0x10, 0xee, 0x00, 0x0a, 0xe7, 0x69, 0x99, 0x74, + 0xde, 0x00, 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, + 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, + 0x99, 0x7c, 0xee, 0xee, 0xea, 0x47, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae, + 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, 0x99, 0x99, + 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, + 0x99, 0x99, 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, + 0xd7, 0x99, 0x99, 0x98, 0x3e, 0x00, 0x0e, 0xee, 0x00, 0x08, 0xd4, 0x89, + 0x99, 0x5a, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, + 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, + 0x99, 0x7c, 0xee, 0xee, 0xb4, 0x79, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae, + 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, 0x99, 0x99, + 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x12, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00, + 0x08, 0x99, 0x00, 0x1c, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, + 0xee, 0xeb, 0x99, 0x99, 0x99, 0xce, 0x0c, 0xee, 0x00, 0x0a, 0xe3, 0x56, + 0x99, 0x97, 0x4d, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, + 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e, + 0x34, 0x99, 0x99, 0x7c, 0xee, 0xed, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16, + 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00, + 0x08, 0x99, 0x00, 0x10, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, + 0x00, 0x00, 0x00, 0x10, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, + 0x0a, 0x99, 0x00, 0x1c, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, + 0xee, 0xed, 0x79, 0x99, 0x99, 0x73, 0x0c, 0xee, 0x00, 0x0a, 0xec, 0x48, + 0x99, 0x95, 0xae, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, + 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e, + 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe7, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14, + 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, 0x0a, 0x99, + 0x00, 0x10, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, + 0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, 0x97, + 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, + 0xee, 0xb9, 0x99, 0x99, 0x9d, 0x00, 0x0c, 0xee, 0x00, 0x0a, 0x34, 0x69, + 0x99, 0x74, 0xde, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xae, + 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12, + 0x34, 0x99, 0x99, 0x7c, 0xee, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44, + 0x00, 0x2c, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, + 0x97, 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, + 0x00, 0x00, 0x00, 0x38, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, + 0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, + 0x3e, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x99, 0x74, 0x0c, 0x44, 0x00, 0x06, + 0x99, 0x99, 0x4b, 0x00, 0x0c, 0xee, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xae, + 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12, + 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, + 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, 0x99, 0x95, 0xbc, + 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, + 0x00, 0x30, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0x34, 0x79, 0x99, 0x84, + 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, + 0xee, 0xeb, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x00, 0x0a, + 0xed, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0xee, 0x00, 0x08, 0x34, 0x99, + 0x99, 0x9d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, + 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, + 0xee, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, + 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, + 0xde, 0xee, 0xc4, 0x99, 0x99, 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, + 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xed, 0x79, 0x00, 0x14, 0x99, + 0x02, 0x94, 0x02, 0xce, 0x0a, 0xee, 0x00, 0x0c, 0x35, 0x59, 0x99, 0x99, + 0x98, 0x3e, 0x0e, 0xee, 0x00, 0x0a, 0x34, 0x99, 0x99, 0x99, 0x74, 0x00, + 0x16, 0x44, 0x00, 0x10, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x9d, + 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xc4, 0x99, 0x99, + 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, + 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, + 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, + 0x3e, 0xee, 0xee, 0xee, 0xc9, 0x00, 0x14, 0x99, 0x02, 0x74, 0x02, 0x3e, + 0x08, 0xee, 0x00, 0x0e, 0xe3, 0x84, 0x89, 0x99, 0x99, 0x99, 0xa3, 0x00, + 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x0e, 0x7c, 0xee, 0xd6, 0x99, + 0x99, 0x99, 0x64, 0x00, 0x18, 0x44, 0x00, 0x2e, 0x45, 0xce, 0xee, 0x49, + 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, + 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32, + 0xee, 0x49, 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, + 0x99, 0x99, 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee, + 0x37, 0x00, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0xee, 0x02, 0xeb, 0x02, 0x48, + 0x08, 0x99, 0x02, 0x9b, 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, + 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, + 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, 0x99, 0x99, + 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32, + 0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec, + 0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee, + 0xec, 0x00, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x02, 0xd4, + 0x02, 0x79, 0x0a, 0x99, 0x02, 0xde, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, + 0x00, 0x06, 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, + 0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec, + 0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x48, 0xee, 0x49, 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, + 0xee, 0xe3, 0x79, 0x99, 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, + 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99, 0x99, + 0x94, 0xce, 0x08, 0xee, 0x00, 0x12, 0xed, 0x46, 0x99, 0x98, 0x59, 0x99, + 0x99, 0x99, 0x8d, 0x00, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, + 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, + 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, 0xee, 0xe3, 0x79, 0x99, + 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, + 0xee, 0x49, 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, + 0xa9, 0x99, 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, + 0x00, 0x16, 0xc9, 0x99, 0x99, 0x9c, 0xee, 0xee, 0x3b, 0x99, 0x99, 0x74, + 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0x35, 0x59, 0x99, 0x95, 0x7d, 0x99, + 0x99, 0x99, 0x9a, 0x3e, 0x0a, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06, + 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, + 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, 0xa9, 0x99, + 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, + 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, 0x4d, 0xee, 0xee, 0xee, + 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, + 0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xc4, 0x89, 0x99, 0x4c, + 0x08, 0xee, 0x00, 0x16, 0xe3, 0x84, 0x89, 0x99, 0x64, 0x3e, 0xd9, 0x99, + 0x99, 0x99, 0xa3, 0x00, 0x0a, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x64, + 0x16, 0x44, 0x00, 0x0c, 0x99, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x20, 0x99, + 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, + 0x4d, 0xee, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, + 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, + 0x99, 0x96, 0x5e, 0xee, 0xee, 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, + 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0xec, 0x99, 0x99, 0x99, + 0xbe, 0xe3, 0x46, 0x99, 0x96, 0x43, 0x08, 0xee, 0x00, 0x16, 0xeb, 0x48, + 0x99, 0x97, 0x4d, 0xee, 0xed, 0x99, 0x99, 0x99, 0x9c, 0x00, 0x0a, 0xee, + 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x39, 0x99, + 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x54, 0x00, 0x1c, 0x44, 0x00, 0x2c, + 0xae, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x5e, 0xee, 0xee, + 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, + 0x00, 0x2a, 0xee, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xde, 0xee, + 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x3e, 0x00, + 0x08, 0xee, 0x00, 0x14, 0xe3, 0x79, 0x99, 0x99, 0x8d, 0xea, 0x59, 0x99, + 0x84, 0xde, 0x08, 0xee, 0x00, 0x18, 0xd4, 0x79, 0x99, 0x84, 0xbe, 0xee, + 0xee, 0xb9, 0x99, 0x99, 0x99, 0xde, 0x08, 0xee, 0x00, 0x08, 0x34, 0x99, + 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, + 0x99, 0x99, 0x5d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x98, 0xd5, + 0x79, 0x99, 0x94, 0xde, 0xee, 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, + 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x99, + 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, + 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x0a, 0xee, 0x00, 0x32, 0xc9, 0x99, + 0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65, 0x3e, 0xee, 0xee, 0xee, 0xed, 0x46, + 0x99, 0x98, 0x48, 0x3e, 0xee, 0xee, 0x3a, 0x99, 0x99, 0x99, 0x83, 0x00, + 0x08, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, + 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x93, 0x00, 0x20, 0xee, + 0x00, 0x28, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, + 0xee, 0xe3, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x00, + 0x00, 0x10, 0xee, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, + 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, 0x79, 0x99, 0x84, 0x3e, 0x0a, 0xee, + 0x00, 0x10, 0x37, 0x99, 0x99, 0x99, 0x86, 0x99, 0x98, 0x4d, 0x08, 0xee, + 0x00, 0x2a, 0x35, 0x59, 0x99, 0x95, 0x53, 0xee, 0xee, 0xee, 0xe3, 0x89, + 0x99, 0x99, 0x9a, 0x3e, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, + 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, 0xee, 0x36, 0x99, 0x99, + 0x99, 0x54, 0x18, 0x44, 0x00, 0x14, 0x45, 0xce, 0xee, 0x49, 0x99, 0x99, + 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, + 0x79, 0x99, 0x84, 0x3e, 0x00, 0x00, 0x02, 0xee, 0x02, 0x59, 0x08, 0x99, + 0x02, 0x97, 0x02, 0x4d, 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, + 0x0c, 0xee, 0x02, 0xec, 0x0a, 0x99, 0x00, 0x16, 0x96, 0x53, 0xee, 0xee, + 0xee, 0xe3, 0x84, 0x89, 0x99, 0x64, 0xde, 0x00, 0x08, 0xee, 0x00, 0x18, + 0xd9, 0x99, 0x99, 0x99, 0xbe, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, + 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe6, 0x20, 0x99, + 0x00, 0x08, 0x97, 0xae, 0xee, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, + 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x02, 0xee, 0x00, 0x00, + 0x02, 0xee, 0x02, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, + 0x02, 0xec, 0x0a, 0x99, 0x02, 0x5b, 0x0c, 0xee, 0x02, 0xe3, 0x02, 0x89, + 0x08, 0x99, 0x00, 0x14, 0x84, 0xde, 0xee, 0xee, 0xee, 0xeb, 0x48, 0x99, + 0x97, 0x4c, 0x0a, 0xee, 0x00, 0x18, 0xed, 0x99, 0x99, 0x99, 0x9d, 0xee, + 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, + 0x99, 0x7c, 0xee, 0xed, 0x89, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xae, + 0xee, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, 0x02, 0xec, + 0x0a, 0x99, 0x02, 0x5b, 0x02, 0xee, 0x00, 0x00, 0x02, 0xee, 0x02, 0xd7, + 0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x0e, 0xe3, 0x79, + 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd8, 0x99, + 0x99, 0x99, 0x98, 0x48, 0x08, 0xee, 0x00, 0x0a, 0xd4, 0x79, 0x99, 0x84, + 0xbe, 0x00, 0x0c, 0xee, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98, 0xde, 0xee, + 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, + 0x99, 0x7c, 0xee, 0xe3, 0x20, 0x99, 0x00, 0x08, 0x97, 0xae, 0xee, 0xd7, + 0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x10, 0xe3, 0x79, + 0x99, 0x99, 0x99, 0x96, 0x4d, 0xee, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xec, + 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd9, 0x99, + 0x99, 0x99, 0x74, 0xce, 0x0e, 0xee, 0x00, 0x1e, 0xed, 0x99, 0x99, 0x99, + 0x85, 0x53, 0xee, 0xee, 0xee, 0xed, 0x46, 0x99, 0x99, 0x57, 0x3e, 0x00, + 0x0c, 0xee, 0x00, 0x16, 0x39, 0x99, 0x99, 0x99, 0xa3, 0xee, 0xee, 0x34, + 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c, + 0xee, 0xee, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xec, + 0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xd9, 0x99, + 0x99, 0x99, 0x74, 0xce, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xe3, + 0xc8, 0x99, 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0a, 0xed, 0x79, + 0x99, 0x76, 0x4b, 0x00, 0x12, 0xee, 0x00, 0x1a, 0xc7, 0x99, 0x96, 0x45, + 0xde, 0xee, 0xee, 0xee, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00, 0x0e, 0xee, + 0x00, 0x16, 0xe3, 0x89, 0x99, 0x99, 0x9a, 0x3e, 0xee, 0x34, 0x99, 0x99, + 0x6d, 0x00, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xee, + 0xe3, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xe3, 0xc8, 0x99, + 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xed, 0x79, 0x99, 0x76, + 0x4b, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xee, 0xee, 0xed, 0x84, + 0x44, 0xc3, 0x12, 0xee, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xde, 0x12, 0xee, + 0x00, 0x1a, 0xed, 0xa4, 0x44, 0xd3, 0xee, 0xee, 0xee, 0xe3, 0xc8, 0x44, + 0x44, 0x44, 0xde, 0x00, 0x10, 0xee, 0x00, 0x14, 0xd4, 0x44, 0x44, 0x44, + 0xae, 0xee, 0x38, 0x44, 0x44, 0x4d, 0x16, 0xee, 0x00, 0x12, 0x38, 0x44, + 0x44, 0x4c, 0xee, 0xee, 0xee, 0xed, 0xc4, 0x00, 0x1c, 0x44, 0x00, 0x0e, + 0xae, 0xee, 0xee, 0xed, 0x84, 0x44, 0xc3, 0x00, 0x12, 0xee, 0x00, 0x0c, + 0xdc, 0x44, 0x4a, 0xde, 0xee, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, + 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, + 0xe6, 0xee, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x2e, 0xe0, 0x00, + 0x13, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, + 0x00, 0x26, 0x1e, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0x00, 0x00, + 0x13, 0xe3, 0x00, 0x00, 0x1e, 0xe2, 0x00, 0x00, 0x2e, 0x00, 0x24, 0xee, + 0x00, 0x06, 0xe2, 0x00, 0x02, 0x00, 0x08, 0xee, 0x00, 0x50, 0xe2, 0x00, + 0x00, 0x00, 0x01, 0xee, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xee, + 0xe2, 0x00, 0x02, 0xee, 0xee, 0xe0, 0x00, 0x13, 0xe2, 0x00, 0x02, 0xee, + 0x00, 0x01, 0x3e, 0xee, 0xee, 0xee, 0x30, 0x00, 0x1e, 0xe1, 0x00, 0x02, + 0xee, 0x30, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x30, 0x00, 0x1e, 0xe1, 0x00, + 0x02, 0xee, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, + 0x03, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x03, + 0xe3, 0x01, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, + 0xe2, 0x01, 0x11, 0x00, 0x08, 0xee, 0x00, 0x68, 0x10, 0x10, 0x00, 0x11, + 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, + 0x11, 0xee, 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, + 0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, + 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, + 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, + 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01, + 0x10, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, 0xe2, 0x01, + 0x11, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x31, 0x11, 0x0e, + 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, + 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, 0x3e, 0xee, + 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x00, + 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, + 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x2e, 0xe0, 0x11, 0x03, 0xe1, + 0x11, 0x12, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, + 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, + 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x00, 0x2e, 0x20, 0xee, 0x00, 0x06, + 0xe2, 0x01, 0x12, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x30, + 0x11, 0x0e, 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0xee, 0xe2, 0x01, + 0x11, 0xee, 0xee, 0xe0, 0x11, 0x13, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x11, + 0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, + 0x10, 0x00, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x11, 0x00, 0x11, + 0x03, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, + 0xee, 0xee, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x1e, 0xee, 0x01, 0x08, 0x11, + 0x00, 0x10, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x10, 0x2e, 0x20, 0xee, + 0x00, 0x60, 0xe2, 0x01, 0x11, 0x10, 0x00, 0x03, 0xee, 0x11, 0x10, 0x2e, + 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x02, 0xee, 0xee, 0xe2, + 0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, + 0x11, 0x22, 0x22, 0x23, 0xee, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, + 0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x00, 0x11, 0x11, + 0x03, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, + 0xee, 0xee, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x1e, 0xee, 0x01, 0x11, 0x00, + 0x01, 0x11, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x00, 0x00, 0x2e, 0x20, 0xee, + 0x00, 0x60, 0xe2, 0x01, 0x10, 0x01, 0x11, 0x12, 0x3e, 0x11, 0x10, 0x2e, + 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x10, 0x10, 0x11, 0x1e, 0xee, 0xee, 0xe2, + 0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x10, 0x10, 0x11, 0x11, 0xee, 0x01, + 0x11, 0x01, 0x00, 0x01, 0x2e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0xe3, + 0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x2e, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe1, 0x11, + 0x03, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, + 0xee, 0xee, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x01, 0x11, 0x03, + 0xe3, 0x11, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, + 0xe2, 0x01, 0x11, 0xee, 0x11, 0x10, 0x3e, 0x11, 0x10, 0x2e, 0x30, 0x11, + 0x0e, 0xe1, 0x11, 0x01, 0x22, 0x11, 0x12, 0xee, 0xee, 0xe2, 0x01, 0x11, + 0xee, 0xee, 0xe0, 0x11, 0x02, 0x22, 0x11, 0x11, 0xee, 0x01, 0x10, 0x22, + 0x21, 0x11, 0x1e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x10, + 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0xee, 0x00, + 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, + 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee, + 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01, + 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, 0xe2, 0x01, + 0x11, 0xee, 0x01, 0x10, 0x3e, 0x11, 0x11, 0x2e, 0x30, 0x11, 0x0e, 0xe1, + 0x11, 0x12, 0xe3, 0x11, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, 0xee, + 0xe0, 0x11, 0x13, 0xe2, 0x11, 0x11, 0xee, 0x01, 0x11, 0x3e, 0x21, 0x11, + 0x1e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x08, 0xee, + 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x54, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, 0x11, 0x12, + 0xe3, 0x01, 0x00, 0xee, 0x11, 0x11, 0x2e, 0xee, 0xee, 0xee, 0xe1, 0x11, + 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, 0x10, 0x1e, + 0xe2, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x2e, 0x1c, 0xee, 0x00, 0x60, + 0xe2, 0x01, 0x12, 0xee, 0x01, 0x00, 0x3e, 0x11, 0x11, 0x22, 0x20, 0x11, + 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x12, + 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, 0x11, 0x22, + 0x10, 0x11, 0x1e, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x11, + 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, + 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe2, + 0x11, 0x11, 0x00, 0x01, 0x02, 0xee, 0x21, 0x11, 0x10, 0x00, 0x00, 0x1e, + 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x08, 0x11, 0x00, 0x08, + 0x12, 0xee, 0xe3, 0x21, 0x08, 0x11, 0x02, 0x10, 0x02, 0x2e, 0x1c, 0xee, + 0x00, 0x76, 0xe2, 0x01, 0x11, 0x10, 0x01, 0x02, 0x3e, 0x11, 0x11, 0x11, + 0x01, 0x10, 0x0e, 0xe1, 0x11, 0x11, 0x10, 0x11, 0x00, 0xee, 0x10, 0x00, + 0x01, 0x11, 0x10, 0x1e, 0xe0, 0x11, 0x11, 0x10, 0x11, 0x01, 0xee, 0x01, + 0x11, 0x11, 0x01, 0x10, 0x1e, 0x30, 0x11, 0x11, 0x00, 0x10, 0x02, 0xe3, + 0x01, 0x11, 0x10, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x1e, 0xe1, 0x00, + 0x03, 0xee, 0x20, 0x00, 0x00, 0x00, 0x1e, 0xee, 0xe2, 0x00, 0x08, 0x00, + 0x00, 0x12, 0x0e, 0xe2, 0x00, 0x02, 0xe3, 0x10, 0x00, 0xee, 0x10, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x02, 0xee, 0xee, 0x30, 0x0a, 0x00, 0x02, 0x2e, + 0x1c, 0xee, 0x02, 0xe2, 0x08, 0x00, 0x00, 0x12, 0x03, 0xee, 0xe2, 0x00, + 0x00, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, 0x00, 0x06, 0x1e, 0xee, + 0x20, 0x00, 0x08, 0x00, 0x00, 0x1e, 0x0e, 0xee, 0x10, 0x00, 0x00, 0x00, + 0x2e, 0xee, 0x10, 0x00, 0x00, 0x00, 0x02, 0xee, 0xe3, 0x00, 0x08, 0x00, + 0x00, 0x06, 0x2e, 0xee, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x31, + 0x00, 0x0e, 0xe2, 0x00, 0x02, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, + 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x01}; unsigned int splash_bmp_len = 3828; diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index 50558a449..0c7018ff3 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -25,66 +25,65 @@ #include "buffer_exchange.hpp" struct BasebandCapture { - BasebandCapture(CaptureConfig* const config) { - baseband::capture_start(config); - } + BasebandCapture(CaptureConfig* const config) { + baseband::capture_start(config); + } - ~BasebandCapture() { - baseband::capture_stop(); - } + ~BasebandCapture() { + baseband::capture_stop(); + } }; // CaptureThread ////////////////////////////////////////////////////////// CaptureThread::CaptureThread( - std::unique_ptr writer, - size_t write_size, - size_t buffer_count, - std::function success_callback, - std::function error_callback -) : config { write_size, buffer_count }, - writer { std::move(writer) }, - success_callback { std::move(success_callback) }, - error_callback { std::move(error_callback) } -{ - // Need significant stack for FATFS - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, CaptureThread::static_fn, this); + std::unique_ptr writer, + size_t write_size, + size_t buffer_count, + std::function success_callback, + std::function error_callback) + : config{write_size, buffer_count}, + writer{std::move(writer)}, + success_callback{std::move(success_callback)}, + error_callback{std::move(error_callback)} { + // Need significant stack for FATFS + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, CaptureThread::static_fn, this); } CaptureThread::~CaptureThread() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } + if (thread) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } } msg_t CaptureThread::static_fn(void* arg) { - auto obj = static_cast(arg); - const auto error = obj->run(); - if( error.is_valid() && obj->error_callback ) { - obj->error_callback(error.value()); - } else { - if( obj->success_callback ) { - obj->success_callback(); - } - } - return 0; + auto obj = static_cast(arg); + const auto error = obj->run(); + if (error.is_valid() && obj->error_callback) { + obj->error_callback(error.value()); + } else { + if (obj->success_callback) { + obj->success_callback(); + } + } + return 0; } Optional CaptureThread::run() { - BasebandCapture capture { &config }; - BufferExchange buffers { &config }; + BasebandCapture capture{&config}; + BufferExchange buffers{&config}; - while( !chThdShouldTerminate() ) { - auto buffer = buffers.get(); - auto write_result = writer->write(buffer->data(), buffer->size()); - if( write_result.is_error() ) { - return write_result.error(); - } - buffer->empty(); - buffers.put(buffer); - } + while (!chThdShouldTerminate()) { + auto buffer = buffers.get(); + auto write_result = writer->write(buffer->data(), buffer->size()); + if (write_result.is_error()) { + return write_result.error(); + } + buffer->empty(); + buffers.put(buffer); + } - return { }; + return {}; } diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 225c19387..69a0d52a4 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -34,35 +34,34 @@ #include class CaptureThread { -public: - CaptureThread( - std::unique_ptr writer, - size_t write_size, - size_t buffer_count, - std::function success_callback, - std::function error_callback - ); - ~CaptureThread(); + public: + CaptureThread( + std::unique_ptr writer, + size_t write_size, + size_t buffer_count, + std::function success_callback, + std::function error_callback); + ~CaptureThread(); - CaptureThread(const CaptureThread&) = delete; - CaptureThread(CaptureThread&&) = delete; - CaptureThread& operator=(const CaptureThread&) = delete; - CaptureThread& operator=(CaptureThread&&) = delete; + CaptureThread(const CaptureThread&) = delete; + CaptureThread(CaptureThread&&) = delete; + CaptureThread& operator=(const CaptureThread&) = delete; + CaptureThread& operator=(CaptureThread&&) = delete; - const CaptureConfig& state() const { - return config; - } + const CaptureConfig& state() const { + return config; + } -private: - CaptureConfig config; - std::unique_ptr writer; - std::function success_callback; - std::function error_callback; - Thread* thread { nullptr }; + private: + CaptureConfig config; + std::unique_ptr writer; + std::function success_callback; + std::function error_callback; + Thread* thread{nullptr}; - static msg_t static_fn(void* arg); + static msg_t static_fn(void* arg); - Optional run(); + Optional run(); }; -#endif/*__CAPTURE_THREAD_H__*/ +#endif /*__CAPTURE_THREAD_H__*/ diff --git a/firmware/application/chconf.h b/firmware/application/chconf.h index b1968733d..6c4db559b 100755 --- a/firmware/application/chconf.h +++ b/firmware/application/chconf.h @@ -42,7 +42,7 @@ * setting also defines the system tick time unit. */ #if !defined(CH_FREQUENCY) || defined(__DOXYGEN__) -#define CH_FREQUENCY 1000 +#define CH_FREQUENCY 1000 #endif /** @@ -57,7 +57,7 @@ * and generally faster. */ #if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__) -#define CH_TIME_QUANTUM 0 +#define CH_TIME_QUANTUM 0 #endif /** @@ -72,7 +72,7 @@ * @note Requires @p CH_USE_MEMCORE. */ #if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__) -#define CH_MEMCORE_SIZE 0 +#define CH_MEMCORE_SIZE 0 #endif /** @@ -89,7 +89,7 @@ * enter a sleep state. */ #if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__) -#define CH_NO_IDLE_THREAD FALSE +#define CH_NO_IDLE_THREAD FALSE #endif /** @} */ @@ -110,7 +110,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__) -#define CH_OPTIMIZE_SPEED TRUE +#define CH_OPTIMIZE_SPEED TRUE #endif /** @} */ @@ -129,7 +129,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__) -#define CH_USE_REGISTRY TRUE +#define CH_USE_REGISTRY TRUE #endif /** @@ -140,7 +140,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__) -#define CH_USE_WAITEXIT TRUE +#define CH_USE_WAITEXIT TRUE #endif /** @@ -150,7 +150,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__) -#define CH_USE_SEMAPHORES TRUE +#define CH_USE_SEMAPHORES TRUE #endif /** @@ -162,7 +162,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__) -#define CH_USE_SEMAPHORES_PRIORITY FALSE +#define CH_USE_SEMAPHORES_PRIORITY FALSE #endif /** @@ -174,7 +174,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__) -#define CH_USE_SEMSW TRUE +#define CH_USE_SEMSW TRUE #endif /** @@ -184,7 +184,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__) -#define CH_USE_MUTEXES TRUE +#define CH_USE_MUTEXES TRUE #endif /** @@ -196,7 +196,7 @@ * @note Requires @p CH_USE_MUTEXES. */ #if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__) -#define CH_USE_CONDVARS TRUE +#define CH_USE_CONDVARS TRUE #endif /** @@ -208,7 +208,7 @@ * @note Requires @p CH_USE_CONDVARS. */ #if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__) -#define CH_USE_CONDVARS_TIMEOUT TRUE +#define CH_USE_CONDVARS_TIMEOUT TRUE #endif /** @@ -218,7 +218,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__) -#define CH_USE_EVENTS TRUE +#define CH_USE_EVENTS TRUE #endif /** @@ -230,7 +230,7 @@ * @note Requires @p CH_USE_EVENTS. */ #if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) -#define CH_USE_EVENTS_TIMEOUT TRUE +#define CH_USE_EVENTS_TIMEOUT TRUE #endif /** @@ -241,7 +241,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__) -#define CH_USE_MESSAGES TRUE +#define CH_USE_MESSAGES TRUE #endif /** @@ -253,7 +253,7 @@ * @note Requires @p CH_USE_MESSAGES. */ #if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__) -#define CH_USE_MESSAGES_PRIORITY FALSE +#define CH_USE_MESSAGES_PRIORITY FALSE #endif /** @@ -265,7 +265,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__) -#define CH_USE_MAILBOXES TRUE +#define CH_USE_MAILBOXES TRUE #endif /** @@ -275,7 +275,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__) -#define CH_USE_QUEUES TRUE +#define CH_USE_QUEUES TRUE #endif /** @@ -286,7 +286,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__) -#define CH_USE_MEMCORE TRUE +#define CH_USE_MEMCORE TRUE #endif /** @@ -300,7 +300,7 @@ * @note Mutexes are recommended. */ #if !defined(CH_USE_HEAP) || defined(__DOXYGEN__) -#define CH_USE_HEAP TRUE +#define CH_USE_HEAP TRUE #endif /** @@ -314,7 +314,7 @@ * appropriate documentation. */ #if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__) -#define CH_USE_MALLOC_HEAP FALSE +#define CH_USE_MALLOC_HEAP FALSE #endif /** @@ -325,7 +325,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__) -#define CH_USE_MEMPOOLS TRUE +#define CH_USE_MEMPOOLS TRUE #endif /** @@ -338,7 +338,7 @@ * @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS. */ #if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__) -#define CH_USE_DYNAMIC TRUE +#define CH_USE_DYNAMIC TRUE #endif /** @} */ @@ -358,7 +358,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__) -#define CH_DBG_SYSTEM_STATE_CHECK TRUE +#define CH_DBG_SYSTEM_STATE_CHECK TRUE #endif /** @@ -369,7 +369,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_CHECKS TRUE +#define CH_DBG_ENABLE_CHECKS TRUE #endif /** @@ -381,7 +381,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_ASSERTS TRUE +#define CH_DBG_ENABLE_ASSERTS TRUE #endif /** @@ -392,7 +392,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_TRACE FALSE +#define CH_DBG_ENABLE_TRACE FALSE #endif /** @@ -406,7 +406,7 @@ * @p panic_msg variable set to @p NULL. */ #if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_STACK_CHECK TRUE +#define CH_DBG_ENABLE_STACK_CHECK TRUE #endif /** @@ -418,7 +418,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__) -#define CH_DBG_FILL_THREADS TRUE +#define CH_DBG_FILL_THREADS TRUE #endif /** @@ -431,7 +431,7 @@ * some test cases into the test suite. */ #if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) -#define CH_DBG_THREADS_PROFILING TRUE +#define CH_DBG_THREADS_PROFILING TRUE #endif /** @} */ @@ -448,11 +448,11 @@ * @details User fields added to the end of the @p Thread structure. */ #if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__) -#define THREAD_EXT_FIELDS \ - /* Add threads custom fields here.*/ \ - uint32_t switches; \ - uint32_t start_ticks; \ - uint32_t total_ticks; +#define THREAD_EXT_FIELDS \ + /* Add threads custom fields here.*/ \ + uint32_t switches; \ + uint32_t start_ticks; \ + uint32_t total_ticks; #endif /** @@ -463,12 +463,13 @@ * the threads creation APIs. */ #if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__) -#define THREAD_EXT_INIT_HOOK(tp) { \ - /* Add threads initialization code here.*/ \ - tp->switches = 0; \ - tp->start_ticks = 0; \ - tp->total_ticks = 0; \ -} +#define THREAD_EXT_INIT_HOOK(tp) \ + { \ + /* Add threads initialization code here.*/ \ + tp->switches = 0; \ + tp->start_ticks = 0; \ + tp->total_ticks = 0; \ + } #endif /** @@ -480,9 +481,10 @@ * terminate. */ #if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__) -#define THREAD_EXT_EXIT_HOOK(tp) { \ - /* Add threads finalization code here.*/ \ -} +#define THREAD_EXT_EXIT_HOOK(tp) \ + { \ + /* Add threads finalization code here.*/ \ + } #endif /** @@ -490,12 +492,13 @@ * @details This hook is invoked just before switching between threads. */ #if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__) -#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \ - /* System halt code here.*/ \ - otp->switches++; \ - ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \ - otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \ -} +#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) \ + { \ + /* System halt code here.*/ \ + otp->switches++; \ + ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \ + otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \ + } #endif /** @@ -503,9 +506,10 @@ * @details This hook is continuously invoked by the idle thread loop. */ #if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__) -#define IDLE_LOOP_HOOK() { \ - /* Idle loop code here.*/ \ -} +#define IDLE_LOOP_HOOK() \ + { \ + /* Idle loop code here.*/ \ + } #endif /** @@ -514,9 +518,10 @@ * after processing the virtual timers queue. */ #if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__) -#define SYSTEM_TICK_EVENT_HOOK() { \ - /* System tick event code here.*/ \ -} +#define SYSTEM_TICK_EVENT_HOOK() \ + { \ + /* System tick event code here.*/ \ + } #endif /** @@ -525,9 +530,10 @@ * the system is halted. */ #if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__) -#define SYSTEM_HALT_HOOK() { \ - /* System halt code here.*/ \ -} +#define SYSTEM_HALT_HOOK() \ + { \ + /* System halt code here.*/ \ + } #endif /** @} */ @@ -536,8 +542,8 @@ /* Port-specific settings (override port settings defaulted in chcore.h). */ /*===========================================================================*/ -#define CORTEX_ENABLE_WFI_IDLE TRUE +#define CORTEX_ENABLE_WFI_IDLE TRUE -#endif /* _CHCONF_H_ */ +#endif /* _CHCONF_H_ */ /** @} */ diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index beb27b90e..9f5c654bf 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -30,118 +30,112 @@ using namespace hackrf::one; #include "lpc43xx_cpp.hpp" using namespace lpc43xx; -constexpr uint32_t si5351_vco_f = 800000000; +constexpr uint32_t si5351_vco_f = 800000000; -constexpr si5351::Inputs si5351_inputs { - .f_xtal = si5351_xtal_f, - .f_clkin = si5351_clkin_f, - .clkin_div = 1, +constexpr si5351::Inputs si5351_inputs{ + .f_xtal = si5351_xtal_f, + .f_clkin = si5351_clkin_f, + .clkin_div = 1, }; -static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong"); +static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong"); static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong"); -constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources { - si5351::PLLInputSource::PLLA_Source_XTAL - | si5351::PLLInputSource::PLLB_Source_CLKIN - | si5351::PLLInputSource::CLKIN_Div1 -}; +constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources{ + si5351::PLLInputSource::PLLA_Source_XTAL | si5351::PLLInputSource::PLLB_Source_CLKIN | si5351::PLLInputSource::CLKIN_Div1}; -constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources { - si5351::PLLInputSource::PLLA_Source_XTAL - | si5351::PLLInputSource::PLLB_Source_XTAL - | si5351::PLLInputSource::CLKIN_Div1 -}; +constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources{ + si5351::PLLInputSource::PLLA_Source_XTAL | si5351::PLLInputSource::PLLB_Source_XTAL | si5351::PLLInputSource::CLKIN_Div1}; -constexpr si5351::PLL si5351_pll_xtal_25m { - .f_in = si5351_inputs.f_xtal, - .a = 32, - .b = 0, - .c = 1, +constexpr si5351::PLL si5351_pll_xtal_25m{ + .f_in = si5351_inputs.f_xtal, + .a = 32, + .b = 0, + .c = 1, }; constexpr auto si5351_pll_a_xtal_reg = si5351_pll_xtal_25m.reg(0); -constexpr si5351::PLL si5351_pll_clkin_10m { - .f_in = si5351_inputs.f_clkin_out(), - .a = 80, - .b = 0, - .c = 1, +constexpr si5351::PLL si5351_pll_clkin_10m{ + .f_in = si5351_inputs.f_clkin_out(), + .a = 80, + .b = 0, + .c = 1, }; constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1); constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0); static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong"); -static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong"); -static_assert(si5351_pll_xtal_25m.p2() == 0, "PLL XTAL P2 wrong"); -static_assert(si5351_pll_xtal_25m.p3() == 1, "PLL XTAL P3 wrong"); +static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong"); +static_assert(si5351_pll_xtal_25m.p2() == 0, "PLL XTAL P2 wrong"); +static_assert(si5351_pll_xtal_25m.p3() == 1, "PLL XTAL P3 wrong"); static_assert(si5351_pll_clkin_10m.f_vco() == si5351_vco_f, "PLL CLKIN frequency wrong"); -static_assert(si5351_pll_clkin_10m.p1() == 9728, "PLL CLKIN P1 wrong"); -static_assert(si5351_pll_clkin_10m.p2() == 0, "PLL CLKIN P2 wrong"); -static_assert(si5351_pll_clkin_10m.p3() == 1, "PLL CLKIN P3 wrong"); +static_assert(si5351_pll_clkin_10m.p1() == 9728, "PLL CLKIN P1 wrong"); +static_assert(si5351_pll_clkin_10m.p2() == 0, "PLL CLKIN P2 wrong"); +static_assert(si5351_pll_clkin_10m.p3() == 1, "PLL CLKIN P3 wrong"); /* constexpr si5351::MultisynthFractional si5351_ms_18m432 { - .f_src = si5351_vco_f, - .a = 43, - .b = 29, - .c = 72, - .r_div = 1, + .f_src = si5351_vco_f, + .a = 43, + .b = 29, + .c = 72, + .r_div = 1, }; */ /* constexpr si5351::MultisynthFractional si5351_ms_0_20m { - .f_src = si5351_vco_f, - .a = 20, - .b = 0, - .c = 1, - .r_div = 1, + .f_src = si5351_vco_f, + .a = 20, + .b = 0, + .c = 1, + .r_div = 1, }; constexpr auto si5351_ms_0_20m_reg = si5351_ms_0_20m.reg(0); */ -constexpr si5351::MultisynthFractional si5351_ms_0_8m { - .f_src = si5351_vco_f, - .a = 50, - .b = 0, - .c = 1, - .r_div = 1, +constexpr si5351::MultisynthFractional si5351_ms_0_8m{ + .f_src = si5351_vco_f, + .a = 50, + .b = 0, + .c = 1, + .r_div = 1, }; constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec); -constexpr si5351::MultisynthFractional si5351_ms_group { - .f_src = si5351_vco_f, - .a = 80, /* Don't care */ - .b = 0, - .c = 1, - .r_div = 0, +constexpr si5351::MultisynthFractional si5351_ms_group{ + .f_src = si5351_vco_f, + .a = 80, /* Don't care */ + .b = 0, + .c = 1, + .r_div = 0, }; constexpr auto si5351c_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_og_cpld); constexpr auto si5351c_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_og_sgpio); -constexpr si5351::MultisynthFractional si5351_ms_16m { - .f_src = si5351_vco_f, - .a = 50, - .b = 0, - .c = 1, - .r_div = 0, +constexpr si5351::MultisynthFractional si5351_ms_16m{ + .f_src = si5351_vco_f, + .a = 50, + .b = 0, + .c = 1, + .r_div = 0, }; constexpr auto si5351a_ms_1_sgpio_16m_reg = si5351_ms_16m.reg(clock_generator_output_r9_sgpio); -constexpr si5351::MultisynthFractional si5351_ms_10m { - .f_src = si5351_vco_f, - .a = 80, - .b = 0, - .c = 1, - .r_div = 0, +constexpr si5351::MultisynthFractional si5351_ms_10m{ + .f_src = si5351_vco_f, + .a = 80, + .b = 0, + .c = 1, + .r_div = 0, }; constexpr auto si5351c_ms_3_10m_reg = si5351_ms_10m.reg(3); constexpr auto si5351a_ms_2_mcu_10m_reg = si5351_ms_10m.reg(clock_generator_output_r9_mcu_clkin); -constexpr si5351::MultisynthFractional si5351_ms_40m { - .f_src = si5351_vco_f, - .a = 20, - .b = 0, - .c = 1, - .r_div = 0, +constexpr si5351::MultisynthFractional si5351_ms_40m{ + .f_src = si5351_vco_f, + .a = 20, + .b = 0, + .c = 1, + .r_div = 0, }; constexpr auto si5351_ms_rffc5072 = si5351_ms_40m; @@ -152,30 +146,30 @@ constexpr auto si5351c_ms_5_reg = si5351_ms_max283x.reg(clock_generator_output_o constexpr auto si5351a_ms_0_if_40m_reg = si5351_ms_40m.reg(clock_generator_output_r9_if); static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong"); -static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong"); -static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong"); -static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong"); +static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong"); +static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong"); +static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong"); static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong"); static_assert(si5351_ms_max283x.f_out() == max283x_reference_f, "MAX283x reference f_out wrong"); -constexpr si5351::MultisynthInteger si5351_ms_int_off { - .f_src = si5351_vco_f, - .a = 255, - .r_div = 0, +constexpr si5351::MultisynthInteger si5351_ms_int_off{ + .f_src = si5351_vco_f, + .a = 255, + .r_div = 0, }; -constexpr si5351::MultisynthInteger si5351_ms_int_40m { - .f_src = si5351_vco_f, - .a = 20, - .r_div = 0, +constexpr si5351::MultisynthInteger si5351_ms_int_40m{ + .f_src = si5351_vco_f, + .a = 20, + .r_div = 0, }; -constexpr si5351::MultisynthInteger si5351_ms_int_10m { - .f_src = si5351_vco_f, - .a = 80, - .r_div = 0, +constexpr si5351::MultisynthInteger si5351_ms_int_10m{ + .f_src = si5351_vco_f, + .a = 80, + .r_div = 0, }; constexpr auto si5351c_ms_int_mcu_clkin = si5351_ms_int_40m; @@ -185,7 +179,7 @@ constexpr auto si5351c_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int constexpr auto si5351a_ms6_7_off_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_off); static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong"); -static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong"); +static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong"); static_assert(si5351c_ms_int_mcu_clkin.f_out() == mcu_clkin_og_f, "MS int MCU CLKIN OG f_out wrong"); static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CLKIN r9 f_out wrong"); @@ -193,405 +187,394 @@ static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CL using namespace si5351; static constexpr ClockControl::MultiSynthSource get_si5351c_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) { - return (reference_source == ClockManager::ReferenceSource::Xtal) - ? ClockControl::MultiSynthSource::PLLA - : ClockControl::MultiSynthSource::PLLB - ; + return (reference_source == ClockManager::ReferenceSource::Xtal) + ? ClockControl::MultiSynthSource::PLLA + : ClockControl::MultiSynthSource::PLLB; } -constexpr ClockControls si5351c_clock_control_common { { - { ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, -} }; - -constexpr ClockControls si5351a_clock_control_common { { - { ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, - { ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off }, -} }; +constexpr ClockControls si5351c_clock_control_common{{ + {ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, +}}; + +constexpr ClockControls si5351a_clock_control_common{{ + {ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, +}}; ClockManager::Reference ClockManager::get_reference() const { - return reference; + return reference; } static void portapack_tcxo_enable() { - portapack::io.reference_oscillator(true); + portapack::io.reference_oscillator(true); - /* Delay >10ms at 96MHz clock speed for reference oscillator to start. */ - /* Delay an additional 1ms (arbitrary) for the clock generator to detect a signal. */ - volatile uint32_t delay = 240000 + 24000; - while(delay--); + /* Delay >10ms at 96MHz clock speed for reference oscillator to start. */ + /* Delay an additional 1ms (arbitrary) for the clock generator to detect a signal. */ + volatile uint32_t delay = 240000 + 24000; + while (delay--) + ; } static void portapack_tcxo_disable() { - portapack::io.reference_oscillator(false); + portapack::io.reference_oscillator(false); } #include "hackrf_gpio.hpp" using namespace hackrf::one; void ClockManager::init_clock_generator() { - clock_generator.reset(); - clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF); - clock_generator.enable_fanout(); - clock_generator.set_pll_input_sources(hackrf_r9 - ? si5351a_pll_input_sources - : si5351c_pll_input_sources); - - auto si5351_clock_control_common = hackrf_r9 - ? si5351a_clock_control_common - : si5351c_clock_control_common; - - auto clock_generator_output_mcu_clkin = hackrf_r9 - ? clock_generator_output_r9_mcu_clkin - : clock_generator_output_og_mcu_clkin; - - clock_generator.set_clock_control( - clock_generator_output_mcu_clkin, - si5351_clock_control_common[clock_generator_output_mcu_clkin] - .clk_src(hackrf_r9 - ? ClockControl::ClockSource::Xtal - : ClockControl::ClockSource::CLKIN) - .clk_pdn(ClockControl::ClockPowerDown::Power_On) - ); - clock_generator.enable_output(clock_generator_output_mcu_clkin); - - reference = choose_reference(); - - clock_generator.disable_output(clock_generator_output_mcu_clkin); - - const auto ref_pll = hackrf_r9 - ? ClockControl::MultiSynthSource::PLLA - : get_si5351c_reference_clock_generator_pll(reference.source); - - const ClockControls si5351_clock_control = ClockControls { { - si5351_clock_control_common[0].ms_src(ref_pll), - si5351_clock_control_common[1].ms_src(ref_pll), - si5351_clock_control_common[2].ms_src(ref_pll), - si5351_clock_control_common[3].ms_src(ref_pll), - si5351_clock_control_common[4].ms_src(ref_pll), - si5351_clock_control_common[5].ms_src(ref_pll), - si5351_clock_control_common[6].ms_src(ref_pll), - si5351_clock_control_common[7].ms_src(ref_pll), - } }; - clock_generator.set_clock_control(si5351_clock_control); - - if (hackrf_r9) { - const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal) - ? si5351_pll_a_xtal_reg - : si5351a_pll_a_clkin_reg; - clock_generator.write(pll_reg); - clock_generator.write(si5351a_ms_0_if_40m_reg); - clock_generator.write(si5351a_ms_1_sgpio_16m_reg); - clock_generator.write(si5351a_ms_2_mcu_10m_reg); - clock_generator.write(si5351a_ms6_7_off_reg); - } else { - clock_generator.write(si5351_pll_a_xtal_reg); - clock_generator.write(si5351c_pll_b_clkin_reg); - clock_generator.write(si5351c_ms_0_8m_reg); - clock_generator.write(si5351c_ms_1_group_reg); - clock_generator.write(si5351c_ms_2_group_reg); - clock_generator.write(si5351c_ms_3_10m_reg); - clock_generator.write(si5351c_ms_4_reg); - clock_generator.write(si5351c_ms_5_reg); - clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg); - } - - clock_generator.reset_plls(); - - // Wait for PLL(s) to lock. - uint8_t device_status_mask = hackrf_r9 - ? 0x20 - : (ref_pll == ClockControl::MultiSynthSource::PLLB) - ? 0x40 - : 0x20; - while((clock_generator.device_status() & device_status_mask) != 0); - - clock_generator.set_clock_control( - clock_generator_output_mcu_clkin, - si5351_clock_control_common[clock_generator_output_mcu_clkin].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On) - ); - clock_generator.enable_output(clock_generator_output_mcu_clkin); + clock_generator.reset(); + clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF); + clock_generator.enable_fanout(); + clock_generator.set_pll_input_sources(hackrf_r9 + ? si5351a_pll_input_sources + : si5351c_pll_input_sources); + + auto si5351_clock_control_common = hackrf_r9 + ? si5351a_clock_control_common + : si5351c_clock_control_common; + + auto clock_generator_output_mcu_clkin = hackrf_r9 + ? clock_generator_output_r9_mcu_clkin + : clock_generator_output_og_mcu_clkin; + + clock_generator.set_clock_control( + clock_generator_output_mcu_clkin, + si5351_clock_control_common[clock_generator_output_mcu_clkin] + .clk_src(hackrf_r9 + ? ClockControl::ClockSource::Xtal + : ClockControl::ClockSource::CLKIN) + .clk_pdn(ClockControl::ClockPowerDown::Power_On)); + clock_generator.enable_output(clock_generator_output_mcu_clkin); + + reference = choose_reference(); + + clock_generator.disable_output(clock_generator_output_mcu_clkin); + + const auto ref_pll = hackrf_r9 + ? ClockControl::MultiSynthSource::PLLA + : get_si5351c_reference_clock_generator_pll(reference.source); + + const ClockControls si5351_clock_control = ClockControls{{ + si5351_clock_control_common[0].ms_src(ref_pll), + si5351_clock_control_common[1].ms_src(ref_pll), + si5351_clock_control_common[2].ms_src(ref_pll), + si5351_clock_control_common[3].ms_src(ref_pll), + si5351_clock_control_common[4].ms_src(ref_pll), + si5351_clock_control_common[5].ms_src(ref_pll), + si5351_clock_control_common[6].ms_src(ref_pll), + si5351_clock_control_common[7].ms_src(ref_pll), + }}; + clock_generator.set_clock_control(si5351_clock_control); + + if (hackrf_r9) { + const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal) + ? si5351_pll_a_xtal_reg + : si5351a_pll_a_clkin_reg; + clock_generator.write(pll_reg); + clock_generator.write(si5351a_ms_0_if_40m_reg); + clock_generator.write(si5351a_ms_1_sgpio_16m_reg); + clock_generator.write(si5351a_ms_2_mcu_10m_reg); + clock_generator.write(si5351a_ms6_7_off_reg); + } else { + clock_generator.write(si5351_pll_a_xtal_reg); + clock_generator.write(si5351c_pll_b_clkin_reg); + clock_generator.write(si5351c_ms_0_8m_reg); + clock_generator.write(si5351c_ms_1_group_reg); + clock_generator.write(si5351c_ms_2_group_reg); + clock_generator.write(si5351c_ms_3_10m_reg); + clock_generator.write(si5351c_ms_4_reg); + clock_generator.write(si5351c_ms_5_reg); + clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg); + } + + clock_generator.reset_plls(); + + // Wait for PLL(s) to lock. + uint8_t device_status_mask = hackrf_r9 + ? 0x20 + : (ref_pll == ClockControl::MultiSynthSource::PLLB) + ? 0x40 + : 0x20; + while ((clock_generator.device_status() & device_status_mask) != 0) + ; + + clock_generator.set_clock_control( + clock_generator_output_mcu_clkin, + si5351_clock_control_common[clock_generator_output_mcu_clkin].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On)); + clock_generator.enable_output(clock_generator_output_mcu_clkin); } uint32_t ClockManager::measure_gp_clkin_frequency() { - // Measure Si5351B CLKIN frequency against LPC43xx IRC oscillator - start_frequency_monitor_measurement(cgu::CLK_SEL::GP_CLKIN); - wait_For_frequency_monitor_measurement_done(); - return get_frequency_monitor_measurement_in_hertz(); + // Measure Si5351B CLKIN frequency against LPC43xx IRC oscillator + start_frequency_monitor_measurement(cgu::CLK_SEL::GP_CLKIN); + wait_For_frequency_monitor_measurement_done(); + return get_frequency_monitor_measurement_in_hertz(); } bool ClockManager::loss_of_signal() { - return hackrf_r9 - ? clock_generator.plla_loss_of_signal() - : clock_generator.clkin_loss_of_signal(); + return hackrf_r9 + ? clock_generator.plla_loss_of_signal() + : clock_generator.clkin_loss_of_signal(); } ClockManager::ReferenceSource ClockManager::detect_reference_source() { - if(loss_of_signal()) { - // No external reference. Turn on PortaPack reference (if present). - portapack_tcxo_enable(); - - if(loss_of_signal()) { - // No PortaPack reference was detected. Choose the HackRF crystal as the reference. - return ReferenceSource::Xtal; - } else { - return ReferenceSource::PortaPack; - } - } else { - return ReferenceSource::External; - } + if (loss_of_signal()) { + // No external reference. Turn on PortaPack reference (if present). + portapack_tcxo_enable(); + + if (loss_of_signal()) { + // No PortaPack reference was detected. Choose the HackRF crystal as the reference. + return ReferenceSource::Xtal; + } else { + return ReferenceSource::PortaPack; + } + } else { + return ReferenceSource::External; + } } ClockManager::Reference ClockManager::choose_reference() { - if (hackrf_r9) { - gpio_r9_clkin_en.write(1); - volatile uint32_t delay = 240000 + 24000; - while(delay--); - } - const auto detected_reference = detect_reference_source(); - - if( (detected_reference == ReferenceSource::External) || - (detected_reference == ReferenceSource::PortaPack) ) { - const auto frequency = measure_gp_clkin_frequency(); - if( (frequency >= 9850000) && (frequency <= 10150000) ) { - - return { detected_reference, 10000000 }; - } - } - - if (hackrf_r9) { - gpio_r9_clkin_en.write(0); - } - - portapack_tcxo_disable(); - return { ReferenceSource::Xtal, 25000000 }; + if (hackrf_r9) { + gpio_r9_clkin_en.write(1); + volatile uint32_t delay = 240000 + 24000; + while (delay--) + ; + } + const auto detected_reference = detect_reference_source(); + + if ((detected_reference == ReferenceSource::External) || + (detected_reference == ReferenceSource::PortaPack)) { + const auto frequency = measure_gp_clkin_frequency(); + if ((frequency >= 9850000) && (frequency <= 10150000)) { + return {detected_reference, 10000000}; + } + } + + if (hackrf_r9) { + gpio_r9_clkin_en.write(0); + } + + portapack_tcxo_disable(); + return {ReferenceSource::Xtal, 25000000}; } void ClockManager::shutdown() { - clock_generator.reset(); + clock_generator.reset(); } void ClockManager::enable_codec_clocks() { - if (hackrf_r9) { - clock_generator.enable_clock(clock_generator_output_r9_sgpio); - } else { - clock_generator.enable_clock(clock_generator_output_og_codec); - clock_generator.enable_clock(clock_generator_output_og_cpld); - clock_generator.enable_clock(clock_generator_output_og_sgpio); - } - /* Turn on all outputs at the same time. This probably doesn't ensure - * their phase relationships. For example, clocks that output frequencies - * in a 2:1 relationship may start with the slower clock high or low? - */ - if (hackrf_r9) { - clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio); - } else { - clock_generator.enable_output_mask( - (1U << clock_generator_output_og_codec) - | (1U << clock_generator_output_og_cpld) - | (1U << clock_generator_output_og_sgpio) - ); - } + if (hackrf_r9) { + clock_generator.enable_clock(clock_generator_output_r9_sgpio); + } else { + clock_generator.enable_clock(clock_generator_output_og_codec); + clock_generator.enable_clock(clock_generator_output_og_cpld); + clock_generator.enable_clock(clock_generator_output_og_sgpio); + } + /* Turn on all outputs at the same time. This probably doesn't ensure + * their phase relationships. For example, clocks that output frequencies + * in a 2:1 relationship may start with the slower clock high or low? + */ + if (hackrf_r9) { + clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio); + } else { + clock_generator.enable_output_mask( + (1U << clock_generator_output_og_codec) | (1U << clock_generator_output_og_cpld) | (1U << clock_generator_output_og_sgpio)); + } } void ClockManager::disable_codec_clocks() { - /* Turn off outputs before disabling clocks. It seems the clock needs to - * be enabled for the output to come to rest at the state specified by - * CLKx_DISABLE_STATE. - */ - if (hackrf_r9) { - clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio); - clock_generator.disable_clock(clock_generator_output_r9_sgpio); - } else { - clock_generator.disable_output_mask( - (1U << clock_generator_output_og_codec) - | (1U << clock_generator_output_og_cpld) - | (1U << clock_generator_output_og_sgpio) - ); - clock_generator.disable_clock(clock_generator_output_og_codec); - clock_generator.disable_clock(clock_generator_output_og_cpld); - clock_generator.disable_clock(clock_generator_output_og_sgpio); - } - + /* Turn off outputs before disabling clocks. It seems the clock needs to + * be enabled for the output to come to rest at the state specified by + * CLKx_DISABLE_STATE. + */ + if (hackrf_r9) { + clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio); + clock_generator.disable_clock(clock_generator_output_r9_sgpio); + } else { + clock_generator.disable_output_mask( + (1U << clock_generator_output_og_codec) | (1U << clock_generator_output_og_cpld) | (1U << clock_generator_output_og_sgpio)); + clock_generator.disable_clock(clock_generator_output_og_codec); + clock_generator.disable_clock(clock_generator_output_og_cpld); + clock_generator.disable_clock(clock_generator_output_og_sgpio); + } } void ClockManager::enable_if_clocks() { - if (hackrf_r9) { - clock_generator.enable_clock(clock_generator_output_r9_if); - clock_generator.enable_output_mask(1U << clock_generator_output_r9_if); - } else { - clock_generator.enable_clock(clock_generator_output_og_first_if); - clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if); - clock_generator.enable_clock(clock_generator_output_og_second_if); - clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if); - - } + if (hackrf_r9) { + clock_generator.enable_clock(clock_generator_output_r9_if); + clock_generator.enable_output_mask(1U << clock_generator_output_r9_if); + } else { + clock_generator.enable_clock(clock_generator_output_og_first_if); + clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if); + clock_generator.enable_clock(clock_generator_output_og_second_if); + clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if); + } } void ClockManager::disable_if_clocks() { - if (hackrf_r9) { - clock_generator.disable_output_mask(1U << clock_generator_output_r9_if); - clock_generator.disable_clock(clock_generator_output_r9_if); - } else { - clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if); - clock_generator.disable_clock(clock_generator_output_og_first_if); - clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if); - clock_generator.disable_clock(clock_generator_output_og_second_if); - } + if (hackrf_r9) { + clock_generator.disable_output_mask(1U << clock_generator_output_r9_if); + clock_generator.disable_clock(clock_generator_output_r9_if); + } else { + clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if); + clock_generator.disable_clock(clock_generator_output_og_first_if); + clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if); + clock_generator.disable_clock(clock_generator_output_og_second_if); + } } void ClockManager::set_sampling_frequency(const uint32_t frequency) { - /* Codec clock is at sampling frequency, CPLD and SGPIO clocks are at - * twice the frequency, and derived from the MS0 synth. So it's only - * necessary to change the MS0 synth frequency, and ensure the output - * is divided by two. - */ - if (hackrf_r9) { - clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0); - } else { - clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1); - } + /* Codec clock is at sampling frequency, CPLD and SGPIO clocks are at + * twice the frequency, and derived from the MS0 synth. So it's only + * necessary to change the MS0 synth frequency, and ensure the output + * is divided by two. + */ + if (hackrf_r9) { + clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0); + } else { + clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1); + } } void ClockManager::set_reference_ppb(const int32_t ppb) { - /* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal. - * It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment. - * TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too. - */ - if (hackrf_r9 && reference.source != ReferenceSource::Xtal) { - return; - } - constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a; - constexpr uint32_t denominator = 1000000 / pll_multiplier; - const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1); - const uint32_t new_b = (ppb >= 0) ? (ppb / 1000) : (denominator + (ppb / 1000)); - const uint32_t new_c = (ppb == 0) ? 1 : denominator; - - const si5351::PLL pll { - .f_in = si5351_inputs.f_xtal, - .a = new_a, - .b = new_b, - .c = new_c, - }; - const auto pll_a_reg = pll.reg(0); - clock_generator.write(pll_a_reg); + /* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal. + * It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment. + * TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too. + */ + if (hackrf_r9 && reference.source != ReferenceSource::Xtal) { + return; + } + constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a; + constexpr uint32_t denominator = 1000000 / pll_multiplier; + const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1); + const uint32_t new_b = (ppb >= 0) ? (ppb / 1000) : (denominator + (ppb / 1000)); + const uint32_t new_c = (ppb == 0) ? 1 : denominator; + + const si5351::PLL pll{ + .f_in = si5351_inputs.f_xtal, + .a = new_a, + .b = new_b, + .c = new_c, + }; + const auto pll_a_reg = pll.reg(0); + clock_generator.write(pll_a_reg); } void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel) { - // Measure a clock input for 480 cycles of the LPC43xx IRC. - LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type { - .RCNT = 480, - .FCNT = 0, - .MEAS = 0, - .CLK_SEL = toUType(clk_sel), - .RESERVED0 = 0 - }; - LPC_CGU->FREQ_MON.MEAS = 1; + // Measure a clock input for 480 cycles of the LPC43xx IRC. + LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type{ + .RCNT = 480, + .FCNT = 0, + .MEAS = 0, + .CLK_SEL = toUType(clk_sel), + .RESERVED0 = 0}; + LPC_CGU->FREQ_MON.MEAS = 1; } void ClockManager::wait_For_frequency_monitor_measurement_done() { - // FREQ_MON mechanism fails to finish if there's no clock present on selected input?! - while(LPC_CGU->FREQ_MON.MEAS == 1); + // FREQ_MON mechanism fails to finish if there's no clock present on selected input?! + while (LPC_CGU->FREQ_MON.MEAS == 1) + ; } uint32_t ClockManager::get_frequency_monitor_measurement_in_hertz() { - // Measurement is only as accurate as the LPC43xx IRC oscillator, - // which is +/- 1.5%. Measurement is for 480 IRC clcocks. Scale - // the cycle count to get a value in Hertz. - return LPC_CGU->FREQ_MON.FCNT * 25000; + // Measurement is only as accurate as the LPC43xx IRC oscillator, + // which is +/- 1.5%. Measurement is for 480 IRC clcocks. Scale + // the cycle count to get a value in Hertz. + return LPC_CGU->FREQ_MON.FCNT * 25000; } void ClockManager::start_audio_pll() { - cgu::pll0audio::ctrl({ - .pd = 1, - .bypass = 0, - .directi = 0, - .directo = 0, - .clken = 0, - .frm = 0, - .autoblock = 1, - .pllfract_req = 0, - .sel_ext = 1, - .mod_pd = 1, - .clk_sel = cgu::CLK_SEL::GP_CLKIN, - }); - - /* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK: - * Fout=12.288MHz, Fcco=491.52MHz - * OG: PSEL=20, NSEL=125, MSEL=768 - * PDEC=31, NDEC=45, MDEC=30542 - * r9: PSEL=20, NSEL=125, MSEL=3072 - * PDEC=31, NDEC=45, MDEC=8308 - */ - cgu::pll0audio::mdiv({ - .mdec = hackrf_r9 ? 8308UL : 30542UL, - }); - cgu::pll0audio::np_div({ - .pdec = 31, - .ndec = 45, - }); - - cgu::pll0audio::frac({ - .pllfract_ctrl = 0, - }); - - cgu::pll0audio::power_up(); - while( !cgu::pll0audio::is_locked() ); - cgu::pll0audio::clock_enable(); - - set_base_audio_clock_divider(1); - - LPC_CGU->BASE_AUDIO_CLK.AUTOBLOCK = 1; - LPC_CGU->BASE_AUDIO_CLK.CLK_SEL = toUType(cgu::CLK_SEL::IDIVD); + cgu::pll0audio::ctrl({ + .pd = 1, + .bypass = 0, + .directi = 0, + .directo = 0, + .clken = 0, + .frm = 0, + .autoblock = 1, + .pllfract_req = 0, + .sel_ext = 1, + .mod_pd = 1, + .clk_sel = cgu::CLK_SEL::GP_CLKIN, + }); + + /* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK: + * Fout=12.288MHz, Fcco=491.52MHz + * OG: PSEL=20, NSEL=125, MSEL=768 + * PDEC=31, NDEC=45, MDEC=30542 + * r9: PSEL=20, NSEL=125, MSEL=3072 + * PDEC=31, NDEC=45, MDEC=8308 + */ + cgu::pll0audio::mdiv({ + .mdec = hackrf_r9 ? 8308UL : 30542UL, + }); + cgu::pll0audio::np_div({ + .pdec = 31, + .ndec = 45, + }); + + cgu::pll0audio::frac({ + .pllfract_ctrl = 0, + }); + + cgu::pll0audio::power_up(); + while (!cgu::pll0audio::is_locked()) + ; + cgu::pll0audio::clock_enable(); + + set_base_audio_clock_divider(1); + + LPC_CGU->BASE_AUDIO_CLK.AUTOBLOCK = 1; + LPC_CGU->BASE_AUDIO_CLK.CLK_SEL = toUType(cgu::CLK_SEL::IDIVD); } void ClockManager::set_base_audio_clock_divider(const size_t divisor) { - LPC_CGU->IDIVD_CTRL.word = - (0 << 0) - | ((divisor - 1) << 2) - | (1 << 11) - | (toUType(cgu::CLK_SEL::PLL0AUDIO) << 24) - ; + LPC_CGU->IDIVD_CTRL.word = + (0 << 0) | ((divisor - 1) << 2) | (1 << 11) | (toUType(cgu::CLK_SEL::PLL0AUDIO) << 24); } void ClockManager::stop_audio_pll() { - cgu::pll0audio::clock_disable(); - cgu::pll0audio::power_down(); - while( cgu::pll0audio::is_locked() ); + cgu::pll0audio::clock_disable(); + cgu::pll0audio::power_down(); + while (cgu::pll0audio::is_locked()) + ; } void ClockManager::enable_clock_output(bool enable) { - if(enable) { - clock_generator.enable_output(clock_generator_output_og_clkout); - if(portapack::persistent_memory::clkout_freq() < 1000) { - clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7); - } else { - clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0); - } - } else { - clock_generator.disable_output(clock_generator_output_og_clkout); - } - - auto si5351_clock_control_common = hackrf_r9 - ? si5351a_clock_control_common - : si5351c_clock_control_common; - const auto ref_pll = hackrf_r9 - ? ClockControl::MultiSynthSource::PLLA - : get_si5351c_reference_clock_generator_pll(reference.source); - - if(enable) - clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On)); - else - clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off()); + if (enable) { + clock_generator.enable_output(clock_generator_output_og_clkout); + if (portapack::persistent_memory::clkout_freq() < 1000) { + clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7); + } else { + clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0); + } + } else { + clock_generator.disable_output(clock_generator_output_og_clkout); + } + + auto si5351_clock_control_common = hackrf_r9 + ? si5351a_clock_control_common + : si5351c_clock_control_common; + const auto ref_pll = hackrf_r9 + ? ClockControl::MultiSynthSource::PLLA + : get_si5351c_reference_clock_generator_pll(reference.source); + + if (enable) + clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On)); + else + clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off()); } diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index cfecea68f..94cdad8da 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -32,71 +32,70 @@ using namespace lpc43xx; class ClockManager { -public: - enum ReferenceSource { - Xtal, /* 10 MHz crystal onboard the HackRF */ - PortaPack, /* 10 MHz TCXO on 20180820 and newer PortaPack revisions. */ - External, /* HackRF external clock input SMA, or from PortaPack with TCXO feature. */ - }; - using ReferenceFrequency = uint32_t; + public: + enum ReferenceSource { + Xtal, /* 10 MHz crystal onboard the HackRF */ + PortaPack, /* 10 MHz TCXO on 20180820 and newer PortaPack revisions. */ + External, /* HackRF external clock input SMA, or from PortaPack with TCXO feature. */ + }; + using ReferenceFrequency = uint32_t; - typedef struct { - ReferenceSource source; - ReferenceFrequency frequency; - } Reference; + typedef struct { + ReferenceSource source; + ReferenceFrequency frequency; + } Reference; - constexpr ClockManager( - I2C& i2c0, - si5351::Si5351& clock_generator - ) : i2c0(i2c0), - clock_generator(clock_generator), - reference({ReferenceSource::Xtal, 10000000}) - { - } + constexpr ClockManager( + I2C& i2c0, + si5351::Si5351& clock_generator) + : i2c0(i2c0), + clock_generator(clock_generator), + reference({ReferenceSource::Xtal, 10000000}) { + } - void init_clock_generator(); - void shutdown(); + void init_clock_generator(); + void shutdown(); - void start_audio_pll(); - void stop_audio_pll(); + void start_audio_pll(); + void stop_audio_pll(); - void set_base_audio_clock_divider(const size_t divisor); + void set_base_audio_clock_divider(const size_t divisor); - void enable_codec_clocks(); - void disable_codec_clocks(); + void enable_codec_clocks(); + void disable_codec_clocks(); - void enable_if_clocks(); - void disable_if_clocks(); + void enable_if_clocks(); + void disable_if_clocks(); - void set_sampling_frequency(const uint32_t frequency); + void set_sampling_frequency(const uint32_t frequency); - void set_reference_ppb(const int32_t ppb); + void set_reference_ppb(const int32_t ppb); - uint32_t get_frequency_monitor_measurement_in_hertz(); + uint32_t get_frequency_monitor_measurement_in_hertz(); - Reference get_reference() const; + Reference get_reference() const; - void enable_clock_output(bool enable); + void enable_clock_output(bool enable); -private: - I2C& i2c0; - si5351::Si5351& clock_generator; - Reference reference; + private: + I2C& i2c0; + si5351::Si5351& clock_generator; + Reference reference; - void set_gp_clkin_to_clkin_direct(); + void set_gp_clkin_to_clkin_direct(); - void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); - void wait_For_frequency_monitor_measurement_done(); + void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); + void wait_For_frequency_monitor_measurement_done(); - void set_m4_clock_to_irc(); + void set_m4_clock_to_irc(); - void set_m4_clock_to_pll1(); + void set_m4_clock_to_pll1(); - uint32_t measure_gp_clkin_frequency(); + uint32_t measure_gp_clkin_frequency(); - ReferenceSource detect_reference_source(); - Reference choose_reference(); - bool loss_of_signal(); + ReferenceSource detect_reference_source(); + Reference choose_reference(); + bool loss_of_signal(); }; -#endif/*__CLOCK_MANAGER_H__*/ +#endif /*__CLOCK_MANAGER_H__*/ diff --git a/firmware/application/core_control.cpp b/firmware/application/core_control.cpp index 75cc2afb7..a037b0e3e 100644 --- a/firmware/application/core_control.cpp +++ b/firmware/application/core_control.cpp @@ -34,42 +34,40 @@ using namespace lpc43xx; #include void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset) { - const portapack::spi_flash::chunk_t* chunk = reinterpret_cast(portapack::spi_flash::images.base()); - while(chunk->tag) { - if(chunk->tag == image_tag) { + const portapack::spi_flash::chunk_t* chunk = reinterpret_cast(portapack::spi_flash::images.base()); + while (chunk->tag) { + if (chunk->tag == image_tag) { + const void* src = &chunk->data[0]; + void* dst = reinterpret_cast(to.base()); - const void *src = &chunk->data[0]; - void *dst = reinterpret_cast(to.base()); + /* extract and initialize M4 code RAM */ + unlz4_len(src, dst, chunk->compressed_data_size); - /* extract and initialize M4 code RAM */ - unlz4_len(src, dst, chunk->compressed_data_size); + /* M4 core is assumed to be sleeping with interrupts off, so we can mess + * with its address space and RAM without concern. + */ + LPC_CREG->M4MEMMAP = to.base(); - /* M4 core is assumed to be sleeping with interrupts off, so we can mess - * with its address space and RAM without concern. - */ - LPC_CREG->M4MEMMAP = to.base(); + /* Reset M4 core and optionally all peripherals */ + LPC_RGU->RESET_CTRL[0] = (full_reset) ? (1 << 1) // PERIPH_RST + : (1 << 13) // M4_RST + ; - /* Reset M4 core and optionally all peripherals */ - LPC_RGU->RESET_CTRL[0] = (full_reset) ? - (1 << 1) // PERIPH_RST - : (1 << 13) // M4_RST - ; + return; + } + chunk = chunk->next(); + } - return; - } - chunk = chunk->next(); - } - - chDbgPanic("NoImg"); + chDbgPanic("NoImg"); } void m4_request_shutdown() { - baseband::shutdown(); + baseband::shutdown(); } void m0_halt() { - rgu::reset(rgu::Reset::M0APP); - while(true) { - port_wait_for_interrupt(); - } + rgu::reset(rgu::Reset::M0APP); + while (true) { + port_wait_for_interrupt(); + } } diff --git a/firmware/application/core_control.hpp b/firmware/application/core_control.hpp index e34149c71..4cd2ac211 100644 --- a/firmware/application/core_control.hpp +++ b/firmware/application/core_control.hpp @@ -32,4 +32,4 @@ void m4_request_shutdown(); void m0_halt(); -#endif/*__CORE_CONTROL_H__*/ +#endif /*__CORE_CONTROL_H__*/ diff --git a/firmware/application/database.cpp b/firmware/application/database.cpp index 54bc1de4a..55f784e5f 100644 --- a/firmware/application/database.cpp +++ b/firmware/application/database.cpp @@ -27,81 +27,69 @@ namespace std { -int database::retrieve_mid_record(MidDBRecord* record, std::string search_term){ - - file_path = "AIS/mids.db"; - index_item_length = 4; - record_length = 32; +int database::retrieve_mid_record(MidDBRecord* record, std::string search_term) { + file_path = "AIS/mids.db"; + index_item_length = 4; + record_length = 32; - result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); + result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); - return(result); + return (result); } -int database::retrieve_airline_record(AirlinesDBRecord* record, std::string search_term){ - - file_path = "ADSB/airlines.db"; - index_item_length = 4; - record_length = 64; +int database::retrieve_airline_record(AirlinesDBRecord* record, std::string search_term) { + file_path = "ADSB/airlines.db"; + index_item_length = 4; + record_length = 64; - result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); + result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); - return(result); + return (result); } -int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term){ +int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term) { + file_path = "ADSB/icao24.db"; + index_item_length = 7; + record_length = 146; - file_path = "ADSB/icao24.db"; - index_item_length = 7; - record_length = 146; + result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); - result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term); - - return(result); + return (result); } - - -int database::retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term) -{ - - auto result = db_file.open(file_path); - if (!result.is_valid()) { - number_of_records = (db_file.size() / (index_item_length + record_length)); // determine number of records in file - // binary search tree - int first = 0, // First search element - last = number_of_records - 1, // Last search element - middle, // Mid point of search - position = -1; // Position of search value - - while (!found && first <= last) { - middle = (first + last) / 2; // Calculate mid point - db_file.seek(middle * index_item_length); - db_file.read(file_buffer, search_term.length()); - if (file_buffer == search_term) { // If value is found at mid - found = true; - position = middle; - } - else if (file_buffer > search_term) // If value is in lower half - last = middle - 1; - else - first = middle + 1; // If value is in upper half - } - - if(found == true) { - - db_file.seek((number_of_records * index_item_length) + (position * record_length)); // seek starting after index - db_file.read(record, record_length); - return(DATABASE_RECORD_FOUND); - } - else { - return(DATABASE_RECORD_NOT_FOUND); - } - - } - else return(DATABASE_NOT_FOUND); - +int database::retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term) { + auto result = db_file.open(file_path); + if (!result.is_valid()) { + number_of_records = (db_file.size() / (index_item_length + record_length)); // determine number of records in file + // binary search tree + int first = 0, // First search element + last = number_of_records - 1, // Last search element + middle, // Mid point of search + position = -1; // Position of search value + + while (!found && first <= last) { + middle = (first + last) / 2; // Calculate mid point + db_file.seek(middle * index_item_length); + db_file.read(file_buffer, search_term.length()); + if (file_buffer == search_term) { // If value is found at mid + found = true; + position = middle; + } else if (file_buffer > search_term) // If value is in lower half + last = middle - 1; + else + first = middle + 1; // If value is in upper half + } + + if (found == true) { + db_file.seek((number_of_records * index_item_length) + (position * record_length)); // seek starting after index + db_file.read(record, record_length); + return (DATABASE_RECORD_FOUND); + } else { + return (DATABASE_RECORD_NOT_FOUND); + } + + } else + return (DATABASE_NOT_FOUND); } - } /* namespace std */ diff --git a/firmware/application/database.hpp b/firmware/application/database.hpp index 9dee1a61a..a7d3f29d9 100644 --- a/firmware/application/database.hpp +++ b/firmware/application/database.hpp @@ -24,71 +24,59 @@ #ifndef __DATABASE_H__ #define __DATABASE_H__ - #include #include #include #include "file.hpp" - - namespace std { class database { + public: +#define DATABASE_RECORD_FOUND 0 // record found in database +#define DATABASE_NOT_FOUND -1 // database not found / could not be opened +#define DATABASE_RECORD_NOT_FOUND -2 // record could not be found in database + struct MidDBRecord { + char country[32]; // country name + }; + int retrieve_mid_record(MidDBRecord* record, std::string search_term); -public: - -#define DATABASE_RECORD_FOUND 0 // record found in database -#define DATABASE_NOT_FOUND -1 // database not found / could not be opened -#define DATABASE_RECORD_NOT_FOUND -2 // record could not be found in database - - struct MidDBRecord { - char country[32]; // country name - }; - - int retrieve_mid_record(MidDBRecord* record, std::string search_term); + struct AirlinesDBRecord { + char airline[32]; // airline name + char country[32]; // country name + }; - struct AirlinesDBRecord { - char airline[32]; // airline name - char country[32]; // country name - }; + int retrieve_airline_record(AirlinesDBRecord* record, std::string search_term); - int retrieve_airline_record(AirlinesDBRecord* record, std::string search_term); + struct AircraftDBRecord { + char aircraft_registration[9]; // aircraft registration + char aircraft_manufacturer[33]; // aircraft manufacturer + char aircraft_model[33]; // aircraft model + char icao_type[5]; // ICAO type descripton or when unavailable ICAO type designator + char aircraft_owner[33]; // aircraft owner + char aircraft_operator[33]; // aircraft operator + }; - struct AircraftDBRecord { - char aircraft_registration[9]; // aircraft registration - char aircraft_manufacturer[33]; // aircraft manufacturer - char aircraft_model[33]; // aircraft model - char icao_type[5]; // ICAO type descripton or when unavailable ICAO type designator - char aircraft_owner[33]; // aircraft owner - char aircraft_operator[33]; // aircraft operator + int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term); - }; + private: + string file_path = ""; // path inclusing filename + int index_item_length = 0; // length of index item + int record_length = 0; // length of record - int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term); + File db_file{}; + int number_of_records = 0; + int position = 0; -private: - string file_path = ""; // path inclusing filename - int index_item_length = 0; // length of index item - int record_length = 0; // length of record - - File db_file { }; - int number_of_records = 0; - int position = 0; - - char file_buffer[32] { 0 }; - bool found = false; - - int result = 0; - - int retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term); + char file_buffer[32]{0}; + bool found = false; + int result = 0; + int retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term); }; -} // namespace std - - +} // namespace std -#endif/*__DATABASE_H__*/ +#endif /*__DATABASE_H__*/ diff --git a/firmware/application/de_bruijn.cpp b/firmware/application/de_bruijn.cpp index ddd1ef793..97ae105ed 100644 --- a/firmware/application/de_bruijn.cpp +++ b/firmware/application/de_bruijn.cpp @@ -31,23 +31,23 @@ * The shift register is init with 1 and shifted left each step * The polynomial is kept on the right, and used as a AND mask applied on the corresponding shift register bits * The resulting bits are XORed together to produce the new bit pushed in the shift register - * + * * 0001 (init) * AND 1001 (polynomial) * 0001 XOR'd -> 1 - * + * * 00011 (shift left) * AND 1001 * 0001 XOR'd -> 1 - * + * * 000111 (shift left) * AND 1001 * 0001 XOR'd -> 1 - * + * * 0001111 (shift left) * AND 1001 * 1001 XOR'd -> 0 - * + * * 00011110 (shift left) * AND 1001 * 1000 XOR'd -> 1 @@ -73,32 +73,32 @@ */ size_t de_bruijn::init(const uint32_t n) { - // Cap - if ((n < 3) || (n > 16)) - length = 3; - else - length = n; - - poly = de_bruijn_polys[length - 3]; - shift_register = 1; - - return (1U << length) + (length - 1); + // Cap + if ((n < 3) || (n > 16)) + length = 3; + else + length = n; + + poly = de_bruijn_polys[length - 3]; + shift_register = 1; + + return (1U << length) + (length - 1); } uint32_t de_bruijn::compute(const uint32_t steps) { - uint32_t step, bits, masked; - uint8_t new_bit; - - for (step = 0; step < steps; step++) { - masked = shift_register & poly; - new_bit = 0; - for (bits = 0; bits < length; bits++) { - new_bit ^= (masked & 1); - masked >>= 1; - } - shift_register <<= 1; - shift_register |= new_bit; - } - - return shift_register; + uint32_t step, bits, masked; + uint8_t new_bit; + + for (step = 0; step < steps; step++) { + masked = shift_register & poly; + new_bit = 0; + for (bits = 0; bits < length; bits++) { + new_bit ^= (masked & 1); + masked >>= 1; + } + shift_register <<= 1; + shift_register |= new_bit; + } + + return shift_register; } diff --git a/firmware/application/de_bruijn.hpp b/firmware/application/de_bruijn.hpp index 937fb08e9..daed006a8 100644 --- a/firmware/application/de_bruijn.hpp +++ b/firmware/application/de_bruijn.hpp @@ -26,32 +26,31 @@ #define __DE_BRUIJN_H__ // n from 3 to 16 -const uint32_t de_bruijn_polys[14] { - 0b0000000000000101, - 0b0000000000001001, - 0b0000000000011011, - 0b0000000000110011, - 0b0000000001010011, - 0b0000000010001101, - 0b0000000100100001, - 0b0000001000100001, - 0b0000010001100001, - 0b0000110101000011, - 0b0001001001100101, - 0b0010101100000011, - 0b0101000000001001, - 0b1010000101000101 -}; +const uint32_t de_bruijn_polys[14]{ + 0b0000000000000101, + 0b0000000000001001, + 0b0000000000011011, + 0b0000000000110011, + 0b0000000001010011, + 0b0000000010001101, + 0b0000000100100001, + 0b0000001000100001, + 0b0000010001100001, + 0b0000110101000011, + 0b0001001001100101, + 0b0010101100000011, + 0b0101000000001001, + 0b1010000101000101}; struct de_bruijn { -public: - size_t init(const uint32_t n); - uint32_t compute(const uint32_t steps); + public: + size_t init(const uint32_t n); + uint32_t compute(const uint32_t steps); -private: - uint32_t length { }; - uint32_t poly { }; - uint32_t shift_register { }; + private: + uint32_t length{}; + uint32_t poly{}; + uint32_t shift_register{}; }; -#endif/*__DE_BRUIJN_H__*/ +#endif /*__DE_BRUIJN_H__*/ diff --git a/firmware/application/debug.cpp b/firmware/application/debug.cpp index 9621d0cad..bfd59c651 100644 --- a/firmware/application/debug.cpp +++ b/firmware/application/debug.cpp @@ -33,44 +33,41 @@ void runtime_error(LED); std::string number_to_hex_string(uint32_t); -void draw_line(int32_t, const char *, regarm_t); +void draw_line(int32_t, const char*, regarm_t); static bool error_shown = false; -void draw_guru_meditation_header(uint8_t source, const char *hint) { +void draw_guru_meditation_header(uint8_t source, const char* hint) { ui::Painter painter; - ui::Style style_default { + ui::Style style_default{ .font = ui::font::fixed_8x16, .background = ui::Color::black(), - .foreground = ui::Color::white() - }; + .foreground = ui::Color::white()}; painter.fill_rectangle( - { 0, 0, portapack::display.width(), portapack::display.height() }, - ui::Color::red() - ); + {0, 0, portapack::display.width(), portapack::display.height()}, + ui::Color::red()); constexpr int border = 8; painter.fill_rectangle( - { border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2) }, - ui::Color::black() - ); + {border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2)}, + ui::Color::black()); // NOTE: in situations like a hard fault it seems not possible to write strings longer than 16 characters. - painter.draw_string({ 48, 24 }, style_default, "M? Guru"); - painter.draw_string({ 48 + 8*8, 24 }, style_default, "Meditation"); + painter.draw_string({48, 24}, style_default, "M? Guru"); + painter.draw_string({48 + 8 * 8, 24}, style_default, "Meditation"); if (source == CORTEX_M0) - painter.draw_string({ 48 + 8, 24 }, style_default, "0"); - + painter.draw_string({48 + 8, 24}, style_default, "0"); + if (source == CORTEX_M4) - painter.draw_string({ 48 + 8, 24 }, style_default, "4"); - - painter.draw_string({ 15, 55 }, style_default, "Hint: "); - painter.draw_string({ 15 + 8*8, 55 }, style_default, hint); + painter.draw_string({48 + 8, 24}, style_default, "4"); + + painter.draw_string({15, 55}, style_default, "Hint: "); + painter.draw_string({15 + 8 * 8, 55}, style_default, hint); } -void draw_guru_meditation(uint8_t source, const char *hint) { - if(error_shown == false){ +void draw_guru_meditation(uint8_t source, const char* hint) { + if (error_shown == false) { error_shown = true; draw_guru_meditation_header(source, hint); } @@ -79,12 +76,11 @@ void draw_guru_meditation(uint8_t source, const char *hint) { runtime_error(hackrf::one::led_rx); if (source == CORTEX_M4) - runtime_error(hackrf::one::led_tx); + runtime_error(hackrf::one::led_tx); } -void draw_guru_meditation(uint8_t source, const char *hint, struct extctx *ctxp, uint32_t cfsr = 0) { - if(error_shown == false) - { +void draw_guru_meditation(uint8_t source, const char* hint, struct extctx* ctxp, uint32_t cfsr = 0) { + if (error_shown == false) { error_shown = true; draw_guru_meditation_header(source, hint); @@ -106,35 +102,35 @@ void draw_guru_meditation(uint8_t source, const char *hint, struct extctx *ctxp, // see SCB_CFSR_* in libopencm3/cm3/scb.h for details if (cfsr != 0) - draw_line(80 + i++ * 20, "cfsr:", (void *)cfsr); + draw_line(80 + i++ * 20, "cfsr:", (void*)cfsr); } } - + if (source == CORTEX_M0) runtime_error(hackrf::one::led_rx); if (source == CORTEX_M4) - runtime_error(hackrf::one::led_tx); + runtime_error(hackrf::one::led_tx); } -void draw_line(int32_t y_offset, const char *label, regarm_t value){ +void draw_line(int32_t y_offset, const char* label, regarm_t value) { ui::Painter painter; - ui::Style style_default { + ui::Style style_default{ .font = ui::font::fixed_8x16, .background = ui::Color::black(), - .foreground = ui::Color::white() - }; + .foreground = ui::Color::white()}; - painter.draw_string({ 15, y_offset }, style_default, label); - painter.draw_string({ 15 + 8*8, y_offset }, style_default, to_string_hex((uint32_t)value, 8)); + painter.draw_string({15, y_offset}, style_default, label); + painter.draw_string({15 + 8 * 8, y_offset}, style_default, to_string_hex((uint32_t)value, 8)); } void runtime_error(LED led) { led.off(); - while(true) { + while (true) { volatile size_t n = 1000000U; - while(n--); + while (n--) + ; led.toggle(); } } @@ -178,13 +174,13 @@ CH_IRQ_HANDLER(UsageFaultVector) { CH_IRQ_HANDLER(HardFaultVector) { #if CH_DBG_ENABLED CH_IRQ_PROLOGUE(); - struct extctx *ctxp; + struct extctx* ctxp; port_lock_from_isr(); if ((uint32_t)_saved_lr & 0x04) - ctxp = (struct extctx *)__get_PSP(); + ctxp = (struct extctx*)__get_PSP(); else - ctxp = (struct extctx *)__get_MSP(); + ctxp = (struct extctx*)__get_MSP(); port_disable(); diff --git a/firmware/application/debug.hpp b/firmware/application/debug.hpp index a538d59c7..995f851e2 100644 --- a/firmware/application/debug.hpp +++ b/firmware/application/debug.hpp @@ -25,19 +25,20 @@ #include "hackrf_gpio.hpp" -extern void draw_guru_meditation(uint8_t, const char *); -extern void draw_guru_meditation(uint8_t, const char *, struct extctx *, uint32_t); +extern void draw_guru_meditation(uint8_t, const char*); +extern void draw_guru_meditation(uint8_t, const char*, struct extctx*, uint32_t); extern uint32_t __process_stack_base__; extern uint32_t __process_stack_end__; -#define CRT0_STACKS_FILL_PATTERN 0x55555555 +#define CRT0_STACKS_FILL_PATTERN 0x55555555 -inline uint32_t get_free_stack_space(){ - uint32_t *p; - for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++); +inline uint32_t get_free_stack_space() { + uint32_t* p; + for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++) + ; auto stack_space_left = p - &__process_stack_base__; return stack_space_left; } -#endif/*__DEBUG_H__*/ +#endif /*__DEBUG_H__*/ diff --git a/firmware/application/demofont.hpp b/firmware/application/demofont.hpp index 26807d5e7..65bffe0d8 100644 --- a/firmware/application/demofont.hpp +++ b/firmware/application/demofont.hpp @@ -1,541 +1,540 @@ const uint8_t demofont_bin[] = { - 0xff, 0xff, 0xff, 0xff, 0xc7, 0xe4, 0xeb, 0xa3, 0xc0, 0xef, 0x82, 0x65, - 0xcc, 0x6a, 0x50, 0xc8, 0x8c, 0x99, 0xa6, 0x78, 0x76, 0x8d, 0x57, 0x66, - 0xa5, 0x3f, 0x52, 0x7d, 0x2d, 0x66, 0x91, 0x19, 0x46, 0x72, 0x00, 0x5e, - 0x72, 0x00, 0x28, 0x60, 0x00, 0x58, 0x41, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x97, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, - 0xd7, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x60, 0x00, 0x00, - 0xb9, 0x99, 0x99, 0x99, 0x99, 0x92, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x20, 0x00, 0xbb, 0xbb, 0xbb, 0x66, 0x66, 0x66, 0x66, 0x00, - 0x00, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x70, 0x00, 0x00, 0x00, 0x0b, - 0x22, 0x22, 0x22, 0x27, 0xb9, 0x77, 0x77, 0x77, 0x77, 0x42, 0x22, 0x47, - 0xbe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xe7, 0xbc, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcc, 0xc7, 0xba, 0xaa, 0xaa, 0xa7, 0xba, 0xaa, 0xaa, 0xa7, - 0xb8, 0x88, 0x88, 0x87, 0xb8, 0x88, 0x88, 0x87, 0xb4, 0x44, 0x44, 0x47, - 0xb4, 0x44, 0x44, 0x47, 0xb3, 0x33, 0x33, 0x37, 0xb3, 0x33, 0x33, 0x37, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x09, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x60, 0x00, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb5, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0x99, 0x99, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x09, 0x77, 0x77, 0x77, - 0x79, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x70, - 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x47, 0x70, 0x0b, 0xee, 0xee, 0xeb, - 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, - 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, - 0x66, 0x88, 0x88, 0xb0, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4b, 0x00, - 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, - 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0x66, 0x66, 0x65, 0x0b, 0xaa, 0xaa, 0xa6, - 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, 0x6b, 0x88, 0x88, 0x86, - 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x46, 0x00, 0xb3, 0x33, 0x33, - 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, - 0xdd, 0x70, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, 0x00, 0x00, - 0x0b, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x77, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x60, - 0x07, 0x77, 0x77, 0x77, 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, - 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, - 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc7, - 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, - 0x0b, 0x88, 0x88, 0x86, 0x6b, 0x88, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0xb0, 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3b, 0x00, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x76, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, - 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x0b, 0xee, 0xee, 0xeb, - 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, - 0x77, 0x77, 0x77, 0x76, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, - 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, - 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, - 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x0b, 0x42, 0x22, 0x22, - 0x22, 0x22, 0x27, 0x00, 0x0b, 0xee, 0xee, 0xeb, 0xbb, 0xbb, 0xbb, 0x00, - 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0x0b, 0x99, 0x99, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x55, 0x55, 0x57, 0x77, 0x77, 0x77, 0x77, 0x0b, 0x22, 0x22, 0x27, - 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, 0x22, 0x22, 0x22, 0x47, - 0x0b, 0xee, 0xee, 0xe6, 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, - 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, - 0x0b, 0x88, 0x88, 0x86, 0x66, 0x88, 0x88, 0xb0, 0x0b, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x4b, 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0xb0, 0x00, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, - 0x0b, 0x77, 0x60, 0x00, 0x00, 0x0b, 0xdd, 0xd7, 0x0b, 0xdd, 0xd5, 0x00, - 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0x60, 0x0b, 0x99, 0x99, 0x97, - 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x57, - 0x77, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, - 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xeb, - 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, - 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, - 0x0b, 0x88, 0x88, 0x86, 0x00, 0xb4, 0x44, 0x46, 0x0b, 0x44, 0x44, 0xb0, - 0x00, 0x0b, 0x33, 0x36, 0x0b, 0x33, 0x3b, 0x00, 0x00, 0x00, 0xbb, 0xbb, - 0x0b, 0xbb, 0xb0, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, - 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, - 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x0b, 0xbb, 0x99, 0x99, 0x9b, 0xb7, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, - 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, 0x57, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, - 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe6, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, - 0xaa, 0xa6, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x88, 0x88, 0x86, 0x66, 0x00, - 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x46, 0x00, 0x00, 0x0b, 0x33, 0x33, - 0x33, 0x33, 0x36, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x07, 0x77, 0x77, 0x77, - 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, - 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, - 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, - 0xb9, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x7b, 0x77, 0x77, 0x77, 0x70, - 0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x0b, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x00, - 0x0b, 0x42, 0x22, 0x22, 0x22, 0x47, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xee, - 0xee, 0xee, 0x60, 0x00, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, - 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x60, 0x0b, 0x88, 0x88, 0x86, - 0xb8, 0x88, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x46, 0x0b, 0x44, 0x44, 0x46, - 0x0b, 0x33, 0x33, 0x36, 0x0b, 0x33, 0x33, 0x36, 0x0b, 0xbb, 0xbb, 0xbb, - 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe6, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa6, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x86, 0x66, 0x66, 0x66, 0x52, - 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x0b, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x36, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0x0b, 0x77, 0x77, 0x70, 0x00, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, - 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, - 0x0b, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, - 0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x72, - 0x22, 0xb2, 0x22, 0x27, 0x0b, 0x42, 0x24, 0x7b, 0x5b, 0xb4, 0x22, 0x47, - 0x0b, 0xcc, 0xcc, 0x60, 0xb0, 0xbc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0x60, - 0x00, 0xba, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x60, 0x00, 0xb8, 0x88, 0x86, - 0x0b, 0x88, 0x88, 0x60, 0x00, 0xb8, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x60, - 0x00, 0xb4, 0x44, 0x46, 0x0b, 0x33, 0x33, 0x60, 0x00, 0xb3, 0x33, 0x36, - 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x60, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd5, 0x00, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0x99, 0x99, 0x96, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, - 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe7, - 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, - 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, - 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, - 0x0b, 0x33, 0x33, 0x37, 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, - 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, - 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x55, 0x55, 0x57, - 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, - 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe6, 0x0b, 0xee, 0xee, 0xe7, - 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, - 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, 0x66, 0x88, 0x88, 0x86, - 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, - 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, - 0x0b, 0x77, 0x77, 0x77, 0x77, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x24, 0xb0, - 0x0b, 0xee, 0xee, 0xeb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, - 0x77, 0x77, 0x76, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, - 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe6, - 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc6, 0xbc, 0xcc, 0xc9, 0x70, - 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0x90, 0x0b, 0x88, 0x88, 0x86, - 0x6b, 0x88, 0x88, 0x89, 0x0b, 0x44, 0x44, 0x44, 0x4b, 0x44, 0x44, 0x49, - 0x00, 0xb3, 0x33, 0x33, 0x3b, 0x33, 0x33, 0x39, 0x00, 0x0b, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb9, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, - 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, 0x7b, 0x55, 0x55, 0x57, - 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x22, - 0x22, 0x22, 0x24, 0x70, 0x0b, 0xee, 0xee, 0xe7, 0xee, 0xee, 0xe7, 0x00, - 0x0b, 0xcc, 0xcc, 0xc7, 0xbc, 0xcc, 0xc6, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, - 0x0b, 0xaa, 0xaa, 0x50, 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x88, 0x88, 0x86, - 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x33, 0x33, 0x37, - 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, - 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x00, 0xbd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, - 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x0b, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x50, 0x00, 0xb4, 0x22, 0x22, 0x22, 0x22, 0x22, 0x46, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xee, 0xee, 0xe7, 0x07, 0x77, 0x77, 0x77, - 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, - 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0x99, - 0x99, 0x9b, 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, - 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, - 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, - 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, - 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, - 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x67, - 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x55, 0x55, 0x57, - 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, - 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xee, 0xee, 0xe7, - 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, - 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, - 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, 0x00, 0xb3, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x37, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0x70, - 0x00, 0xbd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, - 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x70, - 0x00, 0xb9, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, - 0x0b, 0x66, 0x66, 0x70, 0x00, 0xb6, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x70, - 0x00, 0xb5, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x70, 0x00, 0xb2, 0x22, 0x27, - 0x0b, 0x42, 0x24, 0xb7, 0x7b, 0xb4, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xee, 0xe7, 0x00, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x70, - 0x00, 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0xb8, 0x88, - 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, - 0x00, 0xb7, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0x70, 0x00, 0xbd, 0xdd, 0xd7, - 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x70, - 0x00, 0xb9, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, - 0x0b, 0x66, 0x66, 0x70, 0x00, 0xb6, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x70, - 0x70, 0xb5, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x7b, 0x27, 0xb2, 0x22, 0x27, - 0x0b, 0x42, 0x24, 0xb2, 0x22, 0xb4, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, - 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, 0xb4, 0x44, 0x44, 0x47, - 0x0b, 0x33, 0x33, 0x3b, 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xb0, - 0x00, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, - 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, - 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x00, 0xb5, 0x55, 0x57, - 0x7b, 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, - 0x00, 0x00, 0xb4, 0x22, 0x22, 0x24, 0x70, 0x00, 0x00, 0x0b, 0x42, 0x22, - 0x22, 0x22, 0x47, 0x00, 0x00, 0xbe, 0xee, 0xe7, 0x7b, 0xee, 0xee, 0x70, - 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, - 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x88, 0x88, 0x87, - 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x33, 0x33, 0x37, - 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, - 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, - 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xb7, - 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, - 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x66, 0x66, 0x67, - 0x00, 0xb5, 0x55, 0x57, 0x7b, 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, - 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x22, 0x24, 0x70, 0x00, - 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, - 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, - 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x77, - 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, - 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x25, 0x55, 0x70, 0x00, - 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xee, - 0xee, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, - 0x00, 0xba, 0xaa, 0xaa, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, - 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, - 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xb0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xdd, - 0xdd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, - 0x99, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, - 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x24, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, - 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, - 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, - 0x77, 0x77, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0x70, 0x00, - 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0xbb, 0xbb, 0xb0, - 0x0b, 0xbb, 0xb7, 0x00, 0x00, 0xb9, 0x99, 0x90, 0x09, 0x99, 0x97, 0x00, - 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xb6, 0x66, 0x66, - 0x66, 0x66, 0x70, 0x00, 0x0b, 0x55, 0x55, 0x55, 0x55, 0x57, 0x00, 0x00, - 0xb4, 0x22, 0x22, 0x22, 0x24, 0x77, 0x77, 0x70, 0xbe, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xee, 0xe7, 0xbc, 0xcc, 0xcc, 0xec, 0xcc, 0xcc, 0xcc, 0xc7, - 0xba, 0xaa, 0xaa, 0xae, 0xaa, 0xaa, 0xaa, 0xa7, 0xb8, 0x88, 0x88, 0x88, - 0xe8, 0x88, 0x88, 0x70, 0xb4, 0x44, 0x44, 0x44, 0x4e, 0x44, 0x44, 0x47, - 0x0b, 0x33, 0x33, 0x33, 0x33, 0xe3, 0x33, 0x37, 0x00, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x07, 0x77, 0x77, 0x77, 0x70, 0x00, - 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0xbb, 0xb7, - 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, 0x00, 0xb7, 0x77, 0x70, - 0x07, 0x77, 0x77, 0x77, 0x00, 0xb7, 0x77, 0x70, 0x07, 0x77, 0x77, 0x77, - 0x00, 0xb5, 0x55, 0x55, 0x50, 0x05, 0x55, 0x57, 0x00, 0xb4, 0x22, 0x22, - 0x40, 0x04, 0x22, 0x47, 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, - 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xa0, - 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, 0x00, 0x08, 0x88, 0x87, - 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, 0x00, 0x00, 0xb3, 0x33, - 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, - 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, - 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, 0x00, 0x00, - 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x99, - 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xb7, 0x77, 0x77, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, - 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x47, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xbe, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, - 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0x00, - 0x00, 0xbb, 0xbb, 0xb8, 0x88, 0x87, 0x77, 0x77, 0x00, 0xb4, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x47, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, - 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x07, 0x77, - 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0xb9, 0x99, 0x9b, 0x00, 0xb9, 0x99, 0x97, - 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xb7, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0xb7, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xb5, 0x55, 0x57, 0x00, - 0x00, 0x00, 0x00, 0xbb, 0x42, 0x24, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xbe, - 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xcc, 0xcc, 0x70, 0x00, 0x00, - 0x00, 0x0b, 0xba, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x88, 0x88, - 0x87, 0x77, 0x77, 0x77, 0x00, 0xb4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, - 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x00, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, - 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, - 0x00, 0x00, 0x00, 0xbb, 0xb9, 0x99, 0x70, 0x00, 0x00, 0x00, 0x00, 0xb7, - 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x70, 0x00, - 0x00, 0x00, 0x00, 0xbb, 0xb5, 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb4, 0x22, 0x47, 0x70, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbe, 0xee, 0xe7, - 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xbc, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xab, - 0x00, 0xba, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x8b, 0xbb, 0xb8, 0x88, 0x87, - 0x00, 0xbb, 0xb4, 0x44, 0x44, 0x44, 0x47, 0x70, 0x00, 0x00, 0xb3, 0x33, - 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0xbd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0xbb, 0x99, - 0x99, 0x99, 0x97, 0x00, 0x00, 0x0b, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x00, - 0x00, 0xbb, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xb5, 0x55, 0x50, - 0x05, 0x55, 0x57, 0x00, 0x00, 0xb4, 0x22, 0x40, 0x04, 0x22, 0x47, 0x77, - 0x00, 0xbe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xe7, 0x00, 0xbc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcc, 0xc7, 0x00, 0xbb, 0xbb, 0xbb, 0xba, 0xaa, 0xa7, 0x77, - 0x00, 0x00, 0x00, 0x00, 0xb8, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb4, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x33, 0x37, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0xb7, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, - 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0xbb, 0xbb, 0xb7, - 0x77, 0x77, 0x77, 0x77, 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xbb, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x70, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb5, 0x55, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0xee, 0xe7, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xbc, 0xcc, 0xc7, - 0x00, 0xba, 0xaa, 0xab, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x8b, - 0xbb, 0xb8, 0x88, 0x87, 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x47, 0x70, - 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x00, - 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, - 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0xbb, 0xbb, 0xb7, 0x77, 0x77, 0x77, 0x00, - 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, - 0x00, 0xb5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x70, 0x00, 0xb4, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x47, 0x00, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, - 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xa0, - 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, 0x00, 0x08, 0x88, 0x87, - 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, 0x00, 0x00, 0xb3, 0x33, - 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, - 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, - 0x00, 0xbb, 0xbb, 0xbb, 0xb0, 0x0b, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xb5, - 0x55, 0x55, 0x77, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x70, 0x00, - 0x00, 0x00, 0xbe, 0xee, 0xee, 0x77, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xcc, - 0xc7, 0x70, 0x00, 0x00, 0x00, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xb8, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x44, - 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x33, 0x37, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x77, - 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, - 0x00, 0x0b, 0xbb, 0xb7, 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, - 0x00, 0xb7, 0x77, 0x70, 0x00, 0x07, 0x77, 0x77, 0x00, 0xbb, 0x77, 0x70, - 0x00, 0x07, 0x77, 0x77, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x57, 0x00, - 0x00, 0x0b, 0x42, 0x22, 0x22, 0x22, 0x24, 0x70, 0x00, 0xbc, 0xcc, 0xc0, - 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, - 0x00, 0xba, 0xaa, 0xa0, 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, - 0x00, 0x08, 0x88, 0x87, 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, - 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, 0x77, 0x70, 0x00, - 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0xbb, 0xb7, - 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, 0x00, 0xb7, 0x77, 0x70, - 0x00, 0x07, 0x77, 0x77, 0x00, 0xb7, 0x77, 0x70, 0x00, 0x07, 0x77, 0x77, - 0x00, 0xbb, 0x55, 0x55, 0x55, 0x55, 0x55, 0x57, 0x00, 0x0b, 0xb2, 0x22, - 0x22, 0x22, 0x22, 0x47, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbe, 0xee, 0xe7, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0xaa, 0xaa, 0x77, 0x00, 0x00, 0xbb, 0xbb, 0xb8, 0x88, 0x87, 0x70, - 0x00, 0x00, 0xb4, 0x44, 0x44, 0x44, 0x77, 0x00, 0x00, 0x00, 0xb3, 0x33, - 0x33, 0x37, 0x70, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb6, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, - 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x99, 0x96, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb9, 0x99, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, - 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0x66, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x60, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x60, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xaa, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x66, 0x60, 0x00, - 0x00, 0x00, 0x0b, 0x44, 0x44, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb9, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, - 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb5, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, - 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xb0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x7b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7d, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xbb, 0xbb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x99, 0x99, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0xb0, - 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x75, - 0x55, 0x55, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x42, 0x22, 0x4b, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0xee, 0xeb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0xcc, 0xcc, - 0xcb, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xaa, 0xaa, 0xb0, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x88, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x44, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x77, - 0x77, 0x77, 0x77, 0x70, 0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x70, - 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x24, 0x70, 0x0b, 0xbb, 0xbb, 0xee, - 0xee, 0x77, 0x77, 0x70, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, - 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x70, 0x00, - 0x00, 0xbb, 0xdd, 0xdd, 0xdd, 0xdd, 0x77, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xb7, 0x70, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, - 0x0b, 0x99, 0x99, 0xb0, 0x0b, 0x99, 0x99, 0x70, 0x0b, 0xbb, 0xbb, 0xb0, - 0xb7, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x70, - 0x00, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x70, 0x00, 0x00, 0x0b, 0x42, - 0x22, 0x22, 0x47, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xee, 0x70, 0x00, - 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, - 0x33, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0xdd, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x60, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xaa, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x88, - 0x88, 0x66, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xb0, 0x00, 0x00, - 0x07, 0x7b, 0x00, 0x00, 0x07, 0x7b, 0x00, 0x00, 0x07, 0x55, 0xb0, 0x00, - 0x07, 0x55, 0xb0, 0x00, 0x07, 0x22, 0x2b, 0x00, 0x07, 0x22, 0x2b, 0x00, - 0x07, 0xee, 0xee, 0xb0, 0x07, 0xee, 0xee, 0xb0, 0x00, 0x7c, 0xcc, 0xb0, - 0x00, 0x7c, 0xcc, 0xb0, 0x00, 0x07, 0xaa, 0xb0, 0x00, 0x07, 0xaa, 0xb0, - 0x00, 0x00, 0xbb, 0xb0, 0x00, 0x00, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x70, 0x0b, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x70, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x70, - 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xdd, - 0xdd, 0xdd, 0x70, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, - 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, - 0x99, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, - 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x24, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, - 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xaa, 0x70, 0x00, 0x00, - 0x00, 0x0b, 0x88, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x44, - 0x44, 0x44, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x33, 0x70, 0x00, - 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x77, - 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x07, 0xdd, 0xdd, 0xdd, 0xb0, 0x00, - 0x00, 0x00, 0x07, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x7b, - 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x99, 0x99, 0x99, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x77, 0x77, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x55, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x07, 0xee, 0xee, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0xb0, - 0x00, 0x00, 0x00, 0x07, 0xaa, 0xaa, 0xaa, 0xb0, 0x00, 0x00, 0x00, 0x78, - 0x88, 0x88, 0x88, 0xb0, 0x00, 0x00, 0x07, 0x44, 0x44, 0x44, 0x4b, 0x00, - 0x00, 0x00, 0x07, 0x33, 0x33, 0x33, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0xbb, - 0xbb, 0xbb, 0x00, 0x00 -}; + 0xff, 0xff, 0xff, 0xff, 0xc7, 0xe4, 0xeb, 0xa3, 0xc0, 0xef, 0x82, 0x65, + 0xcc, 0x6a, 0x50, 0xc8, 0x8c, 0x99, 0xa6, 0x78, 0x76, 0x8d, 0x57, 0x66, + 0xa5, 0x3f, 0x52, 0x7d, 0x2d, 0x66, 0x91, 0x19, 0x46, 0x72, 0x00, 0x5e, + 0x72, 0x00, 0x28, 0x60, 0x00, 0x58, 0x41, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x97, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, + 0xd7, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x60, 0x00, 0x00, + 0xb9, 0x99, 0x99, 0x99, 0x99, 0x92, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x20, 0x00, 0xbb, 0xbb, 0xbb, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x70, 0x00, 0x00, 0x00, 0x0b, + 0x22, 0x22, 0x22, 0x27, 0xb9, 0x77, 0x77, 0x77, 0x77, 0x42, 0x22, 0x47, + 0xbe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xe7, 0xbc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xc7, 0xba, 0xaa, 0xaa, 0xa7, 0xba, 0xaa, 0xaa, 0xa7, + 0xb8, 0x88, 0x88, 0x87, 0xb8, 0x88, 0x88, 0x87, 0xb4, 0x44, 0x44, 0x47, + 0xb4, 0x44, 0x44, 0x47, 0xb3, 0x33, 0x33, 0x37, 0xb3, 0x33, 0x33, 0x37, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x09, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x60, 0x00, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb5, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0x99, 0x99, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x09, 0x77, 0x77, 0x77, + 0x79, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x70, + 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x47, 0x70, 0x0b, 0xee, 0xee, 0xeb, + 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, + 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, + 0x66, 0x88, 0x88, 0xb0, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4b, 0x00, + 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, + 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0x66, 0x66, 0x65, 0x0b, 0xaa, 0xaa, 0xa6, + 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, 0x6b, 0x88, 0x88, 0x86, + 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x46, 0x00, 0xb3, 0x33, 0x33, + 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, + 0xdd, 0x70, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, 0x00, 0x00, + 0x0b, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x77, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x60, + 0x07, 0x77, 0x77, 0x77, 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, + 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, + 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc7, + 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, + 0x0b, 0x88, 0x88, 0x86, 0x6b, 0x88, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0xb0, 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3b, 0x00, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x76, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, + 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x0b, 0xee, 0xee, 0xeb, + 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, + 0x77, 0x77, 0x77, 0x76, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, + 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, + 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x0b, 0x42, 0x22, 0x22, + 0x22, 0x22, 0x27, 0x00, 0x0b, 0xee, 0xee, 0xeb, 0xbb, 0xbb, 0xbb, 0x00, + 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0x0b, 0x99, 0x99, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x55, 0x55, 0x57, 0x77, 0x77, 0x77, 0x77, 0x0b, 0x22, 0x22, 0x27, + 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, 0x22, 0x22, 0x22, 0x47, + 0x0b, 0xee, 0xee, 0xe6, 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, + 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, + 0x0b, 0x88, 0x88, 0x86, 0x66, 0x88, 0x88, 0xb0, 0x0b, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x4b, 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0xb0, 0x00, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, + 0x0b, 0x77, 0x60, 0x00, 0x00, 0x0b, 0xdd, 0xd7, 0x0b, 0xdd, 0xd5, 0x00, + 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0x60, 0x0b, 0x99, 0x99, 0x97, + 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x57, + 0x77, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, + 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xeb, + 0xbb, 0xee, 0xee, 0xe6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, + 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, + 0x0b, 0x88, 0x88, 0x86, 0x00, 0xb4, 0x44, 0x46, 0x0b, 0x44, 0x44, 0xb0, + 0x00, 0x0b, 0x33, 0x36, 0x0b, 0x33, 0x3b, 0x00, 0x00, 0x00, 0xbb, 0xbb, + 0x0b, 0xbb, 0xb0, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, + 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, + 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x0b, 0xbb, 0x99, 0x99, 0x9b, 0xb7, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, + 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, 0x57, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, + 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe6, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, + 0xaa, 0xa6, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x88, 0x88, 0x86, 0x66, 0x00, + 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x46, 0x00, 0x00, 0x0b, 0x33, 0x33, + 0x33, 0x33, 0x36, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x07, 0x77, 0x77, 0x77, + 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, + 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, + 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, + 0xb9, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x7b, 0x77, 0x77, 0x77, 0x70, + 0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x0b, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, 0x00, + 0x0b, 0x42, 0x22, 0x22, 0x22, 0x47, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xee, + 0xee, 0xee, 0x60, 0x00, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, + 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x60, 0x0b, 0x88, 0x88, 0x86, + 0xb8, 0x88, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x46, 0x0b, 0x44, 0x44, 0x46, + 0x0b, 0x33, 0x33, 0x36, 0x0b, 0x33, 0x33, 0x36, 0x0b, 0xbb, 0xbb, 0xbb, + 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa6, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x86, 0x66, 0x66, 0x66, 0x52, + 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x0b, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x36, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0x0b, 0x77, 0x77, 0x70, 0x00, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, + 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, + 0x0b, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, + 0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x72, + 0x22, 0xb2, 0x22, 0x27, 0x0b, 0x42, 0x24, 0x7b, 0x5b, 0xb4, 0x22, 0x47, + 0x0b, 0xcc, 0xcc, 0x60, 0xb0, 0xbc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0x60, + 0x00, 0xba, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x60, 0x00, 0xb8, 0x88, 0x86, + 0x0b, 0x88, 0x88, 0x60, 0x00, 0xb8, 0x88, 0x86, 0x0b, 0x44, 0x44, 0x60, + 0x00, 0xb4, 0x44, 0x46, 0x0b, 0x33, 0x33, 0x60, 0x00, 0xb3, 0x33, 0x36, + 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x60, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd5, 0x00, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0x99, 0x99, 0x96, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, + 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe7, + 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, + 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, + 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, + 0x0b, 0x33, 0x33, 0x37, 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, + 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, + 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x55, 0x55, 0x57, + 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, + 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe6, 0x0b, 0xee, 0xee, 0xe7, + 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xcc, 0xcc, 0xc6, 0x0b, 0xaa, 0xaa, 0xa6, + 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0x88, 0x88, 0x86, 0x66, 0x88, 0x88, 0x86, + 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, + 0x33, 0x33, 0x33, 0xb0, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, + 0x0b, 0x77, 0x77, 0x77, 0x77, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x24, 0xb0, + 0x0b, 0xee, 0xee, 0xeb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, + 0x77, 0x77, 0x76, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, + 0x0b, 0x42, 0x22, 0x47, 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe6, + 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xc6, 0xbc, 0xcc, 0xc9, 0x70, + 0x0b, 0xaa, 0xaa, 0xa6, 0x0b, 0xaa, 0xaa, 0x90, 0x0b, 0x88, 0x88, 0x86, + 0x6b, 0x88, 0x88, 0x89, 0x0b, 0x44, 0x44, 0x44, 0x4b, 0x44, 0x44, 0x49, + 0x00, 0xb3, 0x33, 0x33, 0x3b, 0x33, 0x33, 0x39, 0x00, 0x0b, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb9, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, + 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb6, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x77, 0x77, 0x77, 0x7b, 0x55, 0x55, 0x57, + 0x0b, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x22, + 0x22, 0x22, 0x24, 0x70, 0x0b, 0xee, 0xee, 0xe7, 0xee, 0xee, 0xe7, 0x00, + 0x0b, 0xcc, 0xcc, 0xc7, 0xbc, 0xcc, 0xc6, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, + 0x0b, 0xaa, 0xaa, 0x50, 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x88, 0x88, 0x86, + 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x33, 0x33, 0x37, + 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, + 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x00, 0xbd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0x50, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb6, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, + 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x76, 0x00, 0x0b, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x50, 0x00, 0xb4, 0x22, 0x22, 0x22, 0x22, 0x22, 0x46, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xee, 0xee, 0xe7, 0x07, 0x77, 0x77, 0x77, + 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0xaa, 0xaa, 0xa7, + 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x4b, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0xb0, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0x99, + 0x99, 0x9b, 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, + 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x22, 0x22, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, + 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, + 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, + 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, + 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x67, + 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x57, 0x0b, 0x55, 0x55, 0x57, + 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x22, 0x22, 0x27, 0x0b, 0x42, 0x22, 0x47, + 0x0b, 0x42, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xe7, 0x0b, 0xee, 0xee, 0xe7, + 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, + 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, 0x77, 0x88, 0x88, 0x87, + 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, 0x00, 0xb3, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x37, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0x70, + 0x00, 0xbd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, + 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x70, + 0x00, 0xb9, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, + 0x0b, 0x66, 0x66, 0x70, 0x00, 0xb6, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x70, + 0x00, 0xb5, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x70, 0x00, 0xb2, 0x22, 0x27, + 0x0b, 0x42, 0x24, 0xb7, 0x7b, 0xb4, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xe7, 0x00, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x70, + 0x00, 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0xb8, 0x88, + 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, + 0x00, 0xb7, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0x70, 0x00, 0xbd, 0xdd, 0xd7, + 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x70, + 0x00, 0xb9, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x70, 0x00, 0xb7, 0x77, 0x77, + 0x0b, 0x66, 0x66, 0x70, 0x00, 0xb6, 0x66, 0x67, 0x0b, 0x55, 0x55, 0x70, + 0x70, 0xb5, 0x55, 0x57, 0x0b, 0x22, 0x22, 0x7b, 0x27, 0xb2, 0x22, 0x27, + 0x0b, 0x42, 0x24, 0xb2, 0x22, 0xb4, 0x22, 0x47, 0x0b, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xe7, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, + 0x0b, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, 0xb4, 0x44, 0x44, 0x47, + 0x0b, 0x33, 0x33, 0x3b, 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xb0, + 0x00, 0xbb, 0xbb, 0xbb, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, + 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, + 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x00, 0xb5, 0x55, 0x57, + 0x7b, 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, 0x22, 0x22, 0x27, 0x00, + 0x00, 0x00, 0xb4, 0x22, 0x22, 0x24, 0x70, 0x00, 0x00, 0x0b, 0x42, 0x22, + 0x22, 0x22, 0x47, 0x00, 0x00, 0xbe, 0xee, 0xe7, 0x7b, 0xee, 0xee, 0x70, + 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xcc, 0xcc, 0xc7, 0x0b, 0xaa, 0xaa, 0xa7, + 0x0b, 0xaa, 0xaa, 0xa7, 0x0b, 0x88, 0x88, 0x87, 0x0b, 0x88, 0x88, 0x87, + 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x44, 0x44, 0x47, 0x0b, 0x33, 0x33, 0x37, + 0x0b, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, 0x0b, 0xbb, 0xbb, 0xbb, + 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xd7, + 0x0b, 0xdd, 0xdd, 0xd7, 0x0b, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xb7, + 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x99, 0x99, 0x97, 0x0b, 0x77, 0x77, 0x77, + 0x0b, 0x77, 0x77, 0x77, 0x0b, 0x66, 0x66, 0x67, 0x0b, 0x66, 0x66, 0x67, + 0x00, 0xb5, 0x55, 0x57, 0x7b, 0x55, 0x55, 0x70, 0x00, 0x0b, 0x22, 0x22, + 0x22, 0x22, 0x27, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x22, 0x24, 0x70, 0x00, + 0x00, 0x00, 0x0b, 0xee, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, + 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xa7, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x44, + 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x77, + 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, + 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x25, 0x55, 0x70, 0x00, + 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xee, + 0xee, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, + 0x00, 0xba, 0xaa, 0xaa, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x87, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, + 0x0b, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, + 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xb0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xdd, + 0xdd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, + 0x99, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, + 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x24, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, + 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, + 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, + 0x77, 0x77, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xdd, 0xdd, 0xdd, 0x70, 0x00, + 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0xbb, 0xbb, 0xb0, + 0x0b, 0xbb, 0xb7, 0x00, 0x00, 0xb9, 0x99, 0x90, 0x09, 0x99, 0x97, 0x00, + 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xb6, 0x66, 0x66, + 0x66, 0x66, 0x70, 0x00, 0x0b, 0x55, 0x55, 0x55, 0x55, 0x57, 0x00, 0x00, + 0xb4, 0x22, 0x22, 0x22, 0x24, 0x77, 0x77, 0x70, 0xbe, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xe7, 0xbc, 0xcc, 0xcc, 0xec, 0xcc, 0xcc, 0xcc, 0xc7, + 0xba, 0xaa, 0xaa, 0xae, 0xaa, 0xaa, 0xaa, 0xa7, 0xb8, 0x88, 0x88, 0x88, + 0xe8, 0x88, 0x88, 0x70, 0xb4, 0x44, 0x44, 0x44, 0x4e, 0x44, 0x44, 0x47, + 0x0b, 0x33, 0x33, 0x33, 0x33, 0xe3, 0x33, 0x37, 0x00, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x07, 0x77, 0x77, 0x77, 0x70, 0x00, + 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0xbb, 0xb7, + 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, 0x00, 0xb7, 0x77, 0x70, + 0x07, 0x77, 0x77, 0x77, 0x00, 0xb7, 0x77, 0x70, 0x07, 0x77, 0x77, 0x77, + 0x00, 0xb5, 0x55, 0x55, 0x50, 0x05, 0x55, 0x57, 0x00, 0xb4, 0x22, 0x22, + 0x40, 0x04, 0x22, 0x47, 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, + 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xa0, + 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, 0x00, 0x08, 0x88, 0x87, + 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, 0x00, 0x00, 0xb3, 0x33, + 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, + 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb7, 0x00, 0x00, + 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x99, + 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xb7, 0x77, 0x77, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, + 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xbe, 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, + 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0x00, + 0x00, 0xbb, 0xbb, 0xb8, 0x88, 0x87, 0x77, 0x77, 0x00, 0xb4, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x47, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, + 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x07, 0x77, + 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0xb9, 0x99, 0x9b, 0x00, 0xb9, 0x99, 0x97, + 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xb7, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0xb7, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xb5, 0x55, 0x57, 0x00, + 0x00, 0x00, 0x00, 0xbb, 0x42, 0x24, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xbe, + 0xee, 0xe7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xcc, 0xcc, 0x70, 0x00, 0x00, + 0x00, 0x0b, 0xba, 0xaa, 0xa7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x88, 0x88, + 0x87, 0x77, 0x77, 0x77, 0x00, 0xb4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x47, + 0x00, 0xb3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x37, 0x00, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, + 0x00, 0x00, 0x00, 0xbb, 0xb9, 0x99, 0x70, 0x00, 0x00, 0x00, 0x00, 0xb7, + 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x70, 0x00, + 0x00, 0x00, 0x00, 0xbb, 0xb5, 0x55, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb4, 0x22, 0x47, 0x70, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbe, 0xee, 0xe7, + 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xbc, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xab, + 0x00, 0xba, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x8b, 0xbb, 0xb8, 0x88, 0x87, + 0x00, 0xbb, 0xb4, 0x44, 0x44, 0x44, 0x47, 0x70, 0x00, 0x00, 0xb3, 0x33, + 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0xbd, 0xdd, 0xd7, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0xbb, 0x99, + 0x99, 0x99, 0x97, 0x00, 0x00, 0x0b, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x00, + 0x00, 0xbb, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xb5, 0x55, 0x50, + 0x05, 0x55, 0x57, 0x00, 0x00, 0xb4, 0x22, 0x40, 0x04, 0x22, 0x47, 0x77, + 0x00, 0xbe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xe7, 0x00, 0xbc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xc7, 0x00, 0xbb, 0xbb, 0xbb, 0xba, 0xaa, 0xa7, 0x77, + 0x00, 0x00, 0x00, 0x00, 0xb8, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb4, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x33, 0x37, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0xb7, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd7, + 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0xbb, 0xbb, 0xb7, + 0x77, 0x77, 0x77, 0x77, 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0xbb, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x70, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb5, 0x55, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x22, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0xee, 0xe7, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xbc, 0xcc, 0xc7, + 0x00, 0xba, 0xaa, 0xab, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x8b, + 0xbb, 0xb8, 0x88, 0x87, 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x47, 0x70, + 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x00, + 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, + 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0xbb, 0xbb, 0xb7, 0x77, 0x77, 0x77, 0x00, + 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, + 0x00, 0xb5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x70, 0x00, 0xb4, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x47, 0x00, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, + 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xba, 0xaa, 0xa0, + 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, 0x00, 0x08, 0x88, 0x87, + 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, 0x00, 0x00, 0xb3, 0x33, + 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, + 0x00, 0xb7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0xbd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7, + 0x00, 0xbb, 0xbb, 0xbb, 0xb0, 0x0b, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x99, 0x97, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xb5, + 0x55, 0x55, 0x77, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x22, 0x47, 0x70, 0x00, + 0x00, 0x00, 0xbe, 0xee, 0xee, 0x77, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xcc, + 0xc7, 0x70, 0x00, 0x00, 0x00, 0x00, 0xba, 0xaa, 0xa7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xb8, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x44, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x33, 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x77, + 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, + 0x00, 0x0b, 0xbb, 0xb7, 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, + 0x00, 0xb7, 0x77, 0x70, 0x00, 0x07, 0x77, 0x77, 0x00, 0xbb, 0x77, 0x70, + 0x00, 0x07, 0x77, 0x77, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x57, 0x00, + 0x00, 0x0b, 0x42, 0x22, 0x22, 0x22, 0x24, 0x70, 0x00, 0xbc, 0xcc, 0xc0, + 0x00, 0x0c, 0xcc, 0xc7, 0x00, 0xbc, 0xcc, 0xc0, 0x00, 0x0c, 0xcc, 0xc7, + 0x00, 0xba, 0xaa, 0xa0, 0x00, 0x0a, 0xaa, 0xa7, 0x00, 0xb8, 0x88, 0x80, + 0x00, 0x08, 0x88, 0x87, 0x00, 0x0b, 0x44, 0x44, 0x44, 0x44, 0x44, 0x70, + 0x00, 0x00, 0xb3, 0x33, 0x33, 0x33, 0x37, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, 0x77, 0x70, 0x00, + 0x00, 0x00, 0xbd, 0xdd, 0xdd, 0xdd, 0xd7, 0x00, 0x00, 0x0b, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0x70, 0x00, 0xbb, 0xbb, 0xb0, 0x00, 0x0b, 0xbb, 0xb7, + 0x00, 0xb9, 0x99, 0x90, 0x00, 0x09, 0x99, 0x97, 0x00, 0xb7, 0x77, 0x70, + 0x00, 0x07, 0x77, 0x77, 0x00, 0xb7, 0x77, 0x70, 0x00, 0x07, 0x77, 0x77, + 0x00, 0xbb, 0x55, 0x55, 0x55, 0x55, 0x55, 0x57, 0x00, 0x0b, 0xb2, 0x22, + 0x22, 0x22, 0x22, 0x47, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbe, 0xee, 0xe7, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0xaa, 0xaa, 0x77, 0x00, 0x00, 0xbb, 0xbb, 0xb8, 0x88, 0x87, 0x70, + 0x00, 0x00, 0xb4, 0x44, 0x44, 0x44, 0x77, 0x00, 0x00, 0x00, 0xb3, 0x33, + 0x33, 0x37, 0x70, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb6, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, + 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x99, 0x96, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb9, 0x99, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, + 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x66, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x60, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xaa, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x88, 0x88, 0x66, 0x60, 0x00, + 0x00, 0x00, 0x0b, 0x44, 0x44, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb9, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, + 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb5, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, + 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xb0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x7b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7d, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xbb, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x99, 0x99, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0xb0, + 0x00, 0x00, 0x00, 0x07, 0x77, 0x77, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x55, 0x55, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x42, 0x22, 0x4b, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xee, 0xeb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0xcc, 0xcc, + 0xcb, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xaa, 0xaa, 0xb0, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x88, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x44, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x77, + 0x77, 0x77, 0x77, 0x70, 0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x70, + 0x0b, 0x42, 0x22, 0x22, 0x22, 0x22, 0x24, 0x70, 0x0b, 0xbb, 0xbb, 0xee, + 0xee, 0x77, 0x77, 0x70, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, + 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x77, 0x70, 0x00, + 0x00, 0xbb, 0xdd, 0xdd, 0xdd, 0xdd, 0x77, 0x00, 0x0b, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xb7, 0x70, 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, + 0x0b, 0x99, 0x99, 0xb0, 0x0b, 0x99, 0x99, 0x70, 0x0b, 0xbb, 0xbb, 0xb0, + 0xb7, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x70, + 0x00, 0x00, 0x00, 0xb5, 0x55, 0x55, 0x55, 0x70, 0x00, 0x00, 0x0b, 0x42, + 0x22, 0x22, 0x47, 0x00, 0x00, 0x00, 0x0b, 0xee, 0xee, 0xee, 0x70, 0x00, + 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, + 0x33, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0xb0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x66, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0xdd, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, 0x60, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xaa, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x88, + 0x88, 0x66, 0x60, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x44, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0xbb, 0xbb, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xb0, 0x00, 0x00, + 0x07, 0x7b, 0x00, 0x00, 0x07, 0x7b, 0x00, 0x00, 0x07, 0x55, 0xb0, 0x00, + 0x07, 0x55, 0xb0, 0x00, 0x07, 0x22, 0x2b, 0x00, 0x07, 0x22, 0x2b, 0x00, + 0x07, 0xee, 0xee, 0xb0, 0x07, 0xee, 0xee, 0xb0, 0x00, 0x7c, 0xcc, 0xb0, + 0x00, 0x7c, 0xcc, 0xb0, 0x00, 0x07, 0xaa, 0xb0, 0x00, 0x07, 0xaa, 0xb0, + 0x00, 0x00, 0xbb, 0xb0, 0x00, 0x00, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x70, 0x0b, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x70, 0x0b, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x70, + 0x0b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x0b, 0xdd, + 0xdd, 0xdd, 0x70, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xbb, 0x70, 0x00, + 0x00, 0x0b, 0xbb, 0xbb, 0xbb, 0xb7, 0x00, 0x00, 0x00, 0x0b, 0x99, 0x99, + 0x99, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x77, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x55, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x42, 0x24, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0xee, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xcc, 0xcc, + 0xc7, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0xaa, 0xaa, 0x70, 0x00, 0x00, + 0x00, 0x0b, 0x88, 0x88, 0x88, 0x87, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x44, + 0x44, 0x44, 0x70, 0x00, 0x00, 0x00, 0x0b, 0x33, 0x33, 0x33, 0x70, 0x00, + 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x77, + 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x07, 0xdd, 0xdd, 0xdd, 0xb0, 0x00, + 0x00, 0x00, 0x07, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x7b, + 0xbb, 0xbb, 0xbb, 0xb0, 0x00, 0x00, 0x00, 0x07, 0x99, 0x99, 0x99, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x77, 0x77, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x55, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xee, 0xee, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0xb0, + 0x00, 0x00, 0x00, 0x07, 0xaa, 0xaa, 0xaa, 0xb0, 0x00, 0x00, 0x00, 0x78, + 0x88, 0x88, 0x88, 0xb0, 0x00, 0x00, 0x07, 0x44, 0x44, 0x44, 0x4b, 0x00, + 0x00, 0x00, 0x07, 0x33, 0x33, 0x33, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0xbb, + 0xbb, 0xbb, 0x00, 0x00}; unsigned int demofont_bin_len = 6448; diff --git a/firmware/application/dirty_registers.hpp b/firmware/application/dirty_registers.hpp index e2143ab29..c649c9e27 100644 --- a/firmware/application/dirty_registers.hpp +++ b/firmware/application/dirty_registers.hpp @@ -27,38 +27,38 @@ #include "utility.hpp" -template +template class DirtyRegisters { -public: - using mask_t = std::bitset; + public: + using mask_t = std::bitset; - /* TODO: I feel like I might regret implementing this cast operator... */ - operator bool() const { - return mask.any(); - } + /* TODO: I feel like I might regret implementing this cast operator... */ + operator bool() const { + return mask.any(); + } - void set() { - mask.set(); - } + void set() { + mask.set(); + } - void clear() { - mask.reset(); - } + void clear() { + mask.reset(); + } - void clear(const size_t reg_num) { - mask.reset(reg_num); - } + void clear(const size_t reg_num) { + mask.reset(reg_num); + } - typename mask_t::reference operator[](const size_t reg_num) { - return mask[reg_num]; - } + typename mask_t::reference operator[](const size_t reg_num) { + return mask[reg_num]; + } - typename mask_t::reference operator[](const RegisterType reg) { - return mask[toUType(reg)]; - } + typename mask_t::reference operator[](const RegisterType reg) { + return mask[toUType(reg)]; + } -private: - mask_t mask { }; + private: + mask_t mask{}; }; -#endif/*__DIRTY_REGISTERS_H__*/ +#endif /*__DIRTY_REGISTERS_H__*/ diff --git a/firmware/application/emu_cc1101.cpp b/firmware/application/emu_cc1101.cpp index 090894823..de946e8ba 100644 --- a/firmware/application/emu_cc1101.cpp +++ b/firmware/application/emu_cc1101.cpp @@ -25,22 +25,22 @@ namespace cc1101 { void CC1101Emu::whitening_init() { - whitening_pn = 0x1FF; + whitening_pn = 0x1FF; } // See TI app note DN509 uint8_t CC1101Emu::whiten_byte(uint8_t byte) { - uint_fast8_t new_bit; - - byte ^= (whitening_pn & 0xFF); - - for (size_t step = 0; step < 8; step++) { - new_bit = (whitening_pn & 1) ^ ((whitening_pn >> 5) & 1); - whitening_pn >>= 1; - whitening_pn |= (new_bit << 8); - } - - return byte; + uint_fast8_t new_bit; + + byte ^= (whitening_pn & 0xFF); + + for (size_t step = 0; step < 8; step++) { + new_bit = (whitening_pn & 1) ^ ((whitening_pn >> 5) & 1); + whitening_pn >>= 1; + whitening_pn |= (new_bit << 8); + } + + return byte; } } /* namespace cc1101 */ diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index 146492b93..e6b52cced 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -48,47 +48,46 @@ using namespace lpc43xx; extern "C" { CH_IRQ_HANDLER(M4Core_IRQHandler) { - CH_IRQ_PROLOGUE(); + CH_IRQ_PROLOGUE(); - chSysLockFromIsr(); - BufferExchange::handle_isr(); - EventDispatcher::check_fifo_isr(); - chSysUnlockFromIsr(); + chSysLockFromIsr(); + BufferExchange::handle_isr(); + EventDispatcher::check_fifo_isr(); + chSysUnlockFromIsr(); - creg::m4txevent::clear(); + creg::m4txevent::clear(); - CH_IRQ_EPILOGUE(); + CH_IRQ_EPILOGUE(); } - } class MessageHandlerMap { -public: - using MessageHandler = std::function; - - void register_handler(const Message::ID id, MessageHandler&& handler) { - if( map_[toUType(id)] != nullptr ) { - chDbgPanic("MsgDblReg"); - } - map_[toUType(id)] = std::move(handler); - } - - void unregister_handler(const Message::ID id) { - map_[toUType(id)] = nullptr; - } - - void send(Message* const message) { - if( message->id < Message::ID::MAX ) { - auto& fn = map_[toUType(message->id)]; - if( fn ) { - fn(message); - } - } - } - -private: - using MapType = std::array; - MapType map_ { }; + public: + using MessageHandler = std::function; + + void register_handler(const Message::ID id, MessageHandler&& handler) { + if (map_[toUType(id)] != nullptr) { + chDbgPanic("MsgDblReg"); + } + map_[toUType(id)] = std::move(handler); + } + + void unregister_handler(const Message::ID id) { + map_[toUType(id)] = nullptr; + } + + void send(Message* const message) { + if (message->id < Message::ID::MAX) { + auto& fn = map_[toUType(message->id)]; + if (fn) { + fn(message); + } + } + } + + private: + using MapType = std::array; + MapType map_{}; }; static MessageHandlerMap message_map; @@ -97,280 +96,276 @@ bool EventDispatcher::is_running = false; bool EventDispatcher::display_sleep = false; EventDispatcher::EventDispatcher( - ui::Widget* const top_widget, - ui::Context& context -) : top_widget { top_widget }, - painter { }, - context(context) -{ - init_message_queues(); - - thread_event_loop = chThdSelf(); - is_running = true; - touch_manager.on_event = [this](const ui::TouchEvent event) { - this->on_touch_event(event); - }; + ui::Widget* const top_widget, + ui::Context& context) + : top_widget{top_widget}, + painter{}, + context(context) { + init_message_queues(); + + thread_event_loop = chThdSelf(); + is_running = true; + touch_manager.on_event = [this](const ui::TouchEvent event) { + this->on_touch_event(event); + }; } void EventDispatcher::run() { - while(is_running) { - const auto events = wait(); - dispatch(events); - } + while (is_running) { + const auto events = wait(); + dispatch(events); + } } void EventDispatcher::request_stop() { - is_running = false; + is_running = false; } void EventDispatcher::set_display_sleep(const bool sleep) { - // TODO: Distribute display sleep message more broadly, shut down data generation - // on baseband side, since all that data is being discarded during sleep. - if( sleep ) { - portapack::backlight()->off(); - portapack::display.sleep(); - } else { - portapack::display.wake(); - // Don't turn on backlight here. - // Let frame sync handler turn on backlight after repaint. - } - EventDispatcher::display_sleep = sleep; + // TODO: Distribute display sleep message more broadly, shut down data generation + // on baseband side, since all that data is being discarded during sleep. + if (sleep) { + portapack::backlight()->off(); + portapack::display.sleep(); + } else { + portapack::display.wake(); + // Don't turn on backlight here. + // Let frame sync handler turn on backlight after repaint. + } + EventDispatcher::display_sleep = sleep; }; eventmask_t EventDispatcher::wait() { - return chEvtWaitAny(ALL_EVENTS); + return chEvtWaitAny(ALL_EVENTS); } void EventDispatcher::dispatch(const eventmask_t events) { - if( shared_memory.m4_panic_msg[0] != 0 ) { - if (shared_memory.bb_data.data[0] == 0) - draw_guru_meditation(CORTEX_M4, shared_memory.m4_panic_msg); - else - draw_guru_meditation( - CORTEX_M4, - shared_memory.m4_panic_msg, - (struct extctx *)&shared_memory.bb_data.data[8], - *(uint32_t *)&shared_memory.bb_data.data[4]); - } - - if( events & EVT_MASK_APPLICATION ) { - handle_application_queue(); - } - - if( events & EVT_MASK_LOCAL ) { - handle_local_queue(); - } - - if( events & EVT_MASK_RTC_TICK ) { - handle_rtc_tick(); - } - - if( events & EVT_MASK_SWITCHES ) { - handle_switches(); - } - - /*if( events & EVT_MASK_LCD_FRAME_SYNC ) { - blink_timer(); - }*/ - - if( !EventDispatcher::display_sleep ) { - if( events & EVT_MASK_LCD_FRAME_SYNC ) { - handle_lcd_frame_sync(); - } - - if( events & EVT_MASK_ENCODER ) { - handle_encoder(); - } - - if( events & EVT_MASK_TOUCH ) { - handle_touch(); - } - } + if (shared_memory.m4_panic_msg[0] != 0) { + if (shared_memory.bb_data.data[0] == 0) + draw_guru_meditation(CORTEX_M4, shared_memory.m4_panic_msg); + else + draw_guru_meditation( + CORTEX_M4, + shared_memory.m4_panic_msg, + (struct extctx*)&shared_memory.bb_data.data[8], + *(uint32_t*)&shared_memory.bb_data.data[4]); + } + + if (events & EVT_MASK_APPLICATION) { + handle_application_queue(); + } + + if (events & EVT_MASK_LOCAL) { + handle_local_queue(); + } + + if (events & EVT_MASK_RTC_TICK) { + handle_rtc_tick(); + } + + if (events & EVT_MASK_SWITCHES) { + handle_switches(); + } + + /*if( events & EVT_MASK_LCD_FRAME_SYNC ) { + blink_timer(); + }*/ + + if (!EventDispatcher::display_sleep) { + if (events & EVT_MASK_LCD_FRAME_SYNC) { + handle_lcd_frame_sync(); + } + + if (events & EVT_MASK_ENCODER) { + handle_encoder(); + } + + if (events & EVT_MASK_TOUCH) { + handle_touch(); + } + } } void EventDispatcher::handle_application_queue() { - shared_memory.application_queue.handle([](Message* const message) { - message_map.send(message); - }); + shared_memory.application_queue.handle([](Message* const message) { + message_map.send(message); + }); } void EventDispatcher::handle_local_queue() { - shared_memory.app_local_queue.handle([](Message* const message) { - message_map.send(message); - }); + shared_memory.app_local_queue.handle([](Message* const message) { + message_map.send(message); + }); } void EventDispatcher::handle_rtc_tick() { - sd_card::poll_inserted(); + sd_card::poll_inserted(); + + portapack::temperature_logger.second_tick(); - portapack::temperature_logger.second_tick(); - - const auto backlight_timer = portapack::persistent_memory::config_backlight_timer(); - if (backlight_timer.timeout_enabled()) { - if (portapack::bl_tick_counter == backlight_timer.timeout_seconds()) - set_display_sleep(true); - else - portapack::bl_tick_counter++; - } + const auto backlight_timer = portapack::persistent_memory::config_backlight_timer(); + if (backlight_timer.timeout_enabled()) { + if (portapack::bl_tick_counter == backlight_timer.timeout_seconds()) + set_display_sleep(true); + else + portapack::bl_tick_counter++; + } - rtc_time::on_tick_second(); + rtc_time::on_tick_second(); - portapack::persistent_memory::cache::persist(); + portapack::persistent_memory::cache::persist(); } ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) { - if( !w->hidden() ) { - // To achieve reverse depth ordering (last object drawn is - // considered "top"), descend first. - for(const auto child : w->children()) { - const auto touched_widget = touch_widget(child, event); - if( touched_widget ) { - return touched_widget; - } - } - - const auto r = w->screen_rect(); - if( r.contains(event.point) ) { - if( w->on_touch(event) ) { - // This widget responded. Return it up the call stack. - return w; - } - } - } - return nullptr; + if (!w->hidden()) { + // To achieve reverse depth ordering (last object drawn is + // considered "top"), descend first. + for (const auto child : w->children()) { + const auto touched_widget = touch_widget(child, event); + if (touched_widget) { + return touched_widget; + } + } + + const auto r = w->screen_rect(); + if (r.contains(event.point)) { + if (w->on_touch(event)) { + // This widget responded. Return it up the call stack. + return w; + } + } + } + return nullptr; } void EventDispatcher::on_touch_event(ui::TouchEvent event) { - /* TODO: Capture widget receiving the Start event, send Move and - * End events to the same widget. - */ - /* Capture Start widget. - * If touch is over Start widget at Move event, then the widget - * should be highlighted. If the touch is not over the Start - * widget at Move event, widget should un-highlight. - * If touch is over Start widget at End event, then the widget - * action should occur. - */ - if( event.type == ui::TouchEvent::Type::Start ) { - captured_widget = touch_widget(this->top_widget, event); - } - - if( captured_widget ) { - captured_widget->on_touch(event); - } + /* TODO: Capture widget receiving the Start event, send Move and + * End events to the same widget. + */ + /* Capture Start widget. + * If touch is over Start widget at Move event, then the widget + * should be highlighted. If the touch is not over the Start + * widget at Move event, widget should un-highlight. + * If touch is over Start widget at End event, then the widget + * action should occur. + */ + if (event.type == ui::TouchEvent::Type::Start) { + captured_widget = touch_widget(this->top_widget, event); + } + + if (captured_widget) { + captured_widget->on_touch(event); + } } void EventDispatcher::handle_lcd_frame_sync() { - DisplayFrameSyncMessage message; - message_map.send(&message); + DisplayFrameSyncMessage message; + message_map.send(&message); - static_cast(top_widget)->paint_overlay(); - painter.paint_widget_tree(top_widget); + static_cast(top_widget)->paint_overlay(); + painter.paint_widget_tree(top_widget); - portapack::backlight()->on(); + portapack::backlight()->on(); } void EventDispatcher::handle_switches() { - const auto switches_state = get_switches_state(); - - portapack::bl_tick_counter = 0; - - if( switches_state.count() == 0 ) { - // If all keys are released, we are no longer in a key event. - in_key_event = false; - } - - if( in_key_event ) { - if (switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Up]) - { - const auto event = static_cast(ui::KeyEvent::Back); - context.focus_manager().update(top_widget, event); - } - - // If we're in a key event, return. We will ignore all additional key - // presses until the first key is released. We also want to ignore events - // where the last key held generates a key event when other pressed keys - // are released. - return; - } - - if( EventDispatcher::display_sleep ) { - // Swallow event, wake up display. - if( switches_state.any() ) { - set_display_sleep(false); - } - return; - } - - for(size_t i=0; i(i); - if( !event_bubble_key(event) ) { - if (switches_state[(size_t)ui::KeyEvent::Dfu]) { - static_cast(top_widget)->toggle_overlay(); - } - else { - context.focus_manager().update(top_widget, event); - } - } - - in_key_event = true; - } - } + const auto switches_state = get_switches_state(); + + portapack::bl_tick_counter = 0; + + if (switches_state.count() == 0) { + // If all keys are released, we are no longer in a key event. + in_key_event = false; + } + + if (in_key_event) { + if (switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Up]) { + const auto event = static_cast(ui::KeyEvent::Back); + context.focus_manager().update(top_widget, event); + } + + // If we're in a key event, return. We will ignore all additional key + // presses until the first key is released. We also want to ignore events + // where the last key held generates a key event when other pressed keys + // are released. + return; + } + + if (EventDispatcher::display_sleep) { + // Swallow event, wake up display. + if (switches_state.any()) { + set_display_sleep(false); + } + return; + } + + for (size_t i = 0; i < switches_state.size(); i++) { + // TODO: Ignore multiple keys at the same time? + if (switches_state[i]) { + const auto event = static_cast(i); + if (!event_bubble_key(event)) { + if (switches_state[(size_t)ui::KeyEvent::Dfu]) { + static_cast(top_widget)->toggle_overlay(); + } else { + context.focus_manager().update(top_widget, event); + } + } + + in_key_event = true; + } + } } void EventDispatcher::handle_encoder() { - portapack::bl_tick_counter = 0; - - if( EventDispatcher::display_sleep ) { - // Swallow event, wake up display. - set_display_sleep(false); - return; - } - - const uint32_t encoder_now = get_encoder_position(); - const int32_t delta = static_cast(encoder_now - encoder_last); - encoder_last = encoder_now; - const auto event = static_cast(delta); - event_bubble_encoder(event); + portapack::bl_tick_counter = 0; + + if (EventDispatcher::display_sleep) { + // Swallow event, wake up display. + set_display_sleep(false); + return; + } + + const uint32_t encoder_now = get_encoder_position(); + const int32_t delta = static_cast(encoder_now - encoder_last); + encoder_last = encoder_now; + const auto event = static_cast(delta); + event_bubble_encoder(event); } void EventDispatcher::handle_touch() { - portapack::bl_tick_counter = 0; + portapack::bl_tick_counter = 0; - touch_manager.feed(get_touch_frame()); + touch_manager.feed(get_touch_frame()); } bool EventDispatcher::event_bubble_key(const ui::KeyEvent event) { - auto target = context.focus_manager().focus_widget(); - while( (target != nullptr) && !target->on_key(event) ) { - target = target->parent(); - } + auto target = context.focus_manager().focus_widget(); + while ((target != nullptr) && !target->on_key(event)) { + target = target->parent(); + } - /* Return true if event was consumed. */ - return (target != nullptr); + /* Return true if event was consumed. */ + return (target != nullptr); } void EventDispatcher::event_bubble_encoder(const ui::EncoderEvent event) { - auto target = context.focus_manager().focus_widget(); - while( (target != nullptr) && !target->on_encoder(event) ) { - target = target->parent(); - } + auto target = context.focus_manager().focus_widget(); + while ((target != nullptr) && !target->on_encoder(event)) { + target = target->parent(); + } } void EventDispatcher::init_message_queues() { - new (&shared_memory) SharedMemory; + new (&shared_memory) SharedMemory; } MessageHandlerRegistration::MessageHandlerRegistration( - const Message::ID message_id, - MessageHandlerMap::MessageHandler&& callback -) : message_id { message_id } -{ - message_map.register_handler(message_id, std::move(callback)); + const Message::ID message_id, + MessageHandlerMap::MessageHandler&& callback) + : message_id{message_id} { + message_map.register_handler(message_id, std::move(callback)); } MessageHandlerRegistration::~MessageHandlerRegistration() { - message_map.unregister_handler(message_id); + message_map.unregister_handler(message_id); } diff --git a/firmware/application/event_m0.hpp b/firmware/application/event_m0.hpp index 69ac14fbe..cbdb29004 100644 --- a/firmware/application/event_m0.hpp +++ b/firmware/application/event_m0.hpp @@ -38,104 +38,102 @@ #include -constexpr auto EVT_MASK_RTC_TICK = EVENT_MASK(0); -constexpr auto EVT_MASK_LCD_FRAME_SYNC = EVENT_MASK(1); -constexpr auto EVT_MASK_SWITCHES = EVENT_MASK(3); -constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4); -constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5); -constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6); -constexpr auto EVT_MASK_LOCAL = EVENT_MASK(7); +constexpr auto EVT_MASK_RTC_TICK = EVENT_MASK(0); +constexpr auto EVT_MASK_LCD_FRAME_SYNC = EVENT_MASK(1); +constexpr auto EVT_MASK_SWITCHES = EVENT_MASK(3); +constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4); +constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5); +constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6); +constexpr auto EVT_MASK_LOCAL = EVENT_MASK(7); class EventDispatcher { -public: - EventDispatcher( - ui::Widget* const top_widget, - ui::Context& context - ); - - EventDispatcher(const EventDispatcher&) = delete; - EventDispatcher(EventDispatcher&&) = delete; - EventDispatcher& operator=(const EventDispatcher&) = delete; - EventDispatcher& operator=(EventDispatcher&&) = delete; - - void run(); - static void request_stop(); - - static void set_display_sleep(const bool sleep); - - static inline void check_fifo_isr() { - if( !shared_memory.application_queue.is_empty() ) { - events_flag_isr(EVT_MASK_APPLICATION); - } - } - - static inline void events_flag(const eventmask_t events) { - if( thread_event_loop ) { - chEvtSignal(thread_event_loop, events); - } - } - - static inline void events_flag_isr(const eventmask_t events) { - if( thread_event_loop ) { - chEvtSignalI(thread_event_loop, events); - } - } - - template - static void send_message(T& message) { - shared_memory.app_local_queue.push(message); - events_flag(EVT_MASK_LOCAL); - } - -private: - static Thread* thread_event_loop; - - touch::Manager touch_manager { }; - ui::Widget* const top_widget; - ui::Painter painter; - ui::Context& context; - uint32_t encoder_last = 0; - static bool is_running; - bool sd_card_present = false; - static bool display_sleep; - bool in_key_event = false; - - eventmask_t wait(); - void dispatch(const eventmask_t events); - - void handle_application_queue(); - void handle_local_queue(); - void handle_rtc_tick(); - - static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event); - - ui::Widget* captured_widget { nullptr }; - - void on_touch_event(ui::TouchEvent event); - - //void blink_timer(); - void handle_lcd_frame_sync(); - void handle_switches(); - void handle_encoder(); - void handle_touch(); - - bool event_bubble_key(const ui::KeyEvent event); - void event_bubble_encoder(const ui::EncoderEvent event); - - void init_message_queues(); + public: + EventDispatcher( + ui::Widget* const top_widget, + ui::Context& context); + + EventDispatcher(const EventDispatcher&) = delete; + EventDispatcher(EventDispatcher&&) = delete; + EventDispatcher& operator=(const EventDispatcher&) = delete; + EventDispatcher& operator=(EventDispatcher&&) = delete; + + void run(); + static void request_stop(); + + static void set_display_sleep(const bool sleep); + + static inline void check_fifo_isr() { + if (!shared_memory.application_queue.is_empty()) { + events_flag_isr(EVT_MASK_APPLICATION); + } + } + + static inline void events_flag(const eventmask_t events) { + if (thread_event_loop) { + chEvtSignal(thread_event_loop, events); + } + } + + static inline void events_flag_isr(const eventmask_t events) { + if (thread_event_loop) { + chEvtSignalI(thread_event_loop, events); + } + } + + template + static void send_message(T& message) { + shared_memory.app_local_queue.push(message); + events_flag(EVT_MASK_LOCAL); + } + + private: + static Thread* thread_event_loop; + + touch::Manager touch_manager{}; + ui::Widget* const top_widget; + ui::Painter painter; + ui::Context& context; + uint32_t encoder_last = 0; + static bool is_running; + bool sd_card_present = false; + static bool display_sleep; + bool in_key_event = false; + + eventmask_t wait(); + void dispatch(const eventmask_t events); + + void handle_application_queue(); + void handle_local_queue(); + void handle_rtc_tick(); + + static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event); + + ui::Widget* captured_widget{nullptr}; + + void on_touch_event(ui::TouchEvent event); + + // void blink_timer(); + void handle_lcd_frame_sync(); + void handle_switches(); + void handle_encoder(); + void handle_touch(); + + bool event_bubble_key(const ui::KeyEvent event); + void event_bubble_encoder(const ui::EncoderEvent event); + + void init_message_queues(); }; class MessageHandlerRegistration { -public: - MessageHandlerRegistration( - const Message::ID message_id, - std::function&& callback - ); - - ~MessageHandlerRegistration(); - -private: - const Message::ID message_id; + public: + MessageHandlerRegistration( + const Message::ID message_id, + std::function&& callback); + + ~MessageHandlerRegistration(); + + private: + const Message::ID message_id; }; -#endif/*__EVENT_M0_H__*/ +#endif /*__EVENT_M0_H__*/ diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 4a63e9761..fcfcdf4ab 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -27,466 +27,481 @@ #include Optional File::open_fatfs(const std::filesystem::path& filename, BYTE mode) { - auto result = f_open(&f, reinterpret_cast(filename.c_str()), mode); - if( result == FR_OK ) { - if( mode & FA_OPEN_ALWAYS ) { - const auto result = f_lseek(&f, f_size(&f)); - if( result != FR_OK ) { - f_close(&f); - } - } - } - - if( result == FR_OK ) { - return { }; - } else { - return { result }; - } + auto result = f_open(&f, reinterpret_cast(filename.c_str()), mode); + if (result == FR_OK) { + if (mode & FA_OPEN_ALWAYS) { + const auto result = f_lseek(&f, f_size(&f)); + if (result != FR_OK) { + f_close(&f); + } + } + } + + if (result == FR_OK) { + return {}; + } else { + return {result}; + } } Optional File::open(const std::filesystem::path& filename) { - return open_fatfs(filename, FA_READ); + return open_fatfs(filename, FA_READ); } Optional File::append(const std::filesystem::path& filename) { - return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS); + return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS); } Optional File::create(const std::filesystem::path& filename) { - return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS); + return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS); } File::~File() { - f_close(&f); + f_close(&f); } File::Result File::read(void* const data, const Size bytes_to_read) { - UINT bytes_read = 0; - const auto result = f_read(&f, data, bytes_to_read, &bytes_read); - if( result == FR_OK ) { - return { static_cast(bytes_read) }; - } else { - return { static_cast(result) }; - } + UINT bytes_read = 0; + const auto result = f_read(&f, data, bytes_to_read, &bytes_read); + if (result == FR_OK) { + return {static_cast(bytes_read)}; + } else { + return {static_cast(result)}; + } } File::Result File::write(const void* const data, const Size bytes_to_write) { - UINT bytes_written = 0; - const auto result = f_write(&f, data, bytes_to_write, &bytes_written); - if( result == FR_OK ) { - if( bytes_to_write == bytes_written ) { - return { static_cast(bytes_written) }; - } else { - return Error { FR_DISK_FULL }; - } - } else { - return { static_cast(result) }; - } + UINT bytes_written = 0; + const auto result = f_write(&f, data, bytes_to_write, &bytes_written); + if (result == FR_OK) { + if (bytes_to_write == bytes_written) { + return {static_cast(bytes_written)}; + } else { + return Error{FR_DISK_FULL}; + } + } else { + return {static_cast(result)}; + } } File::Result File::seek(const Offset new_position) { - /* NOTE: Returns *old* position, not new position */ - const auto old_position = f_tell(&f); - const auto result = f_lseek(&f, new_position); - if( result != FR_OK ) { - return { static_cast(result) }; - } - if( f_tell(&f) != new_position ) { - return { static_cast(FR_BAD_SEEK) }; - } - return { static_cast(old_position) }; + /* NOTE: Returns *old* position, not new position */ + const auto old_position = f_tell(&f); + const auto result = f_lseek(&f, new_position); + if (result != FR_OK) { + return {static_cast(result)}; + } + if (f_tell(&f) != new_position) { + return {static_cast(FR_BAD_SEEK)}; + } + return {static_cast(old_position)}; } File::Size File::size() { - return { static_cast(f_size(&f)) }; + return {static_cast(f_size(&f))}; } Optional File::write_line(const std::string& s) { - const auto result_s = write(s.c_str(), s.size()); - if( result_s.is_error() ) { - return { result_s.error() }; - } + const auto result_s = write(s.c_str(), s.size()); + if (result_s.is_error()) { + return {result_s.error()}; + } - const auto result_crlf = write("\r\n", 2); - if( result_crlf.is_error() ) { - return { result_crlf.error() }; - } + const auto result_crlf = write("\r\n", 2); + if (result_crlf.is_error()) { + return {result_crlf.error()}; + } - return { }; + return {}; } Optional File::sync() { - const auto result = f_sync(&f); - if( result == FR_OK ) { - return { }; - } else { - return { result }; - } + const auto result = f_sync(&f); + if (result == FR_OK) { + return {}; + } else { + return {result}; + } } /* Range used for filename matching. * Start and end are inclusive positions of "???" */ struct pattern_range { - size_t start; - size_t end; + size_t start; + size_t end; }; /* Finds the last file matching the specified pattern that * can be automatically incremented (digits in pattern). * NB: assumes a patten with contiguous '?' like "FOO_???.txt". */ static std::filesystem::path find_last_ordinal_match( - const std::filesystem::path& folder, - const std::filesystem::path& pattern, - pattern_range range -) { - auto last_match = std::filesystem::path(); - auto can_increment = [range](const auto& path) { - for (auto i = range.start; i <= range.end; ++i) - if (!isdigit(path.native()[i])) - return false; - - return true; - }; - - for (const auto& entry : std::filesystem::directory_iterator(folder, pattern)) { - if (std::filesystem::is_regular_file(entry.status()) && can_increment(entry.path())) { - const auto& match = entry.path(); - if( match > last_match ) { - last_match = match; - } - } - } - - return last_match; + const std::filesystem::path& folder, + const std::filesystem::path& pattern, + pattern_range range) { + auto last_match = std::filesystem::path(); + auto can_increment = [range](const auto& path) { + for (auto i = range.start; i <= range.end; ++i) + if (!isdigit(path.native()[i])) + return false; + + return true; + }; + + for (const auto& entry : std::filesystem::directory_iterator(folder, pattern)) { + if (std::filesystem::is_regular_file(entry.status()) && can_increment(entry.path())) { + const auto& match = entry.path(); + if (match > last_match) { + last_match = match; + } + } + } + + return last_match; } /* Given a file path like "FOO_0001.txt" increment it to "FOO_0002.txt". */ static std::filesystem::path increment_filename_ordinal( - const std::filesystem::path& path, pattern_range range -) { - auto name = path.filename().native(); + const std::filesystem::path& path, + pattern_range range) { + auto name = path.filename().native(); - for (auto i = range.end; i >= range.start; --i) { - auto& c = name[i]; + for (auto i = range.end; i >= range.start; --i) { + auto& c = name[i]; - // Not a digit or would overflow the counter. - if (c < u'0' || c > u'9' || (c == u'9' && i == range.start)) - return { }; + // Not a digit or would overflow the counter. + if (c < u'0' || c > u'9' || (c == u'9' && i == range.start)) + return {}; - if (c == u'9') - c = '0'; - else { - c++; - break; - } - } + if (c == u'9') + c = '0'; + else { + c++; + break; + } + } - return { name }; + return {name}; } std::filesystem::path next_filename_matching_pattern(const std::filesystem::path& filename_pattern) { - auto path = filename_pattern.parent_path(); - auto pattern = filename_pattern.filename(); - auto range = pattern_range { - pattern.native().find_first_of(u'?'), - pattern.native().find_last_of(u'?') - }; + auto path = filename_pattern.parent_path(); + auto pattern = filename_pattern.filename(); + auto range = pattern_range{ + pattern.native().find_first_of(u'?'), + pattern.native().find_last_of(u'?')}; - const auto match = find_last_ordinal_match(path, pattern, range); + const auto match = find_last_ordinal_match(path, pattern, range); - if (match.empty()) { - auto pattern_str = pattern.native(); - for (auto i = range.start; i <= range.end; ++i) - pattern_str[i] = u'0'; - return path / pattern_str; - } + if (match.empty()) { + auto pattern_str = pattern.native(); + for (auto i = range.start; i <= range.end; ++i) + pattern_str[i] = u'0'; + return path / pattern_str; + } - auto next_name = increment_filename_ordinal(match, range); - return next_name.empty() ? next_name : path / next_name; + auto next_name = increment_filename_ordinal(match, range); + return next_name.empty() ? next_name : path / next_name; } std::vector scan_root_files(const std::filesystem::path& directory, - const std::filesystem::path& extension) { - - std::vector file_list { }; - - for(const auto& entry : std::filesystem::directory_iterator(directory, extension)) { - if( std::filesystem::is_regular_file(entry.status()) ) { - file_list.push_back(entry.path()); - } - } - - return file_list; + const std::filesystem::path& extension) { + std::vector file_list{}; + + for (const auto& entry : std::filesystem::directory_iterator(directory, extension)) { + if (std::filesystem::is_regular_file(entry.status())) { + file_list.push_back(entry.path()); + } + } + + return file_list; } std::vector scan_root_directories(const std::filesystem::path& directory) { - - std::vector directory_list { }; - - for(const auto& entry : std::filesystem::directory_iterator(directory, "*")) { - if( std::filesystem::is_directory(entry.status()) ) { - directory_list.push_back(entry.path()); - } - } - - return directory_list; + std::vector directory_list{}; + + for (const auto& entry : std::filesystem::directory_iterator(directory, "*")) { + if (std::filesystem::is_directory(entry.status())) { + directory_list.push_back(entry.path()); + } + } + + return directory_list; } std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path) { - return { f_unlink(reinterpret_cast(file_path.c_str())) }; + return {f_unlink(reinterpret_cast(file_path.c_str()))}; } std::filesystem::filesystem_error rename_file( - const std::filesystem::path& file_path, - const std::filesystem::path& new_name -) { - return { f_rename(reinterpret_cast(file_path.c_str()), reinterpret_cast(new_name.c_str())) }; + const std::filesystem::path& file_path, + const std::filesystem::path& new_name) { + return {f_rename(reinterpret_cast(file_path.c_str()), reinterpret_cast(new_name.c_str()))}; } std::filesystem::filesystem_error copy_file( - const std::filesystem::path& file_path, - const std::filesystem::path& dest_path -) { - File src; - File dst; - constexpr size_t buffer_size = 128; - uint8_t buffer[buffer_size]; + const std::filesystem::path& file_path, + const std::filesystem::path& dest_path) { + File src; + File dst; + constexpr size_t buffer_size = 128; + uint8_t buffer[buffer_size]; - auto error = src.open(file_path); - if (error.is_valid()) return error.value(); + auto error = src.open(file_path); + if (error.is_valid()) return error.value(); - error = dst.create(dest_path); - if (error.is_valid()) return error.value(); + error = dst.create(dest_path); + if (error.is_valid()) return error.value(); - while (true) { - auto result = src.read(buffer, buffer_size); - if (result.is_error()) return result.error(); + while (true) { + auto result = src.read(buffer, buffer_size); + if (result.is_error()) return result.error(); - result = dst.write(buffer, result.value()); - if (result.is_error()) return result.error(); + result = dst.write(buffer, result.value()); + if (result.is_error()) return result.error(); - if (result.value() < buffer_size) - break; - } + if (result.value() < buffer_size) + break; + } - return { }; + return {}; } FATTimestamp file_created_date(const std::filesystem::path& file_path) { - FILINFO filinfo; - - f_stat(reinterpret_cast(file_path.c_str()), &filinfo); - - return { filinfo.fdate, filinfo.ftime }; + FILINFO filinfo; + + f_stat(reinterpret_cast(file_path.c_str()), &filinfo); + + return {filinfo.fdate, filinfo.ftime}; } std::filesystem::filesystem_error make_new_file( - const std::filesystem::path& file_path -) { - File f; - auto result = f.create(file_path); - return result.is_valid() - ? result.value() - : std::filesystem::filesystem_error{ }; + const std::filesystem::path& file_path) { + File f; + auto result = f.create(file_path); + return result.is_valid() + ? result.value() + : std::filesystem::filesystem_error{}; } std::filesystem::filesystem_error make_new_directory( - const std::filesystem::path& dir_path -) { - return { f_mkdir(reinterpret_cast(dir_path.c_str())) }; + const std::filesystem::path& dir_path) { + return {f_mkdir(reinterpret_cast(dir_path.c_str()))}; } std::filesystem::filesystem_error ensure_directory( - const std::filesystem::path& dir_path -) { - if (dir_path.empty() || std::filesystem::file_exists(dir_path)) - return { }; + const std::filesystem::path& dir_path) { + if (dir_path.empty() || std::filesystem::file_exists(dir_path)) + return {}; - auto result = ensure_directory(dir_path.parent_path()); - if (result.code()) - return result; + auto result = ensure_directory(dir_path.parent_path()); + if (result.code()) + return result; - return make_new_directory(dir_path); + return make_new_directory(dir_path); } namespace std { namespace filesystem { std::string filesystem_error::what() const { - switch(err) { - case FR_OK: return "ok"; - case FR_DISK_ERR: return "disk error"; - case FR_INT_ERR: return "insanity detected"; - case FR_NOT_READY: return "SD card not ready"; - case FR_NO_FILE: return "no file"; - case FR_NO_PATH: return "no path"; - case FR_INVALID_NAME: return "invalid name"; - case FR_DENIED: return "denied"; - case FR_EXIST: return "exists"; - case FR_INVALID_OBJECT: return "invalid object"; - case FR_WRITE_PROTECTED: return "write protected"; - case FR_INVALID_DRIVE: return "invalid drive"; - case FR_NOT_ENABLED: return "not enabled"; - case FR_NO_FILESYSTEM: return "no filesystem"; - case FR_MKFS_ABORTED: return "mkfs aborted"; - case FR_TIMEOUT: return "timeout"; - case FR_LOCKED: return "locked"; - case FR_NOT_ENOUGH_CORE: return "not enough core"; - case FR_TOO_MANY_OPEN_FILES: return "too many open files"; - case FR_INVALID_PARAMETER: return "invalid parameter"; - case FR_EOF: return "end of file"; - case FR_DISK_FULL: return "disk full"; - case FR_BAD_SEEK: return "bad seek"; - case FR_UNEXPECTED: return "unexpected"; - default: return "unknown"; - } + switch (err) { + case FR_OK: + return "ok"; + case FR_DISK_ERR: + return "disk error"; + case FR_INT_ERR: + return "insanity detected"; + case FR_NOT_READY: + return "SD card not ready"; + case FR_NO_FILE: + return "no file"; + case FR_NO_PATH: + return "no path"; + case FR_INVALID_NAME: + return "invalid name"; + case FR_DENIED: + return "denied"; + case FR_EXIST: + return "exists"; + case FR_INVALID_OBJECT: + return "invalid object"; + case FR_WRITE_PROTECTED: + return "write protected"; + case FR_INVALID_DRIVE: + return "invalid drive"; + case FR_NOT_ENABLED: + return "not enabled"; + case FR_NO_FILESYSTEM: + return "no filesystem"; + case FR_MKFS_ABORTED: + return "mkfs aborted"; + case FR_TIMEOUT: + return "timeout"; + case FR_LOCKED: + return "locked"; + case FR_NOT_ENOUGH_CORE: + return "not enough core"; + case FR_TOO_MANY_OPEN_FILES: + return "too many open files"; + case FR_INVALID_PARAMETER: + return "invalid parameter"; + case FR_EOF: + return "end of file"; + case FR_DISK_FULL: + return "disk full"; + case FR_BAD_SEEK: + return "bad seek"; + case FR_UNEXPECTED: + return "unexpected"; + default: + return "unknown"; + } } path path::parent_path() const { - const auto index = _s.find_last_of(preferred_separator); - if( index == _s.npos ) { - return { }; // NB: Deviation from STL. - } else { - return _s.substr(0, index); - } + const auto index = _s.find_last_of(preferred_separator); + if (index == _s.npos) { + return {}; // NB: Deviation from STL. + } else { + return _s.substr(0, index); + } } path path::extension() const { - const auto t = filename().native(); - const auto index = t.find_last_of(u'.'); - if( index == t.npos ) { - return { }; - } else { - return t.substr(index); - } + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if (index == t.npos) { + return {}; + } else { + return t.substr(index); + } } path path::filename() const { - const auto index = _s.find_last_of(preferred_separator); - if( index == _s.npos ) { - return _s; - } else { - return _s.substr(index + 1); - } + const auto index = _s.find_last_of(preferred_separator); + if (index == _s.npos) { + return _s; + } else { + return _s.substr(index + 1); + } } path path::stem() const { - const auto t = filename().native(); - const auto index = t.find_last_of(u'.'); - if( index == t.npos ) { - return t; - } else { - return t.substr(0, index); - } + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if (index == t.npos) { + return t; + } else { + return t.substr(0, index); + } } std::string path::string() const { - std::wstring_convert, path::value_type> conv; - return conv.to_bytes(native()); + std::wstring_convert, path::value_type> conv; + return conv.to_bytes(native()); } path& path::replace_extension(const path& replacement) { - const auto t = extension().native(); - _s.erase(_s.size() - t.size()); - if( !replacement._s.empty() ) { - if( replacement._s.front() != u'.' ) { - _s += u'.'; - } - _s += replacement._s; - } - return *this; + const auto t = extension().native(); + _s.erase(_s.size() - t.size()); + if (!replacement._s.empty()) { + if (replacement._s.front() != u'.') { + _s += u'.'; + } + _s += replacement._s; + } + return *this; } bool operator==(const path& lhs, const path& rhs) { - return lhs.native() == rhs.native(); + return lhs.native() == rhs.native(); } bool operator!=(const path& lhs, const path& rhs) { - return !(lhs == rhs); + return !(lhs == rhs); } bool operator<(const path& lhs, const path& rhs) { - return lhs.native() < rhs.native(); + return lhs.native() < rhs.native(); } bool operator>(const path& lhs, const path& rhs) { - return lhs.native() > rhs.native(); + return lhs.native() > rhs.native(); } path operator+(const path& lhs, const path& rhs) { - path result = lhs; - result += rhs; - return result; + path result = lhs; + result += rhs; + return result; } path operator/(const path& lhs, const path& rhs) { - path result = lhs; - result /= rhs; - return result; + path result = lhs; + result /= rhs; + return result; } directory_iterator::directory_iterator( - std::filesystem::path path, - std::filesystem::path wild -) : pattern { wild } -{ - impl = std::make_shared(); - const auto result = f_findfirst(&impl->dir, &impl->filinfo, - reinterpret_cast(path.c_str()), - reinterpret_cast(pattern.c_str())); - if (result != FR_OK || impl->filinfo.fname[0] == (TCHAR)'\0') { - impl.reset(); - // TODO: Throw exception if/when I enable exceptions... - } + std::filesystem::path path, + std::filesystem::path wild) + : pattern{wild} { + impl = std::make_shared(); + const auto result = f_findfirst(&impl->dir, &impl->filinfo, + reinterpret_cast(path.c_str()), + reinterpret_cast(pattern.c_str())); + if (result != FR_OK || impl->filinfo.fname[0] == (TCHAR)'\0') { + impl.reset(); + // TODO: Throw exception if/when I enable exceptions... + } } directory_iterator& directory_iterator::operator++() { - const auto result = f_findnext(&impl->dir, &impl->filinfo); - if( (result != FR_OK) || (impl->filinfo.fname[0] == 0) ) { - impl.reset(); - } - return *this; + const auto result = f_findnext(&impl->dir, &impl->filinfo); + if ((result != FR_OK) || (impl->filinfo.fname[0] == 0)) { + impl.reset(); + } + return *this; } bool is_directory(const file_status s) { - return (s & AM_DIR); + return (s & AM_DIR); } bool is_regular_file(const file_status s) { - return !(s & AM_DIR); + return !(s & AM_DIR); } bool file_exists(const path& file_path) { - FILINFO filinfo; - auto fr = f_stat(reinterpret_cast(file_path.c_str()), &filinfo); - - return fr == FR_OK; + FILINFO filinfo; + auto fr = f_stat(reinterpret_cast(file_path.c_str()), &filinfo); + + return fr == FR_OK; } bool is_directory(const path& file_path) { - FILINFO filinfo; - auto fr = f_stat(reinterpret_cast(file_path.c_str()), &filinfo); - - return fr == FR_OK && is_directory(static_cast(filinfo.fattrib)); + FILINFO filinfo; + auto fr = f_stat(reinterpret_cast(file_path.c_str()), &filinfo); + + return fr == FR_OK && is_directory(static_cast(filinfo.fattrib)); } space_info space(const path& p) { - DWORD free_clusters { 0 }; - FATFS* fs; - if( f_getfree(reinterpret_cast(p.c_str()), &free_clusters, &fs) == FR_OK ) { + DWORD free_clusters{0}; + FATFS* fs; + if (f_getfree(reinterpret_cast(p.c_str()), &free_clusters, &fs) == FR_OK) { #if _MAX_SS != _MIN_SS - static_assert(false, "FatFs not configured for fixed sector size"); + static_assert(false, "FatFs not configured for fixed sector size"); #else - const std::uintmax_t cluster_bytes = fs->csize * _MIN_SS; - return { - (fs->n_fatent - 2) * cluster_bytes, - free_clusters * cluster_bytes, - free_clusters * cluster_bytes, - }; + const std::uintmax_t cluster_bytes = fs->csize * _MIN_SS; + return { + (fs->n_fatent - 2) * cluster_bytes, + free_clusters * cluster_bytes, + free_clusters * cluster_bytes, + }; #endif - } else { - return { 0, 0, 0 }; - } + } else { + return {0, 0, 0}; + } } } /* namespace filesystem */ diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 342d8d990..bdc69e977 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -39,134 +39,125 @@ namespace std { namespace filesystem { struct filesystem_error { - constexpr filesystem_error() = default; - - constexpr filesystem_error( - FRESULT fatfs_error - ) : err { fatfs_error } - { - } - - constexpr filesystem_error( - unsigned int other_error - ) : err { other_error } - { - } - - uint32_t code() const { - return err; - } - - std::string what() const; - - bool ok() const { - return err == FR_OK; - } - -private: - uint32_t err { FR_OK }; + constexpr filesystem_error() = default; + + constexpr filesystem_error( + FRESULT fatfs_error) + : err{fatfs_error} { + } + + constexpr filesystem_error( + unsigned int other_error) + : err{other_error} { + } + + uint32_t code() const { + return err; + } + + std::string what() const; + + bool ok() const { + return err == FR_OK; + } + + private: + uint32_t err{FR_OK}; }; struct path { - using string_type = std::u16string; - using value_type = string_type::value_type; - - static constexpr value_type preferred_separator = u'/'; - - path( - ) : _s { } - { - } - - path( - const path& p - ) : _s { p._s } - { - } - - path( - path&& p - ) : _s { std::move(p._s) } - { - } - - template - path( - const Source& source - ) : path { std::begin(source), std::end(source) } - { - } - - template - path( - InputIt first, - InputIt last - ) : _s { first, last } - { - } - - path( - const char16_t* const s - ) : _s { s } - { - } - - path( - const TCHAR* const s - ) : _s { reinterpret_cast(s) } - { - } - - path& operator=(const path& p) { - _s = p._s; - return *this; - } - - path& operator=(path&& p) { - _s = std::move(p._s); - return *this; - } - - path parent_path() const; - path extension() const; - path filename() const; - path stem() const; - - bool empty() const { - return _s.empty(); - } - - const value_type* c_str() const { - return native().c_str(); - } - - const string_type& native() const { - return _s; - } - - std::string string() const; - - path& operator+=(const path& p) { - _s += p._s; - return *this; - } - - path& operator+=(const string_type& str) { - _s += str; - return *this; - } - - path& operator/=(const path& p) { - if (_s.back() != preferred_separator) - _s += preferred_separator; - _s += p._s; - return *this; - } - - path& replace_extension(const path& replacement = path()); - -private: - string_type _s; + using string_type = std::u16string; + using value_type = string_type::value_type; + + static constexpr value_type preferred_separator = u'/'; + + path() + : _s{} { + } + + path( + const path& p) + : _s{p._s} { + } + + path( + path&& p) + : _s{std::move(p._s)} { + } + + template + path( + const Source& source) + : path{std::begin(source), std::end(source)} { + } + + template + path( + InputIt first, + InputIt last) + : _s{first, last} { + } + + path( + const char16_t* const s) + : _s{s} { + } + + path( + const TCHAR* const s) + : _s{reinterpret_cast(s)} { + } + + path& operator=(const path& p) { + _s = p._s; + return *this; + } + + path& operator=(path&& p) { + _s = std::move(p._s); + return *this; + } + + path parent_path() const; + path extension() const; + path filename() const; + path stem() const; + + bool empty() const { + return _s.empty(); + } + + const value_type* c_str() const { + return native().c_str(); + } + + const string_type& native() const { + return _s; + } + + std::string string() const; + + path& operator+=(const path& p) { + _s += p._s; + return *this; + } + + path& operator+=(const string_type& str) { + _s += str; + return *this; + } + + path& operator/=(const path& p) { + if (_s.back() != preferred_separator) + _s += preferred_separator; + _s += p._s; + return *this; + } + + path& replace_extension(const path& replacement = path()); + + private: + string_type _s; }; bool operator==(const path& lhs, const path& rhs); @@ -182,64 +173,70 @@ static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::valu static_assert(sizeof(path::value_type) == sizeof(TCHAR), "FatFs TCHAR size != std::filesystem::path::value_type"); struct space_info { - static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small (= 8, "std::uintmax_t too small ( impl { }; - const path pattern { }; - - friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs); - -public: - using difference_type = std::ptrdiff_t; - using value_type = directory_entry; - using pointer = const directory_entry*; - using reference = const directory_entry&; - using iterator_category = std::input_iterator_tag; - - directory_iterator() noexcept { }; - directory_iterator(std::filesystem::path path, std::filesystem::path wild); - - ~directory_iterator() { } - - directory_iterator& operator++(); - - reference operator*() const { - // TODO: Exception or assert if impl == nullptr. - return impl->filinfo; - } + struct Impl { + DIR dir; + directory_entry filinfo; + + ~Impl() { + f_closedir(&dir); + } + }; + + std::shared_ptr impl{}; + const path pattern{}; + + friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs); + + public: + using difference_type = std::ptrdiff_t; + using value_type = directory_entry; + using pointer = const directory_entry*; + using reference = const directory_entry&; + using iterator_category = std::input_iterator_tag; + + directory_iterator() noexcept {}; + directory_iterator(std::filesystem::path path, std::filesystem::path wild); + + ~directory_iterator() {} + + directory_iterator& operator++(); + + reference operator*() const { + // TODO: Exception or assert if impl == nullptr. + return impl->filinfo; + } }; -inline const directory_iterator& begin(const directory_iterator& iter) noexcept { return iter; }; -inline directory_iterator end(const directory_iterator&) noexcept { return { }; }; +inline const directory_iterator& begin(const directory_iterator& iter) noexcept { + return iter; +}; +inline directory_iterator end(const directory_iterator&) noexcept { + return {}; +}; -inline bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs) { return lhs.impl != rhs.impl; }; +inline bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs) { + return lhs.impl != rhs.impl; +}; bool is_directory(const file_status s); bool is_regular_file(const file_status s); @@ -252,8 +249,8 @@ space_info space(const path& p); } /* namespace std */ struct FATTimestamp { - uint16_t FAT_date; - uint16_t FAT_time; + uint16_t FAT_date; + uint16_t FAT_time; }; std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path); @@ -279,101 +276,99 @@ static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); /* Dangerous to expose these, as FatFs native error values are byte-sized. However, * my filesystem_error implementation is fine with it. */ -#define FR_DISK_FULL (0x100) -#define FR_EOF (0x101) -#define FR_BAD_SEEK (0x102) -#define FR_UNEXPECTED (0x103) +#define FR_DISK_FULL (0x100) +#define FR_EOF (0x101) +#define FR_BAD_SEEK (0x102) +#define FR_UNEXPECTED (0x103) class File { -public: - using Size = uint64_t; - using Offset = uint64_t; - using Timestamp = uint32_t; - using Error = std::filesystem::filesystem_error; - - template - struct Result { - enum class Type { - Success, - Error, - } type; - union { - T value_; - Error error_; - }; - - bool is_ok() const { - return type == Type::Success; - } - - bool is_error() const { - return type == Type::Error; - } - - const T& value() const { - return value_; - } - - Error error() const { - return error_; - } - - Result() = delete; - - constexpr Result( - T value - ) : type { Type::Success }, - value_ { value } - { - } - - constexpr Result( - Error error - ) : type { Type::Error }, - error_ { error } - { - } - - ~Result() { - if( type == Type::Success ) { - value_.~T(); - } - } - }; - - File() { }; - ~File(); - - /* Prevent copies */ - File(const File&) = delete; - File& operator=(const File&) = delete; - - // TODO: Return Result<>. - Optional open(const std::filesystem::path& filename); - Optional append(const std::filesystem::path& filename); - Optional create(const std::filesystem::path& filename); - - Result read(void* const data, const Size bytes_to_read); - Result write(const void* const data, const Size bytes_to_write); - - Result seek(const uint64_t Offset); - Timestamp created_date(); - Size size(); - - template - Result write(const std::array& data) { - return write(data.data(), N); - } - - Optional write_line(const std::string& s); - - // TODO: Return Result<>. - Optional sync(); - -private: - FIL f { }; - - Optional open_fatfs(const std::filesystem::path& filename, BYTE mode); + public: + using Size = uint64_t; + using Offset = uint64_t; + using Timestamp = uint32_t; + using Error = std::filesystem::filesystem_error; + + template + struct Result { + enum class Type { + Success, + Error, + } type; + union { + T value_; + Error error_; + }; + + bool is_ok() const { + return type == Type::Success; + } + + bool is_error() const { + return type == Type::Error; + } + + const T& value() const { + return value_; + } + + Error error() const { + return error_; + } + + Result() = delete; + + constexpr Result( + T value) + : type{Type::Success}, + value_{value} { + } + + constexpr Result( + Error error) + : type{Type::Error}, + error_{error} { + } + + ~Result() { + if (type == Type::Success) { + value_.~T(); + } + } + }; + + File(){}; + ~File(); + + /* Prevent copies */ + File(const File&) = delete; + File& operator=(const File&) = delete; + + // TODO: Return Result<>. + Optional open(const std::filesystem::path& filename); + Optional append(const std::filesystem::path& filename); + Optional create(const std::filesystem::path& filename); + + Result read(void* const data, const Size bytes_to_read); + Result write(const void* const data, const Size bytes_to_write); + + Result seek(const uint64_t Offset); + Timestamp created_date(); + Size size(); + + template + Result write(const std::array& data) { + return write(data.data(), N); + } + + Optional write_line(const std::string& s); + + // TODO: Return Result<>. + Optional sync(); + + private: + FIL f{}; + + Optional open_fatfs(const std::filesystem::path& filename, BYTE mode); }; -#endif/*__FILE_H__*/ +#endif /*__FILE_H__*/ diff --git a/firmware/application/firmware_info.c b/firmware/application/firmware_info.c index f98d5365b..a026a1871 100644 --- a/firmware/application/firmware_info.c +++ b/firmware/application/firmware_info.c @@ -5,9 +5,9 @@ #define DFU_MODE_VALUE 0 __attribute__((section(".firmware_info"))) const struct firmware_info_t firmware_info = { - .magic = "HACKRFFW", - .struct_version = 1, - .dfu_mode = DFU_MODE_VALUE, - .supported_platform = SUPPORTED_PLATFORM, - .version_string = VERSION_STRING, + .magic = "HACKRFFW", + .struct_version = 1, + .dfu_mode = DFU_MODE_VALUE, + .supported_platform = SUPPORTED_PLATFORM, + .version_string = VERSION_STRING, }; \ No newline at end of file diff --git a/firmware/application/freqman.cpp b/firmware/application/freqman.cpp index 5f826f3c0..543d1ed9d 100644 --- a/firmware/application/freqman.cpp +++ b/firmware/application/freqman.cpp @@ -27,68 +27,63 @@ using option_t = std::pair; using options_t = std::vector; options_t freqman_entry_modulations = { - { "AM", 0 }, - { "NFM", 1 }, - { "WFM", 2 } -}; - -options_t freqman_entry_bandwidths[ 4 ] = { - { //AM - { "DSB 9k" , 0 }, - { "DSB 6k" , 1 }, - { "USB+3k" , 2 }, - { "LSB-3k" , 3 }, - { "CW" , 4 } - }, - { //NFM - { "8k5" , 0 }, - { "11k" , 1 }, - { "16k" , 2 } - }, - { //WFM - { "200k" , 0 }, - { "180k" , 1 }, - { "40k" , 2 }, - } -}; + {"AM", 0}, + {"NFM", 1}, + {"WFM", 2}}; + +options_t freqman_entry_bandwidths[4] = { + {// AM + {"DSB 9k", 0}, + {"DSB 6k", 1}, + {"USB+3k", 2}, + {"LSB-3k", 3}, + {"CW", 4}}, + {// NFM + {"8k5", 0}, + {"11k", 1}, + {"16k", 2}}, + { + // WFM + {"200k", 0}, + {"180k", 1}, + {"40k", 2}, + }}; options_t freqman_entry_steps = { - { "0.1kHz " , 100 }, - { "1kHz " , 1000 }, - { "5kHz (SA AM)" , 5000 }, - { "6.25kHz(NFM)" , 6250 }, - { "8.33kHz(AIR)" , 8330 }, - { "9kHz (EU AM)" , 9000 }, - { "10kHz(US AM)" , 10000 }, - { "12.5kHz(NFM)" , 12500 }, - { "15kHz (HFM)" , 15000 }, - { "25kHz (N1)" , 25000 }, - { "30kHz (OIRT)" , 30000 }, - { "50kHz (FM1)" , 50000 }, - { "100kHz (FM2)" , 100000 }, - { "250kHz (N2)" , 250000 }, - { "500kHz (WFM)" , 500000 }, - { "1MHz " , 1000000 } -}; + {"0.1kHz ", 100}, + {"1kHz ", 1000}, + {"5kHz (SA AM)", 5000}, + {"6.25kHz(NFM)", 6250}, + {"8.33kHz(AIR)", 8330}, + {"9kHz (EU AM)", 9000}, + {"10kHz(US AM)", 10000}, + {"12.5kHz(NFM)", 12500}, + {"15kHz (HFM)", 15000}, + {"25kHz (N1)", 25000}, + {"30kHz (OIRT)", 30000}, + {"50kHz (FM1)", 50000}, + {"100kHz (FM2)", 100000}, + {"250kHz (N2)", 250000}, + {"500kHz (WFM)", 500000}, + {"1MHz ", 1000000}}; options_t freqman_entry_steps_short = { - { "0.1kHz" , 100 }, - { "1kHz" , 1000 }, - { "5kHz" , 5000 }, - { "6.25kHz" , 6250 }, - { "8.33kHz" , 8330 }, - { "9kHz" , 9000 }, - { "10kHz" , 10000 }, - { "12.5kHz" , 12500 }, - { "15kHz" , 15000 }, - { "25kHz" , 25000 }, - { "30kHz" , 30000 }, - { "50kHz" , 50000 }, - { "100kHz" , 100000 }, - { "250kHz" , 250000 }, - { "500kHz" , 500000 }, - { "1MHz" , 1000000 } -}; + {"0.1kHz", 100}, + {"1kHz", 1000}, + {"5kHz", 5000}, + {"6.25kHz", 6250}, + {"8.33kHz", 8330}, + {"9kHz", 9000}, + {"10kHz", 10000}, + {"12.5kHz", 12500}, + {"15kHz", 15000}, + {"25kHz", 25000}, + {"30kHz", 30000}, + {"50kHz", 50000}, + {"100kHz", 100000}, + {"250kHz", 250000}, + {"500kHz", 500000}, + {"1MHz", 1000000}}; std::vector get_freqman_files() { std::vector file_list; @@ -106,25 +101,24 @@ std::vector get_freqman_files() { return file_list; }; -bool load_freqman_file(std::string& file_stem, freqman_db &db) { - return load_freqman_file_ex( file_stem , db , true , true , true ); +bool load_freqman_file(std::string& file_stem, freqman_db& db) { + return load_freqman_file_ex(file_stem, db, true, true, true); } -bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs , bool load_ranges , bool load_hamradios ) { +bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs, bool load_ranges, bool load_hamradios) { File freqman_file; size_t length, n = 0, file_position = 0; - char * pos; - char * line_start; - char * line_end; + char* pos; + char* line_start; + char* line_end; std::string description; rf::Frequency frequency_a, frequency_b; char file_data[257]; freqman_entry_type type; - freqman_index_t modulation = 0 ; - freqman_index_t bandwidth = 0 ; - freqman_index_t step = 0 ; - freqman_index_t tone = 0 ; - + freqman_index_t modulation = 0; + freqman_index_t bandwidth = 0; + freqman_index_t step = 0; + freqman_index_t tone = 0; db.clear(); @@ -139,7 +133,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq memset(file_data, 0, 257); auto read_size = freqman_file.read(file_data, 256); if (read_size.is_error()) - return false; // Read error + return false; // Read error file_position += 256; @@ -152,18 +146,17 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq // Look for complete lines in buffer while ((line_end = strstr(line_start, "\x0A"))) { - - *line_end = 0; // Stop strstr() searches below at EOL - modulation = -1 ; - bandwidth = -1 ; - step = -1 ; - tone = -1 ; + *line_end = 0; // Stop strstr() searches below at EOL + modulation = -1; + bandwidth = -1; + step = -1; + tone = -1; type = ERROR_TYPE; frequency_a = frequency_b = 0; // Read frequency pos = strstr(line_start, "f="); - if(pos) { + if (pos) { pos += 2; frequency_a = strtoll(pos, nullptr, 10); type = SINGLE; @@ -180,7 +173,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq frequency_b = strtoll(pos, nullptr, 10); } else frequency_b = 0; - }else { + } else { // ... or hamradio pos = strstr(line_start, "r="); if (pos) { @@ -192,7 +185,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq pos += 2; frequency_b = strtoll(pos, nullptr, 10); } else - frequency_b = frequency_a ; + frequency_b = frequency_a; } else frequency_a = 0; } @@ -201,20 +194,20 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq pos = strstr(line_start, "m="); if (pos) { pos += 2; - modulation = freqman_entry_get_modulation_from_str( pos ); - } + modulation = freqman_entry_get_modulation_from_str(pos); + } // bandwidth if any pos = strstr(line_start, "bw="); if (pos) { pos += 3; - bandwidth = freqman_entry_get_bandwidth_from_str( modulation , pos ); - } + bandwidth = freqman_entry_get_bandwidth_from_str(modulation, pos); + } // step if any pos = strstr(line_start, "s="); if (pos) { pos += 2; - step = freqman_entry_get_step_from_str_short( pos ); - } + step = freqman_entry_get_step_from_str_short(pos); + } // ctcss tone if any /* disabled until better form pos = strstr(line_start, "c="); @@ -230,9 +223,8 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq description = string(pos, length); } else description = "-"; - if( (type == SINGLE && load_freqs) || (type == RANGE && load_ranges) || (type == HAMRADIO && load_hamradios) ) - { - db.push_back({ frequency_a, frequency_b, description, type , modulation , bandwidth , step , tone }); + if ((type == SINGLE && load_freqs) || (type == RANGE && load_ranges) || (type == HAMRADIO && load_hamradios)) { + db.push_back({frequency_a, frequency_b, description, type, modulation, bandwidth, step, tone}); n++; if (n > FREQMAN_MAX_PER_FILE) return true; } @@ -242,114 +234,98 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq } if (read_size.value() != 256) - break; // End of file + break; // End of file // Restart at beginning of last incomplete line file_position -= (file_data + 256 - line_start); } /* populate implicitly specified modulation / bandwidth */ - if( db.size() > 2 ) - { - modulation = db[ 0 ] . modulation; - bandwidth = db[ 0 ] . bandwidth; - - for( unsigned int it = 1 ; it < db.size() ; it ++ ) - { - if( db[ it ] . modulation < 0 ) - { - db[ it ] . modulation = modulation ; - } - else - { - modulation = db[ it ] . modulation ; - } - if( db[ it ] . bandwidth < 0 ) - { - db[ it ] . bandwidth = bandwidth ; + if (db.size() > 2) { + modulation = db[0].modulation; + bandwidth = db[0].bandwidth; + + for (unsigned int it = 1; it < db.size(); it++) { + if (db[it].modulation < 0) { + db[it].modulation = modulation; + } else { + modulation = db[it].modulation; } - else - { - modulation = db[ it ] . bandwidth ; + if (db[it].bandwidth < 0) { + db[it].bandwidth = bandwidth; + } else { + modulation = db[it].bandwidth; } } } return true; } -bool get_freq_string( freqman_entry &entry , std::string &item_string ) -{ +bool get_freq_string(freqman_entry& entry, std::string& item_string) { rf::Frequency frequency_a, frequency_b; frequency_a = entry.frequency_a; if (entry.type == SINGLE) { // Single item_string = "f=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0'); - } else if( entry.type == RANGE ) { + } else if (entry.type == RANGE) { // Range frequency_b = entry.frequency_b; item_string = "a=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0'); item_string += ",b=" + to_string_dec_uint(frequency_b / 1000) + to_string_dec_uint(frequency_b % 1000UL, 3, '0'); - if( entry.step >= 0 ) - { - item_string += ",s=" + freqman_entry_get_step_string_short( entry.step ); + if (entry.step >= 0) { + item_string += ",s=" + freqman_entry_get_step_string_short(entry.step); } - } else if( entry.type == HAMRADIO ) { + } else if (entry.type == HAMRADIO) { frequency_b = entry.frequency_b; item_string = "r=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0'); item_string += ",t=" + to_string_dec_uint(frequency_b / 1000) + to_string_dec_uint(frequency_b % 1000UL, 3, '0'); - if( entry.tone >= 0 ) - { - item_string += ",c=" + tone_key_string( entry.tone ); + if (entry.tone >= 0) { + item_string += ",c=" + tone_key_string(entry.tone); } } - if( entry.modulation >= 0 && (unsigned)entry.modulation < freqman_entry_modulations . size() ) - { - item_string += ",m=" + freqman_entry_get_modulation_string( entry.modulation ); - if( entry.bandwidth >= 0 && (unsigned)entry.bandwidth < freqman_entry_bandwidths[ entry.modulation ] . size() ) - { - item_string += ",bw=" + freqman_entry_get_bandwidth_string( entry.modulation , entry.bandwidth ); + if (entry.modulation >= 0 && (unsigned)entry.modulation < freqman_entry_modulations.size()) { + item_string += ",m=" + freqman_entry_get_modulation_string(entry.modulation); + if (entry.bandwidth >= 0 && (unsigned)entry.bandwidth < freqman_entry_bandwidths[entry.modulation].size()) { + item_string += ",bw=" + freqman_entry_get_bandwidth_string(entry.modulation, entry.bandwidth); } } if (entry.description.size()) item_string += ",d=" + entry.description; - return true ; + return true; } -bool save_freqman_file(std::string &file_stem, freqman_db &db) { - +bool save_freqman_file(std::string& file_stem, freqman_db& db) { File freqman_file; std::string freq_file_path = "FREQMAN/" + file_stem + ".TXT"; std::string tmp_freq_file_path = "FREQMAN/" + file_stem + ".TXT.TMP"; - if( !db.size() ) - { - delete_file( "FREQMAN/"+file_stem+".TXT" ); - return true ; + if (!db.size()) { + delete_file("FREQMAN/" + file_stem + ".TXT"); + return true; } - delete_file( tmp_freq_file_path ); - auto result = freqman_file.open( tmp_freq_file_path ); - if ( !result.is_valid() ) { + delete_file(tmp_freq_file_path); + auto result = freqman_file.open(tmp_freq_file_path); + if (!result.is_valid()) { for (size_t n = 0; n < db.size(); n++) { std::string item_string; auto& entry = db[n]; - get_freq_string( entry , item_string ); - freqman_file.write_line( item_string ); - delete &item_string; + get_freq_string(entry, item_string); + freqman_file.write_line(item_string); + delete &item_string; } - delete_file( freq_file_path ); - rename_file( tmp_freq_file_path , freq_file_path ); + delete_file(freq_file_path); + rename_file(tmp_freq_file_path, freq_file_path); return true; } - return false ; + return false; } bool create_freqman_file(std::string& file_stem, File& freqman_file) { - - auto result = freqman_file.create( "FREQMAN/" + file_stem + ".TXT" ); + auto result = freqman_file.create("FREQMAN/" + file_stem + ".TXT"); if (result.is_valid()) return false; @@ -357,10 +333,10 @@ bool create_freqman_file(std::string& file_stem, File& freqman_file) { return true; } -std::string freqman_item_string(freqman_entry &entry, size_t max_length) { +std::string freqman_item_string(freqman_entry& entry, size_t max_length) { std::string item_string; - switch( entry.type ){ + switch (entry.type) { case SINGLE: item_string = to_string_short_freq(entry.frequency_a) + "M: " + entry.description; break; @@ -381,143 +357,115 @@ std::string freqman_item_string(freqman_entry &entry, size_t max_length) { return item_string; } -void freqman_set_modulation_option( OptionsField &option ) -{ - option.set_options( freqman_entry_modulations ); +void freqman_set_modulation_option(OptionsField& option) { + option.set_options(freqman_entry_modulations); } -void freqman_set_bandwidth_option( freqman_index_t modulation , OptionsField &option ) -{ - option.set_options( freqman_entry_bandwidths[ modulation ] ); +void freqman_set_bandwidth_option(freqman_index_t modulation, OptionsField& option) { + option.set_options(freqman_entry_bandwidths[modulation]); } -void freqman_set_step_option( OptionsField &option ) -{ - option.set_options( freqman_entry_steps ); +void freqman_set_step_option(OptionsField& option) { + option.set_options(freqman_entry_steps); } -void freqman_set_step_option_short( OptionsField &option ) -{ - option.set_options( freqman_entry_steps_short ); +void freqman_set_step_option_short(OptionsField& option) { + option.set_options(freqman_entry_steps_short); } -std::string freqman_entry_get_modulation_string( freqman_index_t modulation ) -{ - if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() ) - { - return std::string( "" ); // unknown modulation +std::string freqman_entry_get_modulation_string(freqman_index_t modulation) { + if (modulation < 0 || (unsigned)modulation >= freqman_entry_modulations.size()) { + return std::string(""); // unknown modulation } - return freqman_entry_modulations[ modulation ] . first ; + return freqman_entry_modulations[modulation].first; } -std::string freqman_entry_get_bandwidth_string( freqman_index_t modulation , freqman_index_t bandwidth ) -{ - if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() ) - { - return std::string( "" ); // unknown modulation +std::string freqman_entry_get_bandwidth_string(freqman_index_t modulation, freqman_index_t bandwidth) { + if (modulation < 0 || (unsigned)modulation >= freqman_entry_modulations.size()) { + return std::string(""); // unknown modulation } - if( bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[ modulation ] . size() ) - { - return std::string( "" ); // unknown modulation + if (bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[modulation].size()) { + return std::string(""); // unknown modulation } - return freqman_entry_bandwidths[ modulation ][ bandwidth ] . first ; + return freqman_entry_bandwidths[modulation][bandwidth].first; } -std::string freqman_entry_get_step_string( freqman_index_t step ) -{ - if( step < 0 || (unsigned)step >= freqman_entry_steps . size() ) - { - return std::string( "" ); // unknown modulation +std::string freqman_entry_get_step_string(freqman_index_t step) { + if (step < 0 || (unsigned)step >= freqman_entry_steps.size()) { + return std::string(""); // unknown modulation } - return freqman_entry_steps[ step ] . first ; + return freqman_entry_steps[step].first; } -std::string freqman_entry_get_step_string_short( freqman_index_t step ) -{ - if( step < 0 || (unsigned)step >= freqman_entry_steps_short . size() ) - { - return std::string( "" ); // unknown modulation +std::string freqman_entry_get_step_string_short(freqman_index_t step) { + if (step < 0 || (unsigned)step >= freqman_entry_steps_short.size()) { + return std::string(""); // unknown modulation } - return freqman_entry_steps_short[ step ] . first ; + return freqman_entry_steps_short[step].first; } -int32_t freqman_entry_get_modulation_value( freqman_index_t modulation ) -{ - if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() ) - { - return -1 ; // unknown modulation +int32_t freqman_entry_get_modulation_value(freqman_index_t modulation) { + if (modulation < 0 || (unsigned)modulation >= freqman_entry_modulations.size()) { + return -1; // unknown modulation } - return freqman_entry_modulations[ modulation ] . second ; + return freqman_entry_modulations[modulation].second; } -int32_t freqman_entry_get_bandwidth_value( freqman_index_t modulation , freqman_index_t bandwidth ) -{ - if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() ) - { - return -1 ; // unknown modulation +int32_t freqman_entry_get_bandwidth_value(freqman_index_t modulation, freqman_index_t bandwidth) { + if (modulation < 0 || (unsigned)modulation >= freqman_entry_modulations.size()) { + return -1; // unknown modulation } - if( bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[ modulation ] . size() ) - { - return -1 ; // unknown bandwidth for modulation + if (bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[modulation].size()) { + return -1; // unknown bandwidth for modulation } - return freqman_entry_bandwidths[ modulation ][ bandwidth ] . second ; + return freqman_entry_bandwidths[modulation][bandwidth].second; } -int32_t freqman_entry_get_step_value( freqman_index_t step ) -{ - if( step < 0 || (unsigned)step >= freqman_entry_steps . size() ) - { - return -1 ; // unknown modulation +int32_t freqman_entry_get_step_value(freqman_index_t step) { + if (step < 0 || (unsigned)step >= freqman_entry_steps.size()) { + return -1; // unknown modulation } - return freqman_entry_steps[ step ] . second ; + return freqman_entry_steps[step].second; } -freqman_index_t freqman_entry_get_modulation_from_str( char *str ) -{ - if( !str ) - return -1 ; - for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_modulations . size() ; index ++ ) - { - if( strncmp( freqman_entry_modulations[ index ] . first . c_str() , str , freqman_entry_modulations[ index ] . first . size() ) == 0 ) - return index ; +freqman_index_t freqman_entry_get_modulation_from_str(char* str) { + if (!str) + return -1; + for (freqman_index_t index = 0; (unsigned)index < freqman_entry_modulations.size(); index++) { + if (strncmp(freqman_entry_modulations[index].first.c_str(), str, freqman_entry_modulations[index].first.size()) == 0) + return index; } - return -1 ; + return -1; } -freqman_index_t freqman_entry_get_bandwidth_from_str( freqman_index_t modulation , char *str ) -{ - if( !str ) - return -1 ; - if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() ) - return -1 ; - for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_bandwidths[ modulation ] . size() ; index ++ ) - { - if( strncmp( freqman_entry_bandwidths[ modulation ][ index ] . first . c_str() , str , freqman_entry_bandwidths[ modulation ][ index ] . first . size() ) == 0 ) - return index ; +freqman_index_t freqman_entry_get_bandwidth_from_str(freqman_index_t modulation, char* str) { + if (!str) + return -1; + if (modulation < 0 || (unsigned)modulation >= freqman_entry_modulations.size()) + return -1; + for (freqman_index_t index = 0; (unsigned)index < freqman_entry_bandwidths[modulation].size(); index++) { + if (strncmp(freqman_entry_bandwidths[modulation][index].first.c_str(), str, freqman_entry_bandwidths[modulation][index].first.size()) == 0) + return index; } - return -1 ; + return -1; } -freqman_index_t freqman_entry_get_step_from_str( char *str ) -{ - if( !str ) - return -1 ; - for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_steps . size() ; index ++ ) - { - if( strncmp( freqman_entry_steps[ index ] . first . c_str() , str , freqman_entry_steps[ index ] . first . size() ) == 0 ) - return index ; +freqman_index_t freqman_entry_get_step_from_str(char* str) { + if (!str) + return -1; + for (freqman_index_t index = 0; (unsigned)index < freqman_entry_steps.size(); index++) { + if (strncmp(freqman_entry_steps[index].first.c_str(), str, freqman_entry_steps[index].first.size()) == 0) + return index; } - return -1 ; + return -1; } -freqman_index_t freqman_entry_get_step_from_str_short( char *str ) -{ - if( !str ) - return -1 ; - for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_steps_short . size() ; index ++ ) - { - if( strncmp( freqman_entry_steps_short[ index ] . first . c_str() , str , freqman_entry_steps_short[ index ] . first . size() ) == 0 ) - return index ; +freqman_index_t freqman_entry_get_step_from_str_short(char* str) { + if (!str) + return -1; + for (freqman_index_t index = 0; (unsigned)index < freqman_entry_steps_short.size(); index++) { + if (strncmp(freqman_entry_steps_short[index].first.c_str(), str, freqman_entry_steps_short[index].first.size()) == 0) + return index; } - return -1 ; + return -1; } diff --git a/firmware/application/freqman.hpp b/firmware/application/freqman.hpp index 468873c82..73487c203 100644 --- a/firmware/application/freqman.hpp +++ b/firmware/application/freqman.hpp @@ -32,7 +32,7 @@ #include "ui_widget.hpp" #define FREQMAN_DESC_MAX_LEN 24 // This is the number of characters that can be drawn in front of "R: TEXT..." before taking a full screen line -#define FREQMAN_MAX_PER_FILE 115 // Maximum of entries we can read. This is a hardware limit +#define FREQMAN_MAX_PER_FILE 115 // Maximum of entries we can read. This is a hardware limit \ // It was tested and lowered to leave a bit of space to the caller #define FREQMAN_MAX_PER_FILE_STR "115" // STRING OF FREQMAN_MAX_PER_FILE @@ -41,85 +41,85 @@ using namespace std; using namespace tonekey; // needs to be signed as -1 means not set -typedef int8_t freqman_index_t ; +typedef int8_t freqman_index_t; enum freqman_error { - NO_ERROR = 0, - ERROR_ACCESS, - ERROR_NOFILES, - ERROR_DUPLICATE + NO_ERROR = 0, + ERROR_ACCESS, + ERROR_NOFILES, + ERROR_DUPLICATE }; enum freqman_entry_type { - SINGLE = 0, //f= - RANGE, //a=,b= - HAMRADIO, //r=,t= - ERROR_TYPE + SINGLE = 0, // f= + RANGE, // a=,b= + HAMRADIO, // r=,t= + ERROR_TYPE }; enum freqman_entry_modulation { - AM_MODULATION = 0 , - NFM_MODULATION , - WFM_MODULATION , - MODULATION_DEF , - ERROR_MODULATION + AM_MODULATION = 0, + NFM_MODULATION, + WFM_MODULATION, + MODULATION_DEF, + ERROR_MODULATION }; -//Entry step placed for AlainD freqman version (or any other enhanced version) +// Entry step placed for AlainD freqman version (or any other enhanced version) enum freqman_entry_step { - STEP_DEF = -1, // default - AM_US, // 10 kHz AM/CB - AM_EUR, // 9 kHz LW/MW - NFM_1, // 12,5 kHz (Analogic PMR 446) - NFM_2, // 6,25 kHz (Digital PMR 446) - FM_1, // 100 kHz - FM_2, // 50 kHz - N_1, // 25 kHz - N_2, // 250 kHz - AIRBAND, // AIRBAND 8,33 kHz - ERROR_STEP + STEP_DEF = -1, // default + AM_US, // 10 kHz AM/CB + AM_EUR, // 9 kHz LW/MW + NFM_1, // 12,5 kHz (Analogic PMR 446) + NFM_2, // 6,25 kHz (Digital PMR 446) + FM_1, // 100 kHz + FM_2, // 50 kHz + N_1, // 25 kHz + N_2, // 250 kHz + AIRBAND, // AIRBAND 8,33 kHz + ERROR_STEP }; struct freqman_entry { - rf::Frequency frequency_a { 0 }; // 'f=freq' or 'a=freq_start' or 'r=recv_freq' - rf::Frequency frequency_b { 0 }; // 'b=freq_end' or 't=tx_freq' - std::string description { }; // 'd=desc' - freqman_entry_type type { }; // SINGLE,RANGE,HAMRADIO - freqman_index_t modulation { }; // AM,NFM,WFM - freqman_index_t bandwidth { }; // AM_DSB, ... - freqman_index_t step { }; // 5khz (SA AM,... - tone_index tone { }; // 0XZ, 11 1ZB,... + rf::Frequency frequency_a{0}; // 'f=freq' or 'a=freq_start' or 'r=recv_freq' + rf::Frequency frequency_b{0}; // 'b=freq_end' or 't=tx_freq' + std::string description{}; // 'd=desc' + freqman_entry_type type{}; // SINGLE,RANGE,HAMRADIO + freqman_index_t modulation{}; // AM,NFM,WFM + freqman_index_t bandwidth{}; // AM_DSB, ... + freqman_index_t step{}; // 5khz (SA AM,... + tone_index tone{}; // 0XZ, 11 1ZB,... }; using freqman_db = std::vector; std::vector get_freqman_files(); bool load_freqman_file(std::string& file_stem, freqman_db& db); -bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs , bool load_ranges , bool load_hamradios ); -bool get_freq_string( freqman_entry &entry , std::string &item_string ); +bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs, bool load_ranges, bool load_hamradios); +bool get_freq_string(freqman_entry& entry, std::string& item_string); bool save_freqman_file(std::string& file_stem, freqman_db& db); bool create_freqman_file(std::string& file_stem, File& freqman_file); -std::string freqman_item_string(freqman_entry &item, size_t max_length); +std::string freqman_item_string(freqman_entry& item, size_t max_length); -void freqman_set_bandwidth_option( freqman_index_t modulation , OptionsField &option ); -void freqman_set_modulation_option( OptionsField &option ); -void freqman_set_step_option( OptionsField &option ); -void freqman_set_step_option_short( OptionsField &option ); -void freqman_set_tone_option( OptionsField &option ); +void freqman_set_bandwidth_option(freqman_index_t modulation, OptionsField& option); +void freqman_set_modulation_option(OptionsField& option); +void freqman_set_step_option(OptionsField& option); +void freqman_set_step_option_short(OptionsField& option); +void freqman_set_tone_option(OptionsField& option); -std::string freqman_entry_get_modulation_string( freqman_index_t modulation ); -std::string freqman_entry_get_bandwidth_string( freqman_index_t modulation , freqman_index_t bandwidth ); -std::string freqman_entry_get_step_string( freqman_index_t step ); -std::string freqman_entry_get_step_string_short( freqman_index_t step ); +std::string freqman_entry_get_modulation_string(freqman_index_t modulation); +std::string freqman_entry_get_bandwidth_string(freqman_index_t modulation, freqman_index_t bandwidth); +std::string freqman_entry_get_step_string(freqman_index_t step); +std::string freqman_entry_get_step_string_short(freqman_index_t step); -int32_t freqman_entry_get_modulation_value( freqman_index_t modulation ); -int32_t freqman_entry_get_bandwidth_value( freqman_index_t modulation , freqman_index_t bandwidth ); -int32_t freqman_entry_get_step_value( freqman_index_t step ); +int32_t freqman_entry_get_modulation_value(freqman_index_t modulation); +int32_t freqman_entry_get_bandwidth_value(freqman_index_t modulation, freqman_index_t bandwidth); +int32_t freqman_entry_get_step_value(freqman_index_t step); -freqman_index_t freqman_entry_get_modulation_from_str( char *str ); -freqman_index_t freqman_entry_get_bandwidth_from_str( freqman_index_t modulation , char *str ); -freqman_index_t freqman_entry_get_step_from_str( char *str ); -freqman_index_t freqman_entry_get_step_from_str_short( char *str ); +freqman_index_t freqman_entry_get_modulation_from_str(char* str); +freqman_index_t freqman_entry_get_bandwidth_from_str(freqman_index_t modulation, char* str); +freqman_index_t freqman_entry_get_step_from_str(char* str); +freqman_index_t freqman_entry_get_step_from_str_short(char* str); -#endif/*__FREQMAN_H__*/ +#endif /*__FREQMAN_H__*/ diff --git a/firmware/application/halconf.h b/firmware/application/halconf.h index ea3e98669..23af6593c 100755 --- a/firmware/application/halconf.h +++ b/firmware/application/halconf.h @@ -35,126 +35,126 @@ * @brief Enables the TM subsystem. */ #if !defined(HAL_USE_TM) || defined(__DOXYGEN__) -#define HAL_USE_TM FALSE +#define HAL_USE_TM FALSE #endif /** * @brief Enables the PAL subsystem. */ #if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) -#define HAL_USE_PAL TRUE +#define HAL_USE_PAL TRUE #endif /** * @brief Enables the ADC subsystem. */ #if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) -#define HAL_USE_ADC FALSE +#define HAL_USE_ADC FALSE #endif /** * @brief Enables the CAN subsystem. */ #if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) -#define HAL_USE_CAN FALSE +#define HAL_USE_CAN FALSE #endif /** * @brief Enables the EXT subsystem. */ #if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) -#define HAL_USE_EXT FALSE +#define HAL_USE_EXT FALSE #endif /** * @brief Enables the GPT subsystem. */ #if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) -#define HAL_USE_GPT TRUE +#define HAL_USE_GPT TRUE #endif /** * @brief Enables the I2C subsystem. */ #if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) -#define HAL_USE_I2C TRUE +#define HAL_USE_I2C TRUE #endif /** * @brief Enables the ICU subsystem. */ #if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) -#define HAL_USE_ICU FALSE +#define HAL_USE_ICU FALSE #endif /** * @brief Enables the MAC subsystem. */ #if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) -#define HAL_USE_MAC FALSE +#define HAL_USE_MAC FALSE #endif /** * @brief Enables the MMC_SPI subsystem. */ #if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) -#define HAL_USE_MMC_SPI FALSE +#define HAL_USE_MMC_SPI FALSE #endif /** * @brief Enables the PWM subsystem. */ #if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) -#define HAL_USE_PWM FALSE +#define HAL_USE_PWM FALSE #endif /** * @brief Enables the RTC subsystem. */ #if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) -#define HAL_USE_RTC TRUE +#define HAL_USE_RTC TRUE #endif /** * @brief Enables the SDC subsystem. */ #if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) -#define HAL_USE_SDC TRUE +#define HAL_USE_SDC TRUE #endif /** * @brief Enables the SERIAL subsystem. */ #if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) -#define HAL_USE_SERIAL FALSE +#define HAL_USE_SERIAL FALSE #endif /** * @brief Enables the SERIAL over USB subsystem. */ #if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) -#define HAL_USE_SERIAL_USB FALSE +#define HAL_USE_SERIAL_USB FALSE #endif /** * @brief Enables the SPI subsystem. */ #if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) -#define HAL_USE_SPI TRUE +#define HAL_USE_SPI TRUE #endif /** * @brief Enables the UART subsystem. */ #if !defined(HAL_USE_UART) || defined(__DOXYGEN__) -#define HAL_USE_UART FALSE +#define HAL_USE_UART FALSE #endif /** * @brief Enables the USB subsystem. */ #if !defined(HAL_USE_USB) || defined(__DOXYGEN__) -#define HAL_USE_USB FALSE +#define HAL_USE_USB FALSE #endif /*===========================================================================*/ @@ -166,7 +166,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) -#define ADC_USE_WAIT TRUE +#define ADC_USE_WAIT TRUE #endif /** @@ -174,7 +174,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define ADC_USE_MUTUAL_EXCLUSION TRUE +#define ADC_USE_MUTUAL_EXCLUSION TRUE #endif /*===========================================================================*/ @@ -185,7 +185,7 @@ * @brief Sleep mode related APIs inclusion switch. */ #if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) -#define CAN_USE_SLEEP_MODE TRUE +#define CAN_USE_SLEEP_MODE TRUE #endif /*===========================================================================*/ @@ -196,7 +196,7 @@ * @brief Enables the mutual exclusion APIs on the I2C bus. */ #if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define I2C_USE_MUTUAL_EXCLUSION TRUE +#define I2C_USE_MUTUAL_EXCLUSION TRUE #endif /*===========================================================================*/ @@ -207,14 +207,14 @@ * @brief Enables an event sources for incoming packets. */ #if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) -#define MAC_USE_ZERO_COPY FALSE +#define MAC_USE_ZERO_COPY FALSE #endif /** * @brief Enables an event sources for incoming packets. */ #if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) -#define MAC_USE_EVENTS TRUE +#define MAC_USE_EVENTS TRUE #endif /*===========================================================================*/ @@ -230,7 +230,7 @@ * use a DMA channel and heavily loads the CPU. */ #if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) -#define MMC_NICE_WAITING TRUE +#define MMC_NICE_WAITING TRUE #endif /*===========================================================================*/ @@ -242,7 +242,7 @@ * @note Attempts are performed at 10mS intervals. */ #if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) -#define SDC_INIT_RETRY 100 +#define SDC_INIT_RETRY 100 #endif /** @@ -251,7 +251,7 @@ * at @p FALSE. */ #if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) -#define SDC_MMC_SUPPORT FALSE +#define SDC_MMC_SUPPORT FALSE #endif /** @@ -261,7 +261,7 @@ * lower priority, this may slow down the driver a bit however. */ #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) -#define SDC_NICE_WAITING FALSE +#define SDC_NICE_WAITING FALSE #endif /*===========================================================================*/ @@ -274,7 +274,7 @@ * default configuration. */ #if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) -#define SERIAL_DEFAULT_BITRATE 38400 +#define SERIAL_DEFAULT_BITRATE 38400 #endif /** @@ -285,7 +285,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 16 #endif /*===========================================================================*/ @@ -297,7 +297,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) -#define SPI_USE_WAIT TRUE +#define SPI_USE_WAIT TRUE #endif /** @@ -305,7 +305,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define SPI_USE_MUTUAL_EXCLUSION TRUE +#define SPI_USE_MUTUAL_EXCLUSION TRUE #endif #endif /* _HALCONF_H_ */ diff --git a/firmware/application/hw/debounce.cpp b/firmware/application/hw/debounce.cpp index 8917542a8..2e338a102 100644 --- a/firmware/application/hw/debounce.cpp +++ b/firmware/application/hw/debounce.cpp @@ -24,16 +24,16 @@ #include "utility.hpp" bool Debounce::feed(const uint8_t bit) { - history_ = (history_ << 1) | (bit & 1); + history_ = (history_ << 1) | (bit & 1); - if( history_ == 0b00001111 ) { - state_ = 1; - return true; - } - if( history_ == 0b11110000 ) { - state_ = 0; - return true; - } + if (history_ == 0b00001111) { + state_ = 1; + return true; + } + if (history_ == 0b11110000) { + state_ = 0; + return true; + } - return false; + return false; } diff --git a/firmware/application/hw/debounce.hpp b/firmware/application/hw/debounce.hpp index 2196c3d15..0d2ba1cd7 100644 --- a/firmware/application/hw/debounce.hpp +++ b/firmware/application/hw/debounce.hpp @@ -25,16 +25,16 @@ #include class Debounce { -public: - bool feed(const uint8_t bit); - - uint8_t state() const { - return state_; - } + public: + bool feed(const uint8_t bit); -private: - uint8_t history_ { 0 }; - uint8_t state_ { 0 }; + uint8_t state() const { + return state_; + } + + private: + uint8_t history_{0}; + uint8_t state_{0}; }; -#endif/*__DEBOUNCE_H__*/ +#endif /*__DEBOUNCE_H__*/ diff --git a/firmware/application/hw/encoder.cpp b/firmware/application/hw/encoder.cpp index 8fac0f322..4a38953ba 100644 --- a/firmware/application/hw/encoder.cpp +++ b/firmware/application/hw/encoder.cpp @@ -24,32 +24,31 @@ #include "utility.hpp" static const int8_t transition_map[] = { - 0, // 0000: noop - 0, // 0001: start - 0, // 0010: start - 0, // 0011: rate - 1, // 0100: end - 0, // 0101: noop - 0, // 0110: rate - -1, // 0111: end - -1, // 1000: end - 0, // 1001: rate - 0, // 1010: noop - 1, // 1011: end - 0, // 1100: rate - 0, // 1101: start - 0, // 1110: start - 0, // 1111: noop + 0, // 0000: noop + 0, // 0001: start + 0, // 0010: start + 0, // 0011: rate + 1, // 0100: end + 0, // 0101: noop + 0, // 0110: rate + -1, // 0111: end + -1, // 1000: end + 0, // 1001: rate + 0, // 1010: noop + 1, // 1011: end + 0, // 1100: rate + 0, // 1101: start + 0, // 1110: start + 0, // 1111: noop }; int_fast8_t Encoder::update( - const uint_fast8_t phase_0, - const uint_fast8_t phase_1 -) { - state <<= 1; - state |= phase_0; - state <<= 1; - state |= phase_1; + const uint_fast8_t phase_0, + const uint_fast8_t phase_1) { + state <<= 1; + state |= phase_0; + state <<= 1; + state |= phase_1; - return transition_map[state & 0xf]; + return transition_map[state & 0xf]; } diff --git a/firmware/application/hw/encoder.hpp b/firmware/application/hw/encoder.hpp index 5283d5f68..3436e071d 100644 --- a/firmware/application/hw/encoder.hpp +++ b/firmware/application/hw/encoder.hpp @@ -25,14 +25,13 @@ #include class Encoder { -public: - int_fast8_t update( - const uint_fast8_t phase_0, - const uint_fast8_t phase_1 - ); + public: + int_fast8_t update( + const uint_fast8_t phase_0, + const uint_fast8_t phase_1); -private: - uint_fast8_t state { 0 }; + private: + uint_fast8_t state{0}; }; -#endif/*__ENCODER_H__*/ +#endif /*__ENCODER_H__*/ diff --git a/firmware/application/hw/max2837.cpp b/firmware/application/hw/max2837.cpp index dab751b93..08351d24a 100644 --- a/firmware/application/hw/max2837.cpp +++ b/firmware/application/hw/max2837.cpp @@ -38,14 +38,13 @@ namespace lna { using namespace max283x::lna; -constexpr std::array lookup_8db_steps { - 0b111, 0b011, 0b110, 0b010, - 0b100, 0b000, 0b000, 0b000 -}; +constexpr std::array lookup_8db_steps{ + 0b111, 0b011, 0b110, 0b010, + 0b100, 0b000, 0b000, 0b000}; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range.clip(db); - return lna::lookup_8db_steps[(db_sat >> 3) & 7]; + const auto db_sat = gain_db_range.clip(db); + return lna::lookup_8db_steps[(db_sat >> 3) & 7]; } } /* namespace lna */ @@ -55,8 +54,8 @@ namespace vga { using namespace max283x::vga; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range.clip(db); - return ((db_sat >> 1) & 0b11111) ^ 0b11111; + const auto db_sat = gain_db_range.clip(db); + return ((db_sat >> 1) & 0b11111) ^ 0b11111; } } /* namespace vga */ @@ -66,11 +65,11 @@ namespace tx { using namespace max283x::tx; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range.clip(db); - uint8_t value = db_sat & 0x0f; - value = (db_sat >= 16) ? (value | 0x20) : value; - value = (db_sat >= 32) ? (value | 0x10) : value; - return (value & 0b111111) ^ 0b111111; + const auto db_sat = gain_db_range.clip(db); + uint8_t value = db_sat & 0x0f; + value = (db_sat >= 16) ? (value | 0x20) : value; + value = (db_sat >= 32) ? (value | 0x10) : value; + return (value & 0b111111) ^ 0b111111; } } /* namespace tx */ @@ -80,10 +79,10 @@ namespace filter { using namespace max283x::filter; static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) { - /* Determine filter setting that will provide bandwidth greater than or - * equal to requested bandwidth. - */ - return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin(); + /* Determine filter setting that will provide bandwidth greater than or + * equal to requested bandwidth. + */ + return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin(); } } /* namespace filter */ @@ -98,36 +97,36 @@ constexpr uint32_t reference_frequency = max283x_reference_f; constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5; void MAX2837::init() { - set_mode(Mode::Shutdown); + set_mode(Mode::Shutdown); - gpio_max283x_enable.output(); - gpio_max2837_rxenable.output(); - gpio_max2837_txenable.output(); + gpio_max283x_enable.output(); + gpio_max2837_rxenable.output(); + gpio_max2837_txenable.output(); - _map.r.tx_gain.TXVGA_GAIN_SPI_EN = 1; - _map.r.tx_gain.TXVGA_GAIN_MSB_SPI_EN = 1; - _map.r.tx_gain.TXVGA_GAIN_SPI = 0x00; + _map.r.tx_gain.TXVGA_GAIN_SPI_EN = 1; + _map.r.tx_gain.TXVGA_GAIN_MSB_SPI_EN = 1; + _map.r.tx_gain.TXVGA_GAIN_SPI = 0x00; - _map.r.lpf_3_vga_1.VGAMUX_enable = 1; - _map.r.lpf_3_vga_1.VGA_EN = 1; + _map.r.lpf_3_vga_1.VGAMUX_enable = 1; + _map.r.lpf_3_vga_1.VGA_EN = 1; - _map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */ + _map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */ - _map.r.rx_top_rx_bias.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */ - _map.r.rxrf_2.L = 0b000; + _map.r.rx_top_rx_bias.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */ + _map.r.rxrf_2.L = 0b000; - _map.r.rx_top_rx_bias.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */ - _map.r.vga_2.VGA = 0b01010; + _map.r.rx_top_rx_bias.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */ + _map.r.vga_2.VGA = 0b01010; - _map.r.lpf_3_vga_1.BUFF_VCM = 0b00; /* TODO: Check values out of ADC */ + _map.r.lpf_3_vga_1.BUFF_VCM = 0b00; /* TODO: Check values out of ADC */ - _map.r.lpf_1.LPF_EN = 1; /* Enable low-pass filter */ - _map.r.lpf_1.ModeCtrl = 0b01; /* Rx LPF */ - _map.r.lpf_1.FT = 0b0000; /* 5MHz LPF */ + _map.r.lpf_1.LPF_EN = 1; /* Enable low-pass filter */ + _map.r.lpf_1.ModeCtrl = 0b01; /* Rx LPF */ + _map.r.lpf_1.FT = 0b0000; /* 5MHz LPF */ - _map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */ + _map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */ - _map.r.lo_gen.LOGEN_2GM = 0; + _map.r.lo_gen.LOGEN_2GM = 0; #if 0 _map.r.rxrf_1.LNA_EN = 1; @@ -141,184 +140,188 @@ void MAX2837::init() { _map.r.xtal_cfg.XTAL_CLKOUT_EN = 0; /* CLKOUT pin disabled. (Seems to have no effect.) */ #endif - _map.r.vga_3_rx_top.RSSI_EN_SPIenables = 1; - _map.r.vga_3_rx_top.RSSI_MODE = 1; /* RSSI independent of RXHP */ + _map.r.vga_3_rx_top.RSSI_EN_SPIenables = 1; + _map.r.vga_3_rx_top.RSSI_MODE = 1; /* RSSI independent of RXHP */ - _dirty.set(); - flush(); + _dirty.set(); + flush(); - set_mode(Mode::Standby); + set_mode(Mode::Standby); } enum class Mask { - Enable = 0b001, - RxEnable = 0b010, - TxEnable = 0b100, - Shutdown = 0b000, - Standby = Enable, - Receive = Enable | RxEnable, - Transmit = Enable | TxEnable, + Enable = 0b001, + RxEnable = 0b010, + TxEnable = 0b100, + Shutdown = 0b000, + Standby = Enable, + Receive = Enable | RxEnable, + Transmit = Enable | TxEnable, }; Mask mode_mask(const Mode mode) { - switch (mode) { - case Mode::Standby: return Mask::Standby; - case Mode::Receive: return Mask::Receive; - case Mode::Transmit: return Mask::Transmit; - default: return Mask::Shutdown; - } + switch (mode) { + case Mode::Standby: + return Mask::Standby; + case Mode::Receive: + return Mask::Receive; + case Mode::Transmit: + return Mask::Transmit; + default: + return Mask::Shutdown; + } } void MAX2837::set_mode(const Mode mode) { - Mask mask = mode_mask(mode); - gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable)); - gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable)); - gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable)); + Mask mask = mode_mask(mode); + gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable)); + gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable)); + gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable)); } void MAX2837::flush() { - if( _dirty ) { - for(size_t n=0; n> 20; - _dirty[Register::SYN_INT_DIV] = 1; - _map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff; - _dirty[Register::SYN_FR_DIV_2] = 1; - /* flush to commit high FRDIV first, as low FRDIV commits the change */ - flush(); - - _map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff); - _dirty[Register::SYN_FR_DIV_1] = 1; - flush(); - - return true; + /* TODO: This is a sad implementation. Refactor. */ + if (lo::band[0].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */ + _map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */ + } else if (lo::band[1].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */ + _map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */ + } else if (lo::band[2].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */ + _map.r.rxrf_1.LNAband = 1; /* 2.5 - 2.7GHz */ + } else if (lo::band[3].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */ + _map.r.rxrf_1.LNAband = 1; /* 2.5 - 2.7GHz */ + } else { + return false; + } + _dirty[Register::SYN_INT_DIV] = 1; + _dirty[Register::RXRF_1] = 1; + + const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor; + + _map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20; + _dirty[Register::SYN_INT_DIV] = 1; + _map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff; + _dirty[Register::SYN_FR_DIV_2] = 1; + /* flush to commit high FRDIV first, as low FRDIV commits the change */ + flush(); + + _map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff); + _dirty[Register::SYN_FR_DIV_1] = 1; + flush(); + + return true; } void MAX2837::set_rx_lo_iq_calibration(const size_t v) { - _map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; - _dirty[Register::RX_TOP_RX_BIAS] = 1; - _map.r.rxrf_2.iqerr_trim = v; - _dirty[Register::RXRF_2] = 1; - flush(); + _map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; + _dirty[Register::RX_TOP_RX_BIAS] = 1; + _map.r.rxrf_2.iqerr_trim = v; + _dirty[Register::RXRF_2] = 1; + flush(); } void MAX2837::set_rx_bias_trim(const size_t v) { - _map.r.rx_top_rx_bias.EN_Bias_Trim = 1; - _map.r.rx_top_rx_bias.BIAS_TRIM_SPI = v; - _dirty[Register::RX_TOP_RX_BIAS] = 1; - flush(); + _map.r.rx_top_rx_bias.EN_Bias_Trim = 1; + _map.r.rx_top_rx_bias.BIAS_TRIM_SPI = v; + _dirty[Register::RX_TOP_RX_BIAS] = 1; + flush(); } void MAX2837::set_vco_bias(const size_t v) { - _map.r.vco_cfg.VCO_BIAS_SPI_EN = 1; - _map.r.vco_cfg.VCO_BIAS_SPI = v; - _dirty[Register::VCO_CFG] = 1; - flush(); + _map.r.vco_cfg.VCO_BIAS_SPI_EN = 1; + _map.r.vco_cfg.VCO_BIAS_SPI = v; + _dirty[Register::VCO_CFG] = 1; + flush(); } void MAX2837::set_rx_buff_vcm(const size_t v) { - _map.r.lpf_3_vga_1.BUFF_VCM = v; - _dirty[Register::LPF_3_VGA_1] = 1; - flush(); + _map.r.lpf_3_vga_1.BUFF_VCM = v; + _dirty[Register::LPF_3_VGA_1] = 1; + flush(); } reg_t MAX2837::temp_sense() { - if( !_map.r.rx_top.ts_en ) { - _map.r.rx_top.ts_en = 1; - flush_one(Register::RX_TOP); + if (!_map.r.rx_top.ts_en) { + _map.r.rx_top.ts_en = 1; + flush_one(Register::RX_TOP); - chThdSleepMilliseconds(1); - } + chThdSleepMilliseconds(1); + } - _map.r.rx_top.ts_adc_trigger = 1; - flush_one(Register::RX_TOP); + _map.r.rx_top.ts_adc_trigger = 1; + flush_one(Register::RX_TOP); - halPolledDelay(ticks_for_temperature_sense_adc_conversion); + halPolledDelay(ticks_for_temperature_sense_adc_conversion); - const auto value = read(Register::TEMP_SENSE); + const auto value = read(Register::TEMP_SENSE); - _map.r.rx_top.ts_adc_trigger = 0; - flush_one(Register::RX_TOP); + _map.r.rx_top.ts_adc_trigger = 0; + flush_one(Register::RX_TOP); - return value; + return value; } -} +} // namespace max2837 diff --git a/firmware/application/hw/max2837.hpp b/firmware/application/hw/max2837.hpp index b62447d38..3fd060f2a 100644 --- a/firmware/application/hw/max2837.hpp +++ b/firmware/application/hw/max2837.hpp @@ -39,721 +39,751 @@ using namespace max283x; constexpr size_t reg_count = 32; enum class Register : address_t { - RXRF_1 = 0, - RXRF_2 = 1, - LPF_1 = 2, - LPF_2 = 3, - LPF_3_VGA_1 = 4, - VGA_2 = 5, - VGA_3_RX_TOP = 6, - TEMP_SENSE = 7, - RX_TOP_RX_BIAS = 8, - RX_TOP = 9, - TX_TOP_1 = 10, - TX_TOP_2 = 11, - HPFSM_1 = 12, - HPFSM_2 = 13, - HPFSM_3 = 14, - HPFSM_4 = 15, - SPI_EN = 16, - SYN_FR_DIV_1 = 17, - SYN_FR_DIV_2 = 18, - SYN_INT_DIV = 19, - SYN_CFG_1 = 20, - SYN_CFG_2 = 21, - VAS_CFG = 22, - LO_MISC = 23, - XTAL_CFG = 24, - VCO_CFG = 25, - LO_GEN = 26, - PA_DRV_PA_DAC = 27, - PA_DAC = 28, - TX_GAIN = 29, - TX_LO_IQ = 30, - TX_DC_CORR = 31, + RXRF_1 = 0, + RXRF_2 = 1, + LPF_1 = 2, + LPF_2 = 3, + LPF_3_VGA_1 = 4, + VGA_2 = 5, + VGA_3_RX_TOP = 6, + TEMP_SENSE = 7, + RX_TOP_RX_BIAS = 8, + RX_TOP = 9, + TX_TOP_1 = 10, + TX_TOP_2 = 11, + HPFSM_1 = 12, + HPFSM_2 = 13, + HPFSM_3 = 14, + HPFSM_4 = 15, + SPI_EN = 16, + SYN_FR_DIV_1 = 17, + SYN_FR_DIV_2 = 18, + SYN_INT_DIV = 19, + SYN_CFG_1 = 20, + SYN_CFG_2 = 21, + VAS_CFG = 22, + LO_MISC = 23, + XTAL_CFG = 24, + VCO_CFG = 25, + LO_GEN = 26, + PA_DRV_PA_DAC = 27, + PA_DAC = 28, + TX_GAIN = 29, + TX_LO_IQ = 30, + TX_DC_CORR = 31, }; struct RXRF_1_Type { - reg_t LNA_EN : 1; - reg_t Mixer_EN : 1; - reg_t RxLO_EN : 1; - reg_t Lbias : 2; - reg_t Mbias : 2; - reg_t buf : 2; - reg_t LNAband : 1; - reg_t RESERVED0 : 6; + reg_t LNA_EN : 1; + reg_t Mixer_EN : 1; + reg_t RxLO_EN : 1; + reg_t Lbias : 2; + reg_t Mbias : 2; + reg_t buf : 2; + reg_t LNAband : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size"); struct RXRF_2_Type { - reg_t LNAtune : 1; - reg_t LNAde_Q : 1; - reg_t L : 3; - reg_t iqerr_trim : 5; - reg_t RESERVED0 : 6; + reg_t LNAtune : 1; + reg_t LNAde_Q : 1; + reg_t L : 3; + reg_t iqerr_trim : 5; + reg_t RESERVED0 : 6; }; static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size"); struct LPF_1_Type { - reg_t LPF_EN : 1; - reg_t TxBB_EN : 1; - reg_t ModeCtrl : 2; - reg_t FT : 4; - reg_t dF : 2; - reg_t RESERVED0 : 6; + reg_t LPF_EN : 1; + reg_t TxBB_EN : 1; + reg_t ModeCtrl : 2; + reg_t FT : 4; + reg_t dF : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LPF_1_Type) == sizeof(reg_t), "LPF_1_Type wrong size"); struct LPF_2_Type { - reg_t PT_SPI : 4; - reg_t Bqd : 3; - reg_t TxRPCM : 3; - reg_t RESERVED0 : 6; + reg_t PT_SPI : 4; + reg_t Bqd : 3; + reg_t TxRPCM : 3; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LPF_2_Type) == sizeof(reg_t), "LPF_2_Type wrong size"); struct LPF_3_VGA_1_Type { - reg_t RP : 2; - reg_t TxBuff : 2; - reg_t VGA_EN : 1; - reg_t VGAMUX_enable : 1; - reg_t BUFF_Curr : 2; - reg_t BUFF_VCM : 2; - reg_t RESERVED0 : 6; + reg_t RP : 2; + reg_t TxBuff : 2; + reg_t VGA_EN : 1; + reg_t VGAMUX_enable : 1; + reg_t BUFF_Curr : 2; + reg_t BUFF_VCM : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LPF_3_VGA_1_Type) == sizeof(reg_t), "LPF_3_VGA_1_Type wrong size"); struct VGA_2_Type { - reg_t VGA : 5; - reg_t sel_In1_In2 : 1; - reg_t turbo15n20 : 1; - reg_t VGA_Curr : 2; - reg_t fuse_arm : 1; - reg_t RESERVED0 : 6; + reg_t VGA : 5; + reg_t sel_In1_In2 : 1; + reg_t turbo15n20 : 1; + reg_t VGA_Curr : 2; + reg_t fuse_arm : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(VGA_2_Type) == sizeof(reg_t), "VGA_2_Type wrong size"); struct VGA_3_RX_TOP_Type { - reg_t RESERVED0 : 6; - reg_t RSSI_EN_SPIenables : 1; - reg_t RSSI_MUX : 1; - reg_t RSSI_MODE : 1; - reg_t LPF_MODE_SEL : 1; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 6; + reg_t RSSI_EN_SPIenables : 1; + reg_t RSSI_MUX : 1; + reg_t RSSI_MODE : 1; + reg_t LPF_MODE_SEL : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(VGA_3_RX_TOP_Type) == sizeof(reg_t), "VGA_3_RX_TOP_Type wrong size"); struct TEMP_SENSE_Type { - reg_t ts_adc : 5; - reg_t RESERVED0 : 1; - reg_t PLL_test_output : 1; - reg_t VAS_test_output : 1; - reg_t HPFSM_test_output : 1; - reg_t LOGEN_trim_divider_test_output : 1; - reg_t RESERVED1 : 6; + reg_t ts_adc : 5; + reg_t RESERVED0 : 1; + reg_t PLL_test_output : 1; + reg_t VAS_test_output : 1; + reg_t HPFSM_test_output : 1; + reg_t LOGEN_trim_divider_test_output : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size"); struct RX_TOP_RX_BIAS_Type { - reg_t LNAgain_SPI_EN : 1; - reg_t VGAgain_SPI_EN : 1; - reg_t EN_Bias_Trim : 1; - reg_t BIAS_TRIM_SPI : 5; - reg_t BIAS_TRIM_CNTRL : 1; - reg_t RX_IQERR_SPI_EN : 1; - reg_t RESERVED0 : 6; + reg_t LNAgain_SPI_EN : 1; + reg_t VGAgain_SPI_EN : 1; + reg_t EN_Bias_Trim : 1; + reg_t BIAS_TRIM_SPI : 5; + reg_t BIAS_TRIM_CNTRL : 1; + reg_t RX_IQERR_SPI_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(RX_TOP_RX_BIAS_Type) == sizeof(reg_t), "RX_TOP_RX_BIAS_Type wrong size"); struct RX_TOP_Type { - reg_t ts_adc_trigger : 1; - reg_t ts_en : 1; - reg_t LPFtrim_SPI_EN : 1; - reg_t DOUT_DRVH : 1; - reg_t DOUT_PU : 1; - reg_t DOUT_SEL : 3; - reg_t fuse_th : 1; - reg_t fuse_burn_gkt : 1; - reg_t RESERVED0 : 6; + reg_t ts_adc_trigger : 1; + reg_t ts_en : 1; + reg_t LPFtrim_SPI_EN : 1; + reg_t DOUT_DRVH : 1; + reg_t DOUT_PU : 1; + reg_t DOUT_SEL : 3; + reg_t fuse_th : 1; + reg_t fuse_burn_gkt : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(RX_TOP_Type) == sizeof(reg_t), "RX_TOP_Type wrong size"); struct TX_TOP_1_Type { - reg_t RESERVED0 : 1; - reg_t TXCAL_GAIN : 2; - reg_t TXCAL_V2I_FILT : 3; - reg_t TX_BIAS_ADJ : 2; - reg_t RESERVED1 : 2; - reg_t RESERVED2 : 6; + reg_t RESERVED0 : 1; + reg_t TXCAL_GAIN : 2; + reg_t TXCAL_V2I_FILT : 3; + reg_t TX_BIAS_ADJ : 2; + reg_t RESERVED1 : 2; + reg_t RESERVED2 : 6; }; static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size"); struct TX_TOP_2_Type { - reg_t AMD_SPI_EN : 1; - reg_t TXMXR_V2I_GAIN : 4; - reg_t RESERVED0 : 5; - reg_t RESERVED1 : 6; + reg_t AMD_SPI_EN : 1; + reg_t TXMXR_V2I_GAIN : 4; + reg_t RESERVED0 : 5; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TX_TOP_2_Type) == sizeof(reg_t), "TX_TOP_2_Type wrong size"); struct HPFSM_1_Type { - reg_t HPC_10M : 2; - reg_t HPC_10M_GAIN : 2; - reg_t HPC_600k : 3; - reg_t HPC_600k_GAIN : 3; - reg_t RESERVED0 : 6; + reg_t HPC_10M : 2; + reg_t HPC_10M_GAIN : 2; + reg_t HPC_600k : 3; + reg_t HPC_600k_GAIN : 3; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size"); struct HPFSM_2_Type { - reg_t HPC_100k : 2; - reg_t HPC_100k_GAIN : 2; - reg_t HPC_30k : 2; - reg_t HPC_30k_GAIN : 2; - reg_t HPC_1k : 2; - reg_t RESERVED0 : 6; + reg_t HPC_100k : 2; + reg_t HPC_100k_GAIN : 2; + reg_t HPC_30k : 2; + reg_t HPC_30k_GAIN : 2; + reg_t HPC_1k : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size"); struct HPFSM_3_Type { - reg_t HPC_1k_GAIN : 2; - reg_t HPC_DELAY : 2; - reg_t HPC_STOP : 2; - reg_t HPC_STOP_M2 : 2; - reg_t HPC_RXGAIN_EN : 1; - reg_t HPC_MODE : 1; - reg_t RESERVED0 : 6; + reg_t HPC_1k_GAIN : 2; + reg_t HPC_DELAY : 2; + reg_t HPC_STOP : 2; + reg_t HPC_STOP_M2 : 2; + reg_t HPC_RXGAIN_EN : 1; + reg_t HPC_MODE : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size"); struct HPFSM_4_Type { - reg_t HPC_DIVH : 1; - reg_t HPC_TST : 5; - reg_t HPC_SEQ_BYP : 1; - reg_t DOUT_CSB_SEL : 1; - reg_t RESERVED0 : 2; - reg_t RESERVED1 : 6; + reg_t HPC_DIVH : 1; + reg_t HPC_TST : 5; + reg_t HPC_SEQ_BYP : 1; + reg_t DOUT_CSB_SEL : 1; + reg_t RESERVED0 : 2; + reg_t RESERVED1 : 6; }; static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size"); struct SPI_EN_Type { - reg_t EN_SPI : 1; - reg_t CAL_SPI : 1; - reg_t LOGEN_SPI_EN : 1; - reg_t SYN_SPI_EN : 1; - reg_t VAS_SPI_EN : 1; - reg_t PADRV_SPI_EN : 1; - reg_t PADAC_SPI_EN : 1; - reg_t PADAC_TX_EN : 1; - reg_t TXMX_SPI_EN : 1; - reg_t TXLO_SPI_EN : 1; - reg_t RESERVED0 : 6; + reg_t EN_SPI : 1; + reg_t CAL_SPI : 1; + reg_t LOGEN_SPI_EN : 1; + reg_t SYN_SPI_EN : 1; + reg_t VAS_SPI_EN : 1; + reg_t PADRV_SPI_EN : 1; + reg_t PADAC_SPI_EN : 1; + reg_t PADAC_TX_EN : 1; + reg_t TXMX_SPI_EN : 1; + reg_t TXLO_SPI_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size"); struct SYN_FR_DIV_1_Type { - reg_t SYN_FRDIV_9_0 : 10; - reg_t RESERVED0 : 6; + reg_t SYN_FRDIV_9_0 : 10; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size"); struct SYN_FR_DIV_2_Type { - reg_t SYN_FRDIV_19_10 : 10; - reg_t RESERVED0 : 6; + reg_t SYN_FRDIV_19_10 : 10; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size"); struct SYN_INT_DIV_Type { - reg_t SYN_INTDIV : 8; - reg_t LOGEN_BSW : 2; - reg_t RESERVED0 : 6; + reg_t SYN_INTDIV : 8; + reg_t LOGEN_BSW : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size"); struct SYN_CFG_1_Type { - reg_t SYN_MODE_FR_EN : 1; - reg_t SYN_REF_DIV_RATIO : 2; - reg_t SYN_CP_CURRENT : 2; - reg_t SYN_CLOCKOUT_DRIVE : 1; - reg_t SYN_TURBO_EN : 1; - reg_t CP_TRM_SET : 1; - reg_t CP_TRM_CODE : 2; - reg_t RESERVED0 : 6; + reg_t SYN_MODE_FR_EN : 1; + reg_t SYN_REF_DIV_RATIO : 2; + reg_t SYN_CP_CURRENT : 2; + reg_t SYN_CLOCKOUT_DRIVE : 1; + reg_t SYN_TURBO_EN : 1; + reg_t CP_TRM_SET : 1; + reg_t CP_TRM_CODE : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size"); struct SYN_CFG_2_Type { - reg_t SYN_CP_COMMON_MODE_EN : 1; - reg_t SYN_PRESCALER_BIAS_BOOST : 1; - reg_t SYN_CP_BETA_CURRENT_COMP_EN : 1; - reg_t SYN_SD_CLK_SEL : 1; - reg_t SYN_CP_PW_ADJ : 1; - reg_t SYN_CP_LIN_CURRENT_SEL : 2; - reg_t SYN_TEST_OUT_SEL : 3; - reg_t RESERVED0 : 6; + reg_t SYN_CP_COMMON_MODE_EN : 1; + reg_t SYN_PRESCALER_BIAS_BOOST : 1; + reg_t SYN_CP_BETA_CURRENT_COMP_EN : 1; + reg_t SYN_SD_CLK_SEL : 1; + reg_t SYN_CP_PW_ADJ : 1; + reg_t SYN_CP_LIN_CURRENT_SEL : 2; + reg_t SYN_TEST_OUT_SEL : 3; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size"); struct VAS_CFG_Type { - reg_t VAS_MODE : 1; - reg_t VAS_RELOCK_SEL : 1; - reg_t VAS_DIV : 3; - reg_t VAS_DLY : 2; - reg_t VAS_TRIG_EN : 1; - reg_t VAS_ADE : 1; - reg_t VAS_ADL_SPI : 1; - reg_t RESERVED0 : 6; + reg_t VAS_MODE : 1; + reg_t VAS_RELOCK_SEL : 1; + reg_t VAS_DIV : 3; + reg_t VAS_DLY : 2; + reg_t VAS_TRIG_EN : 1; + reg_t VAS_ADE : 1; + reg_t VAS_ADL_SPI : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size"); struct LO_MISC_Type { - reg_t VAS_SPI : 5; - reg_t XTAL_BIAS_SEL : 2; - reg_t XTAL_E2C_BIAS_SEL : 1; - reg_t VAS_SE : 1; - reg_t VCO_SPI_EN : 1; - reg_t RESERVED0 : 6; + reg_t VAS_SPI : 5; + reg_t XTAL_BIAS_SEL : 2; + reg_t XTAL_E2C_BIAS_SEL : 1; + reg_t VAS_SE : 1; + reg_t VCO_SPI_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size"); struct XTAL_CFG_Type { - reg_t XTAL_FTUNE : 7; - reg_t XTAL_CLKOUT_EN : 1; - reg_t XTAL_CLKOUT_DIV : 1; - reg_t XTAL_CORE_EN : 1; - reg_t RESERVED0 : 6; + reg_t XTAL_FTUNE : 7; + reg_t XTAL_CLKOUT_EN : 1; + reg_t XTAL_CLKOUT_DIV : 1; + reg_t XTAL_CORE_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size"); struct VCO_CFG_Type { - reg_t VCO_BIAS_SPI_EN : 1; - reg_t VCO_BIAS_SPI : 4; - reg_t VCO_CMEN : 1; - reg_t VCO_PDET_TST : 2; - reg_t VCO_BUF_BIASH : 2; - reg_t RESERVED0 : 6; + reg_t VCO_BIAS_SPI_EN : 1; + reg_t VCO_BIAS_SPI : 4; + reg_t VCO_CMEN : 1; + reg_t VCO_PDET_TST : 2; + reg_t VCO_BUF_BIASH : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size"); struct LO_GEN_Type { - reg_t LOGEN_BIASH1 : 2; - reg_t LOGEN_BIASH2 : 1; - reg_t LOGEN_2GM : 1; - reg_t LOGEN_TRIM1 : 1; - reg_t LOGEN_TRIM2 : 1; - reg_t VAS_TST : 4; - reg_t RESERVED0 : 6; + reg_t LOGEN_BIASH1 : 2; + reg_t LOGEN_BIASH2 : 1; + reg_t LOGEN_2GM : 1; + reg_t LOGEN_TRIM1 : 1; + reg_t LOGEN_TRIM2 : 1; + reg_t VAS_TST : 4; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size"); struct PA_DRV_PA_DAC_Type { - reg_t PADRV_BIAS : 3; - reg_t PADRV_DOWN_SPI_EN : 1; - reg_t PADRV_DOWN_SPI_SEL : 1; - reg_t PADAC_IV : 1; - reg_t PADAC_VMODE : 1; - reg_t PADAC_DIVH : 1; - reg_t TXGATE_EN : 1; - reg_t TX_DCCORR_EN : 1; - reg_t RESERVED0 : 6; + reg_t PADRV_BIAS : 3; + reg_t PADRV_DOWN_SPI_EN : 1; + reg_t PADRV_DOWN_SPI_SEL : 1; + reg_t PADAC_IV : 1; + reg_t PADAC_VMODE : 1; + reg_t PADAC_DIVH : 1; + reg_t TXGATE_EN : 1; + reg_t TX_DCCORR_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(PA_DRV_PA_DAC_Type) == sizeof(reg_t), "PA_DRV_PA_DAC_Type wrong size"); struct PA_DAC_Type { - reg_t PADAC_BIAS : 6; - reg_t PADAC_DLY : 4; - reg_t RESERVED0 : 6; + reg_t PADAC_BIAS : 6; + reg_t PADAC_DLY : 4; + reg_t RESERVED0 : 6; }; static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size"); struct TX_GAIN_Type { - reg_t TXVGA_GAIN_SPI_EN : 1; - reg_t TXVGA_GAIN_MSB_SPI_EN : 1; - reg_t TX_DCCORR_SPI_EN : 1; - reg_t FUSE_ARM : 1; - reg_t TXVGA_GAIN_SPI : 6; - reg_t RESERVED0 : 6; + reg_t TXVGA_GAIN_SPI_EN : 1; + reg_t TXVGA_GAIN_MSB_SPI_EN : 1; + reg_t TX_DCCORR_SPI_EN : 1; + reg_t FUSE_ARM : 1; + reg_t TXVGA_GAIN_SPI : 6; + reg_t RESERVED0 : 6; }; static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size"); struct TX_LO_IQ_Type { - reg_t TXLO_IQ_SPI : 5; - reg_t TXLO_IQ_SPI_EN : 1; - reg_t TXLO_BUFF : 2; - reg_t FUSE_GKT : 1; - reg_t FUSE_RTH : 1; - reg_t RESERVED0 : 6; + reg_t TXLO_IQ_SPI : 5; + reg_t TXLO_IQ_SPI_EN : 1; + reg_t TXLO_BUFF : 2; + reg_t FUSE_GKT : 1; + reg_t FUSE_RTH : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size"); struct TX_DC_CORR_Type { - reg_t TX_DCCORR_I : 5; - reg_t TX_DCCORR_Q : 5; - reg_t RESERVED0 : 6; + reg_t TX_DCCORR_I : 5; + reg_t TX_DCCORR_Q : 5; + reg_t RESERVED0 : 6; }; static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size"); struct Register_Type { - RXRF_1_Type rxrf_1; /* 0 */ - RXRF_2_Type rxrf_2; - LPF_1_Type lpf_1; - LPF_2_Type lpf_2; - LPF_3_VGA_1_Type lpf_3_vga_1; /* 4 */ - VGA_2_Type vga_2; - VGA_3_RX_TOP_Type vga_3_rx_top; - TEMP_SENSE_Type temp_sense; - RX_TOP_RX_BIAS_Type rx_top_rx_bias; /* 8 */ - RX_TOP_Type rx_top; - TX_TOP_1_Type tx_top_1; - TX_TOP_2_Type tx_top_2; - HPFSM_1_Type hpfsm_1; /* 12 */ - HPFSM_2_Type hpfsm_2; - HPFSM_3_Type hpfsm_3; - HPFSM_4_Type hpfsm_4; - SPI_EN_Type spi_en; /* 16 */ - SYN_FR_DIV_1_Type syn_fr_div_1; - SYN_FR_DIV_2_Type syn_fr_div_2; - SYN_INT_DIV_Type syn_int_div; - SYN_CFG_1_Type syn_cfg_1; /* 20 */ - SYN_CFG_2_Type syn_cfg_2; - VAS_CFG_Type vas_cfg; - LO_MISC_Type lo_misc; - XTAL_CFG_Type xtal_cfg; /* 24 */ - VCO_CFG_Type vco_cfg; - LO_GEN_Type lo_gen; - PA_DRV_PA_DAC_Type pa_drv_pa_dac; - PA_DAC_Type pa_dac; /* 28 */ - TX_GAIN_Type tx_gain; - TX_LO_IQ_Type tx_lo_iq; - TX_DC_CORR_Type tx_dc_corr; + RXRF_1_Type rxrf_1; /* 0 */ + RXRF_2_Type rxrf_2; + LPF_1_Type lpf_1; + LPF_2_Type lpf_2; + LPF_3_VGA_1_Type lpf_3_vga_1; /* 4 */ + VGA_2_Type vga_2; + VGA_3_RX_TOP_Type vga_3_rx_top; + TEMP_SENSE_Type temp_sense; + RX_TOP_RX_BIAS_Type rx_top_rx_bias; /* 8 */ + RX_TOP_Type rx_top; + TX_TOP_1_Type tx_top_1; + TX_TOP_2_Type tx_top_2; + HPFSM_1_Type hpfsm_1; /* 12 */ + HPFSM_2_Type hpfsm_2; + HPFSM_3_Type hpfsm_3; + HPFSM_4_Type hpfsm_4; + SPI_EN_Type spi_en; /* 16 */ + SYN_FR_DIV_1_Type syn_fr_div_1; + SYN_FR_DIV_2_Type syn_fr_div_2; + SYN_INT_DIV_Type syn_int_div; + SYN_CFG_1_Type syn_cfg_1; /* 20 */ + SYN_CFG_2_Type syn_cfg_2; + VAS_CFG_Type vas_cfg; + LO_MISC_Type lo_misc; + XTAL_CFG_Type xtal_cfg; /* 24 */ + VCO_CFG_Type vco_cfg; + LO_GEN_Type lo_gen; + PA_DRV_PA_DAC_Type pa_drv_pa_dac; + PA_DAC_Type pa_dac; /* 28 */ + TX_GAIN_Type tx_gain; + TX_LO_IQ_Type tx_lo_iq; + TX_DC_CORR_Type tx_dc_corr; }; static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size"); struct RegisterMap { - constexpr RegisterMap( - Register_Type values - ) : r(values) - { - } + constexpr RegisterMap( + Register_Type values) + : r(values) { + } - union { - Register_Type r; - std::array w; - }; + union { + Register_Type r; + std::array w; + }; }; static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size"); -constexpr RegisterMap initial_register_values { Register_Type { - /* Best effort to reconcile default values specified in three - * different places in the MAX2837 documentation. - */ - .rxrf_1 = { /* 0 */ - .LNA_EN = 0, - .Mixer_EN = 0, - .RxLO_EN = 0, - .Lbias = 0b10, - .Mbias = 0b10, - .buf = 0b10, - .LNAband = 0, - .RESERVED0 = 0, - }, - .rxrf_2 = { /* 1 */ - .LNAtune = 0, - .LNAde_Q = 1, - .L = 0b000, - .iqerr_trim = 0b00000, - .RESERVED0 = 0, - }, - .lpf_1 = { /* 2 */ - .LPF_EN = 0, - .TxBB_EN = 0, - .ModeCtrl = 0b01, - .FT = 0b1111, - .dF = 0b01, - .RESERVED0 = 0, - }, - .lpf_2 = { /* 3 */ - .PT_SPI = 0b1001, - .Bqd = 0b011, - .TxRPCM = 0b011, - .RESERVED0 = 0, - }, - .lpf_3_vga_1 = { /* 4 */ - .RP = 0b10, - .TxBuff = 0b10, - .VGA_EN = 0, - .VGAMUX_enable = 0, - .BUFF_Curr = 0b00, - .BUFF_VCM = 0b00, - .RESERVED0 = 0, - }, - .vga_2 = { /* 5 */ - .VGA = 0b00000, - .sel_In1_In2 = 0, - .turbo15n20 = 0, - .VGA_Curr = 0b01, - .fuse_arm = 0, - .RESERVED0 = 0, - }, - .vga_3_rx_top = { /* 6 */ - .RESERVED0 = 0b000110, - .RSSI_EN_SPIenables = 0, - .RSSI_MUX = 0, - .RSSI_MODE = 0, - .LPF_MODE_SEL = 0, - .RESERVED1 = 0, - }, - .temp_sense = { /* 7 */ - .ts_adc = 0b00000, - .RESERVED0 = 0, - .PLL_test_output = 0, - .VAS_test_output = 0, - .HPFSM_test_output = 0, - .LOGEN_trim_divider_test_output = 0, - .RESERVED1 = 0, - }, - .rx_top_rx_bias = { /* 8 */ - .LNAgain_SPI_EN = 0, - .VGAgain_SPI_EN = 0, - .EN_Bias_Trim = 0, - .BIAS_TRIM_SPI = 0b10000, - .BIAS_TRIM_CNTRL = 0, - .RX_IQERR_SPI_EN = 0, - .RESERVED0 = 0, - }, - .rx_top = { /* 9 */ - .ts_adc_trigger = 0, - .ts_en = 0, - .LPFtrim_SPI_EN = 0, - .DOUT_DRVH = 1, /* Documentation mismatch */ - .DOUT_PU = 1, - .DOUT_SEL = 0b000, - .fuse_th = 0, - .fuse_burn_gkt = 0, - .RESERVED0 = 0, - }, - .tx_top_1 = { /* 10 */ - .RESERVED0 = 0, - .TXCAL_GAIN = 0b00, - .TXCAL_V2I_FILT = 0b011, - .TX_BIAS_ADJ = 0b01, - .RESERVED1 = 0b00, - .RESERVED2 = 0, - }, - .tx_top_2 = { /* 11 */ - .AMD_SPI_EN = 0, - .TXMXR_V2I_GAIN = 0b1011, - .RESERVED0 = 0b00000, - .RESERVED1 = 0, - }, - .hpfsm_1 = { /* 12 */ - .HPC_10M = 0b11, - .HPC_10M_GAIN = 0b11, - .HPC_600k = 0b100, - .HPC_600k_GAIN = 0b100, - .RESERVED0 = 0, - }, - .hpfsm_2 = { /* 13 */ - .HPC_100k = 0b00, - .HPC_100k_GAIN = 0b00, - .HPC_30k = 0b01, - .HPC_30k_GAIN = 0b01, - .HPC_1k = 0b01, - .RESERVED0 = 0, - }, - .hpfsm_3 = { /* 14 */ - .HPC_1k_GAIN = 0b01, - .HPC_DELAY = 0b01, - .HPC_STOP = 0b00, - .HPC_STOP_M2 = 0b11, - .HPC_RXGAIN_EN = 1, - .HPC_MODE = 0, - .RESERVED0 = 0, - }, - .hpfsm_4 = { /* 15 */ - .HPC_DIVH = 1, - .HPC_TST = 0b00000, - .HPC_SEQ_BYP = 0, - .DOUT_CSB_SEL = 1, /* Documentation mismatch */ - .RESERVED0 = 0b00, - .RESERVED1 = 0, - }, - .spi_en = { /* 16 */ - .EN_SPI = 0, - .CAL_SPI = 0, - .LOGEN_SPI_EN = 1, - .SYN_SPI_EN = 1, - .VAS_SPI_EN = 1, - .PADRV_SPI_EN = 0, - .PADAC_SPI_EN = 0, - .PADAC_TX_EN = 0, - .TXMX_SPI_EN = 0, - .TXLO_SPI_EN = 0, - .RESERVED0 = 0, - }, - .syn_fr_div_1 = { /* 17 */ - .SYN_FRDIV_9_0 = 0b0101010101, - .RESERVED0 = 0, - }, - .syn_fr_div_2 = { /* 18 */ - .SYN_FRDIV_19_10 = 0b0101010101, - .RESERVED0 = 0, - }, - .syn_int_div = { /* 19 */ - .SYN_INTDIV = 0b01010011, - .LOGEN_BSW = 0b01, - .RESERVED0 = 0, - }, - .syn_cfg_1 = { /* 20 */ - .SYN_MODE_FR_EN = 1, - .SYN_REF_DIV_RATIO = 0b00, - .SYN_CP_CURRENT = 0b00, - .SYN_CLOCKOUT_DRIVE = 0, - .SYN_TURBO_EN = 1, - .CP_TRM_SET = 0, - .CP_TRM_CODE = 0b10, - .RESERVED0 = 0, - }, - .syn_cfg_2 = { /* 21 */ - .SYN_CP_COMMON_MODE_EN = 1, - .SYN_PRESCALER_BIAS_BOOST = 0, - .SYN_CP_BETA_CURRENT_COMP_EN = 1, - .SYN_SD_CLK_SEL = 1, - .SYN_CP_PW_ADJ = 0, - .SYN_CP_LIN_CURRENT_SEL = 0b01, - .SYN_TEST_OUT_SEL = 0b000, - .RESERVED0 = 0, - }, - .vas_cfg = { /* 22 */ - .VAS_MODE = 1, - .VAS_RELOCK_SEL = 0, - .VAS_DIV = 0b010, - .VAS_DLY = 0b01, - .VAS_TRIG_EN = 1, - .VAS_ADE = 1, - .VAS_ADL_SPI = 0, - .RESERVED0 = 0, - }, - .lo_misc = { /* 23 */ - .VAS_SPI = 0b01111, - .XTAL_BIAS_SEL = 0b10, - .XTAL_E2C_BIAS_SEL = 0, - .VAS_SE = 0, - .VCO_SPI_EN = 1, - .RESERVED0 = 0, - }, - .xtal_cfg = { /* 24 */ - .XTAL_FTUNE = 0b0000000, - .XTAL_CLKOUT_EN = 0, /* 1->0 to "turn off" CLKOUT pin. Doesn't seem to work though... */ - .XTAL_CLKOUT_DIV = 1, - .XTAL_CORE_EN = 0, - .RESERVED0 = 0, - }, - .vco_cfg = { /* 25 */ - .VCO_BIAS_SPI_EN = 0, - .VCO_BIAS_SPI = 0b0000, - .VCO_CMEN = 0, - .VCO_PDET_TST = 0b00, - .VCO_BUF_BIASH = 0b01, - .RESERVED0 = 0, - }, - .lo_gen = { /* 26 */ - .LOGEN_BIASH1 = 0b10, - .LOGEN_BIASH2 = 0, - .LOGEN_2GM = 1, - .LOGEN_TRIM1 = 0, - .LOGEN_TRIM2 = 0, - .VAS_TST = 0b1111, - .RESERVED0 = 0, - }, - .pa_drv_pa_dac = { /* 27 */ - .PADRV_BIAS = 0b011, - .PADRV_DOWN_SPI_EN = 0, - .PADRV_DOWN_SPI_SEL = 0, /* Documentation mismatch */ - .PADAC_IV = 1, - .PADAC_VMODE = 1, - .PADAC_DIVH = 1, - .TXGATE_EN = 1, - .TX_DCCORR_EN = 1, - .RESERVED0 = 0, - }, - .pa_dac = { /* 28 */ - .PADAC_BIAS = 0b000000, - .PADAC_DLY = 0b0011, - .RESERVED0 = 0, - }, - .tx_gain = { /* 29 */ - .TXVGA_GAIN_SPI_EN = 0, - .TXVGA_GAIN_MSB_SPI_EN = 0, - .TX_DCCORR_SPI_EN = 0, - .FUSE_ARM = 0, - .TXVGA_GAIN_SPI = 0b111111, - .RESERVED0 = 0, - }, - .tx_lo_iq = { /* 30 */ - .TXLO_IQ_SPI = 0b00000, - .TXLO_IQ_SPI_EN = 0, - .TXLO_BUFF = 0b10, - .FUSE_GKT = 0, - .FUSE_RTH = 0, - .RESERVED0 = 0, - }, - .tx_dc_corr = { /* 31 */ - .TX_DCCORR_I = 0b00000, - .TX_DCCORR_Q = 0b00000, - .RESERVED0 = 0, - }, -} }; +constexpr RegisterMap initial_register_values{Register_Type{ + /* Best effort to reconcile default values specified in three + * different places in the MAX2837 documentation. + */ + .rxrf_1 = { + /* 0 */ + .LNA_EN = 0, + .Mixer_EN = 0, + .RxLO_EN = 0, + .Lbias = 0b10, + .Mbias = 0b10, + .buf = 0b10, + .LNAband = 0, + .RESERVED0 = 0, + }, + .rxrf_2 = { + /* 1 */ + .LNAtune = 0, + .LNAde_Q = 1, + .L = 0b000, + .iqerr_trim = 0b00000, + .RESERVED0 = 0, + }, + .lpf_1 = { + /* 2 */ + .LPF_EN = 0, + .TxBB_EN = 0, + .ModeCtrl = 0b01, + .FT = 0b1111, + .dF = 0b01, + .RESERVED0 = 0, + }, + .lpf_2 = { + /* 3 */ + .PT_SPI = 0b1001, + .Bqd = 0b011, + .TxRPCM = 0b011, + .RESERVED0 = 0, + }, + .lpf_3_vga_1 = { + /* 4 */ + .RP = 0b10, + .TxBuff = 0b10, + .VGA_EN = 0, + .VGAMUX_enable = 0, + .BUFF_Curr = 0b00, + .BUFF_VCM = 0b00, + .RESERVED0 = 0, + }, + .vga_2 = { + /* 5 */ + .VGA = 0b00000, + .sel_In1_In2 = 0, + .turbo15n20 = 0, + .VGA_Curr = 0b01, + .fuse_arm = 0, + .RESERVED0 = 0, + }, + .vga_3_rx_top = { + /* 6 */ + .RESERVED0 = 0b000110, + .RSSI_EN_SPIenables = 0, + .RSSI_MUX = 0, + .RSSI_MODE = 0, + .LPF_MODE_SEL = 0, + .RESERVED1 = 0, + }, + .temp_sense = { + /* 7 */ + .ts_adc = 0b00000, + .RESERVED0 = 0, + .PLL_test_output = 0, + .VAS_test_output = 0, + .HPFSM_test_output = 0, + .LOGEN_trim_divider_test_output = 0, + .RESERVED1 = 0, + }, + .rx_top_rx_bias = { + /* 8 */ + .LNAgain_SPI_EN = 0, + .VGAgain_SPI_EN = 0, + .EN_Bias_Trim = 0, + .BIAS_TRIM_SPI = 0b10000, + .BIAS_TRIM_CNTRL = 0, + .RX_IQERR_SPI_EN = 0, + .RESERVED0 = 0, + }, + .rx_top = { + /* 9 */ + .ts_adc_trigger = 0, + .ts_en = 0, + .LPFtrim_SPI_EN = 0, + .DOUT_DRVH = 1, /* Documentation mismatch */ + .DOUT_PU = 1, + .DOUT_SEL = 0b000, + .fuse_th = 0, + .fuse_burn_gkt = 0, + .RESERVED0 = 0, + }, + .tx_top_1 = { + /* 10 */ + .RESERVED0 = 0, + .TXCAL_GAIN = 0b00, + .TXCAL_V2I_FILT = 0b011, + .TX_BIAS_ADJ = 0b01, + .RESERVED1 = 0b00, + .RESERVED2 = 0, + }, + .tx_top_2 = { + /* 11 */ + .AMD_SPI_EN = 0, + .TXMXR_V2I_GAIN = 0b1011, + .RESERVED0 = 0b00000, + .RESERVED1 = 0, + }, + .hpfsm_1 = { + /* 12 */ + .HPC_10M = 0b11, + .HPC_10M_GAIN = 0b11, + .HPC_600k = 0b100, + .HPC_600k_GAIN = 0b100, + .RESERVED0 = 0, + }, + .hpfsm_2 = { + /* 13 */ + .HPC_100k = 0b00, + .HPC_100k_GAIN = 0b00, + .HPC_30k = 0b01, + .HPC_30k_GAIN = 0b01, + .HPC_1k = 0b01, + .RESERVED0 = 0, + }, + .hpfsm_3 = { + /* 14 */ + .HPC_1k_GAIN = 0b01, + .HPC_DELAY = 0b01, + .HPC_STOP = 0b00, + .HPC_STOP_M2 = 0b11, + .HPC_RXGAIN_EN = 1, + .HPC_MODE = 0, + .RESERVED0 = 0, + }, + .hpfsm_4 = { + /* 15 */ + .HPC_DIVH = 1, + .HPC_TST = 0b00000, + .HPC_SEQ_BYP = 0, + .DOUT_CSB_SEL = 1, /* Documentation mismatch */ + .RESERVED0 = 0b00, + .RESERVED1 = 0, + }, + .spi_en = { + /* 16 */ + .EN_SPI = 0, + .CAL_SPI = 0, + .LOGEN_SPI_EN = 1, + .SYN_SPI_EN = 1, + .VAS_SPI_EN = 1, + .PADRV_SPI_EN = 0, + .PADAC_SPI_EN = 0, + .PADAC_TX_EN = 0, + .TXMX_SPI_EN = 0, + .TXLO_SPI_EN = 0, + .RESERVED0 = 0, + }, + .syn_fr_div_1 = { + /* 17 */ + .SYN_FRDIV_9_0 = 0b0101010101, + .RESERVED0 = 0, + }, + .syn_fr_div_2 = { + /* 18 */ + .SYN_FRDIV_19_10 = 0b0101010101, + .RESERVED0 = 0, + }, + .syn_int_div = { + /* 19 */ + .SYN_INTDIV = 0b01010011, + .LOGEN_BSW = 0b01, + .RESERVED0 = 0, + }, + .syn_cfg_1 = { + /* 20 */ + .SYN_MODE_FR_EN = 1, + .SYN_REF_DIV_RATIO = 0b00, + .SYN_CP_CURRENT = 0b00, + .SYN_CLOCKOUT_DRIVE = 0, + .SYN_TURBO_EN = 1, + .CP_TRM_SET = 0, + .CP_TRM_CODE = 0b10, + .RESERVED0 = 0, + }, + .syn_cfg_2 = { + /* 21 */ + .SYN_CP_COMMON_MODE_EN = 1, + .SYN_PRESCALER_BIAS_BOOST = 0, + .SYN_CP_BETA_CURRENT_COMP_EN = 1, + .SYN_SD_CLK_SEL = 1, + .SYN_CP_PW_ADJ = 0, + .SYN_CP_LIN_CURRENT_SEL = 0b01, + .SYN_TEST_OUT_SEL = 0b000, + .RESERVED0 = 0, + }, + .vas_cfg = { + /* 22 */ + .VAS_MODE = 1, + .VAS_RELOCK_SEL = 0, + .VAS_DIV = 0b010, + .VAS_DLY = 0b01, + .VAS_TRIG_EN = 1, + .VAS_ADE = 1, + .VAS_ADL_SPI = 0, + .RESERVED0 = 0, + }, + .lo_misc = { + /* 23 */ + .VAS_SPI = 0b01111, + .XTAL_BIAS_SEL = 0b10, + .XTAL_E2C_BIAS_SEL = 0, + .VAS_SE = 0, + .VCO_SPI_EN = 1, + .RESERVED0 = 0, + }, + .xtal_cfg = { + /* 24 */ + .XTAL_FTUNE = 0b0000000, + .XTAL_CLKOUT_EN = 0, /* 1->0 to "turn off" CLKOUT pin. Doesn't seem to work though... */ + .XTAL_CLKOUT_DIV = 1, + .XTAL_CORE_EN = 0, + .RESERVED0 = 0, + }, + .vco_cfg = { + /* 25 */ + .VCO_BIAS_SPI_EN = 0, + .VCO_BIAS_SPI = 0b0000, + .VCO_CMEN = 0, + .VCO_PDET_TST = 0b00, + .VCO_BUF_BIASH = 0b01, + .RESERVED0 = 0, + }, + .lo_gen = { + /* 26 */ + .LOGEN_BIASH1 = 0b10, + .LOGEN_BIASH2 = 0, + .LOGEN_2GM = 1, + .LOGEN_TRIM1 = 0, + .LOGEN_TRIM2 = 0, + .VAS_TST = 0b1111, + .RESERVED0 = 0, + }, + .pa_drv_pa_dac = { + /* 27 */ + .PADRV_BIAS = 0b011, + .PADRV_DOWN_SPI_EN = 0, + .PADRV_DOWN_SPI_SEL = 0, /* Documentation mismatch */ + .PADAC_IV = 1, + .PADAC_VMODE = 1, + .PADAC_DIVH = 1, + .TXGATE_EN = 1, + .TX_DCCORR_EN = 1, + .RESERVED0 = 0, + }, + .pa_dac = { + /* 28 */ + .PADAC_BIAS = 0b000000, + .PADAC_DLY = 0b0011, + .RESERVED0 = 0, + }, + .tx_gain = { + /* 29 */ + .TXVGA_GAIN_SPI_EN = 0, + .TXVGA_GAIN_MSB_SPI_EN = 0, + .TX_DCCORR_SPI_EN = 0, + .FUSE_ARM = 0, + .TXVGA_GAIN_SPI = 0b111111, + .RESERVED0 = 0, + }, + .tx_lo_iq = { + /* 30 */ + .TXLO_IQ_SPI = 0b00000, + .TXLO_IQ_SPI_EN = 0, + .TXLO_BUFF = 0b10, + .FUSE_GKT = 0, + .FUSE_RTH = 0, + .RESERVED0 = 0, + }, + .tx_dc_corr = { + /* 31 */ + .TX_DCCORR_I = 0b00000, + .TX_DCCORR_Q = 0b00000, + .RESERVED0 = 0, + }, +}}; class MAX2837 : public MAX283x { -public: - constexpr MAX2837( - spi::arbiter::Target& target - ) : _target(target) - { - } - - void init() override; - void set_mode(const Mode mode) override; - - void set_tx_vga_gain(const int_fast8_t db) override; - void set_lna_gain(const int_fast8_t db) override; - void set_vga_gain(const int_fast8_t db) override; - void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override; + public: + constexpr MAX2837( + spi::arbiter::Target& target) + : _target(target) { + } + + void init() override; + void set_mode(const Mode mode) override; + + void set_tx_vga_gain(const int_fast8_t db) override; + void set_lna_gain(const int_fast8_t db) override; + void set_vga_gain(const int_fast8_t db) override; + void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override; #if 0 void rx_cal() { _map.r.spi_en.EN_SPI = 1; @@ -795,33 +825,33 @@ class MAX2837 : public MAX283x { } #endif - bool set_frequency(const rf::Frequency lo_frequency) override; + bool set_frequency(const rf::Frequency lo_frequency) override; - void set_rx_lo_iq_calibration(const size_t v) override; - void set_rx_bias_trim(const size_t v); - void set_vco_bias(const size_t v); - void set_rx_buff_vcm(const size_t v) override; + void set_rx_lo_iq_calibration(const size_t v) override; + void set_rx_bias_trim(const size_t v); + void set_vco_bias(const size_t v); + void set_rx_buff_vcm(const size_t v) override; - reg_t temp_sense() override; + reg_t temp_sense() override; - reg_t read(const address_t reg_num) override; + reg_t read(const address_t reg_num) override; -private: - spi::arbiter::Target& _target; + private: + spi::arbiter::Target& _target; - RegisterMap _map { initial_register_values }; - DirtyRegisters _dirty { }; + RegisterMap _map{initial_register_values}; + DirtyRegisters _dirty{}; - void flush_one(const Register reg); + void flush_one(const Register reg); - void write(const address_t reg_num, const reg_t value); + void write(const address_t reg_num, const reg_t value); - void write(const Register reg, const reg_t value); - reg_t read(const Register reg); + void write(const Register reg, const reg_t value); + reg_t read(const Register reg); - void flush(); + void flush(); }; -} +} // namespace max2837 -#endif/*__MAX2837_H__*/ +#endif /*__MAX2837_H__*/ diff --git a/firmware/application/hw/max2839.cpp b/firmware/application/hw/max2839.cpp index 2e0bbf4df..bb229e6b7 100644 --- a/firmware/application/hw/max2839.cpp +++ b/firmware/application/hw/max2839.cpp @@ -37,14 +37,13 @@ namespace lna { using namespace max283x::lna; -constexpr std::array lookup_8db_steps { - 0b11, 0b11, 0b10, 0b10, - 0b01, 0b00, 0b00, 0b00 -}; +constexpr std::array lookup_8db_steps{ + 0b11, 0b11, 0b10, 0b10, + 0b01, 0b00, 0b00, 0b00}; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range.clip(db); - return lna::lookup_8db_steps[(db_sat >> 3) & 7]; + const auto db_sat = gain_db_range.clip(db); + return lna::lookup_8db_steps[(db_sat >> 3) & 7]; } } /* namespace lna */ @@ -53,11 +52,11 @@ namespace vga { using namespace max283x::vga; -constexpr range_t gain_db_range_internal { 0, 63 }; +constexpr range_t gain_db_range_internal{0, 63}; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range_internal.clip(db); - return (db_sat & 0b111111) ^ 0b111111; + const auto db_sat = gain_db_range_internal.clip(db); + return (db_sat & 0b111111) ^ 0b111111; } } /* namespace vga */ @@ -67,8 +66,8 @@ namespace tx { using namespace max283x::tx; static uint_fast8_t gain_ordinal(const int8_t db) { - const auto db_sat = gain_db_range.clip(db); - return 47 - db_sat; + const auto db_sat = gain_db_range.clip(db); + return 47 - db_sat; } } /* namespace tx */ @@ -78,10 +77,10 @@ namespace filter { using namespace max283x::filter; static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) { - /* Determine filter setting that will provide bandwidth greater than or - * equal to requested bandwidth. - */ - return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin(); + /* Determine filter setting that will provide bandwidth greater than or + * equal to requested bandwidth. + */ + return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin(); } } /* namespace filter */ @@ -99,114 +98,118 @@ static int_fast8_t requested_rx_lna_gain = 0; static int_fast8_t requested_rx_vga_gain = 0; void MAX2839::init() { - set_mode(Mode::Shutdown); + set_mode(Mode::Shutdown); - gpio_max283x_enable.output(); - gpio_max2839_rxtx.output(); + gpio_max283x_enable.output(); + gpio_max2839_rxtx.output(); - _map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */ + _map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */ - _map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1; - _map.r.tx_gain.TXVGA_GAIN_SPI = 0x00; + _map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1; + _map.r.tx_gain.TXVGA_GAIN_SPI = 0x00; - _map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */ + _map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */ - _map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */ - _map.r.lpf_vga_1.L = 0b000; - _map.r.lpf_vga_2.L = 0b000; + _map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */ + _map.r.lpf_vga_1.L = 0b000; + _map.r.lpf_vga_2.L = 0b000; - _map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */ - _map.r.lpf_vga_1.VGA = 0b000000; - _map.r.lpf_vga_2.VGA = 0b010101; + _map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */ + _map.r.lpf_vga_1.VGA = 0b000000; + _map.r.lpf_vga_2.VGA = 0b010101; - _map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */ + _map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */ - _map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */ - _map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */ + _map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */ + _map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */ - _map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */ + _map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */ - _map.r.lo_gen.LOGEN_2GM = 0; + _map.r.lo_gen.LOGEN_2GM = 0; - _map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */ + _map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */ - /* - * There are two LNA band settings, but we only use one of them. - * Switching to the other one doesn't make the overall spectrum any - * flatter but adds a surprise step in the middle. - */ - _map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */ + /* + * There are two LNA band settings, but we only use one of them. + * Switching to the other one doesn't make the overall spectrum any + * flatter but adds a surprise step in the middle. + */ + _map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */ - _dirty.set(); - flush(); + _dirty.set(); + flush(); - set_mode(Mode::Standby); + set_mode(Mode::Standby); } enum class Mask { - Enable = 0b01, - RxTx = 0b10, - Shutdown = 0b00, - Standby = RxTx, - Receive = Enable | RxTx, - Transmit = Enable, + Enable = 0b01, + RxTx = 0b10, + Shutdown = 0b00, + Standby = RxTx, + Receive = Enable | RxTx, + Transmit = Enable, }; Mask mode_mask(const Mode mode) { - switch (mode) { - case Mode::Standby: return Mask::Standby; - case Mode::Receive: return Mask::Receive; - case Mode::Transmit: return Mask::Transmit; - default: return Mask::Shutdown; - } + switch (mode) { + case Mode::Standby: + return Mask::Standby; + case Mode::Receive: + return Mask::Receive; + case Mode::Transmit: + return Mask::Transmit; + default: + return Mask::Shutdown; + } } void MAX2839::set_mode(const Mode mode) { - Mask mask = mode_mask(mode); - gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable)); - gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx)); + Mask mask = mode_mask(mode); + gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable)); + gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx)); } void MAX2839::flush() { - if( _dirty ) { - for(size_t n=0; n 63) { - if (lna_gain <= 32) { - vga_gain -= 8; - lna_gain += 8; - } else { - vga_gain = 63; - } - } - - /* - * MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain - * settings, so we use VGA gain to compensate. - */ - if (lna_gain == 0) { - lna_gain = 8; - vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0; - } - if (lna_gain == 16) { - if (vga_gain > 32) { - vga_gain -= 8; - lna_gain += 8; - } else { - vga_gain += 8; - lna_gain -= 8; - } - } - - _map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain); - _dirty[Register::RXRF_2] = 1; - _map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain); - _dirty[Register::LPF_VGA_2] = 1; - flush(); + /* Apply MAX2837 restrictions to requested gain settings. */ + int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain); + lna_gain &= 0x38; + int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain); + vga_gain &= 0x3e; + + /* + * MAX2839 has lower full-scale RX output voltage than MAX2837, so we + * adjust the VGA (baseband) gain to compensate. + */ + vga_gain += 3; + + /* + * If that adjustment puts VGA gain out of range, use LNA gain to + * compensate. MAX2839 VGA gain can be any number from 0 through 63. + */ + if (vga_gain > 63) { + if (lna_gain <= 32) { + vga_gain -= 8; + lna_gain += 8; + } else { + vga_gain = 63; + } + } + + /* + * MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain + * settings, so we use VGA gain to compensate. + */ + if (lna_gain == 0) { + lna_gain = 8; + vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0; + } + if (lna_gain == 16) { + if (vga_gain > 32) { + vga_gain -= 8; + lna_gain += 8; + } else { + vga_gain += 8; + lna_gain -= 8; + } + } + + _map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain); + _dirty[Register::RXRF_2] = 1; + _map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain); + _dirty[Register::LPF_VGA_2] = 1; + flush(); } void MAX2839::set_lna_gain(const int_fast8_t db) { - requested_rx_lna_gain = db; - configure_rx_gain(); + requested_rx_lna_gain = db; + configure_rx_gain(); } void MAX2839::set_vga_gain(const int_fast8_t db) { - requested_rx_vga_gain = db; - configure_rx_gain(); + requested_rx_vga_gain = db; + configure_rx_gain(); } void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) { - _map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum); - _dirty[Register::LPF] = 1; - flush(); + _map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum); + _dirty[Register::LPF] = 1; + flush(); } bool MAX2839::set_frequency(const rf::Frequency lo_frequency) { - /* TODO: This is a sad implementation. Refactor. */ - if( lo::band[0].contains(lo_frequency) ) { - _map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */ - } else if( lo::band[1].contains(lo_frequency) ) { - _map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */ - } else if( lo::band[2].contains(lo_frequency) ) { - _map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */ - } else if( lo::band[3].contains(lo_frequency) ) { - _map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */ - } else { - return false; - } - _dirty[Register::SYN_INT_DIV] = 1; - - const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor; - - _map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20; - _dirty[Register::SYN_INT_DIV] = 1; - _map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff; - _dirty[Register::SYN_FR_DIV_2] = 1; - /* flush to commit high FRDIV first, as low FRDIV commits the change */ - flush(); - - _map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff); - _dirty[Register::SYN_FR_DIV_1] = 1; - flush(); - - return true; + /* TODO: This is a sad implementation. Refactor. */ + if (lo::band[0].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */ + } else if (lo::band[1].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */ + } else if (lo::band[2].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */ + } else if (lo::band[3].contains(lo_frequency)) { + _map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */ + } else { + return false; + } + _dirty[Register::SYN_INT_DIV] = 1; + + const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor; + + _map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20; + _dirty[Register::SYN_INT_DIV] = 1; + _map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff; + _dirty[Register::SYN_FR_DIV_2] = 1; + /* flush to commit high FRDIV first, as low FRDIV commits the change */ + flush(); + + _map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff); + _dirty[Register::SYN_FR_DIV_1] = 1; + flush(); + + return true; } void MAX2839::set_rx_lo_iq_calibration(const size_t v) { - _map.r.rxrf_2.RX_IQERR_SPI_EN = 1; - _dirty[Register::RXRF_2] = 1; - _map.r.rxrf_1.iqerr_trim = v; - _dirty[Register::RXRF_1] = 1; - flush(); + _map.r.rxrf_2.RX_IQERR_SPI_EN = 1; + _dirty[Register::RXRF_2] = 1; + _map.r.rxrf_1.iqerr_trim = v; + _dirty[Register::RXRF_1] = 1; + flush(); } void MAX2839::set_rx_buff_vcm(const size_t v) { - _map.r.lpf_vga_2.BUFF_VCM = v; - _dirty[Register::LPF_VGA_2] = 1; - flush(); + _map.r.lpf_vga_2.BUFF_VCM = v; + _dirty[Register::LPF_VGA_2] = 1; + flush(); } reg_t MAX2839::temp_sense() { - if( !_map.r.rx_top_2.ts_en ) { - _map.r.rx_top_2.ts_en = 1; - flush_one(Register::RX_TOP_2); + if (!_map.r.rx_top_2.ts_en) { + _map.r.rx_top_2.ts_en = 1; + flush_one(Register::RX_TOP_2); - chThdSleepMilliseconds(1); - } + chThdSleepMilliseconds(1); + } - _map.r.rx_top_2.ts_adc_trigger = 1; - flush_one(Register::RX_TOP_2); + _map.r.rx_top_2.ts_adc_trigger = 1; + flush_one(Register::RX_TOP_2); - halPolledDelay(ticks_for_temperature_sense_adc_conversion); + halPolledDelay(ticks_for_temperature_sense_adc_conversion); - /* - * Things look very similar to MAX2837, so this probably works, but the - * MAX2839 data sheet does not describe the TEMP_SENSE register contents. - */ - const auto value = read(Register::TEMP_SENSE); + /* + * Things look very similar to MAX2837, so this probably works, but the + * MAX2839 data sheet does not describe the TEMP_SENSE register contents. + */ + const auto value = read(Register::TEMP_SENSE); - _map.r.rx_top_2.ts_adc_trigger = 0; - flush_one(Register::RX_TOP_2); + _map.r.rx_top_2.ts_adc_trigger = 0; + flush_one(Register::RX_TOP_2); - return value; + return value; } -} \ No newline at end of file +} // namespace max2839 \ No newline at end of file diff --git a/firmware/application/hw/max2839.hpp b/firmware/application/hw/max2839.hpp index 4b94f6d98..ac75db6e3 100644 --- a/firmware/application/hw/max2839.hpp +++ b/firmware/application/hw/max2839.hpp @@ -40,649 +40,679 @@ using namespace max283x; constexpr size_t reg_count = 32; enum class Register : address_t { - RXENABLE = 0, - RXRF_1 = 1, - RXRF_2 = 2, - RXRF_LPF = 3, - LPF = 4, - LPF_VGA_1 = 5, - LPF_VGA_2 = 6, - RSSI_VGA = 7, - RX_TOP_1 = 8, - RX_TOP_2 = 9, - TX_TOP_1 = 10, - TEMP_SENSE = 11, - HPFSM_1 = 12, - HPFSM_2 = 13, - HPFSM_3 = 14, - HPFSM_4 = 15, - SPI_EN = 16, - SYN_FR_DIV_1 = 17, - SYN_FR_DIV_2 = 18, - SYN_INT_DIV = 19, - SYN_CFG_1 = 20, - SYN_CFG_2 = 21, - VAS_CFG = 22, - LO_MISC = 23, - XTAL_CFG = 24, - VCO_CFG = 25, - LO_GEN = 26, - PA_DRV = 27, - PA_DAC = 28, - TX_GAIN = 29, - TX_LO_IQ = 30, - TX_DC_CORR = 31, + RXENABLE = 0, + RXRF_1 = 1, + RXRF_2 = 2, + RXRF_LPF = 3, + LPF = 4, + LPF_VGA_1 = 5, + LPF_VGA_2 = 6, + RSSI_VGA = 7, + RX_TOP_1 = 8, + RX_TOP_2 = 9, + TX_TOP_1 = 10, + TEMP_SENSE = 11, + HPFSM_1 = 12, + HPFSM_2 = 13, + HPFSM_3 = 14, + HPFSM_4 = 15, + SPI_EN = 16, + SYN_FR_DIV_1 = 17, + SYN_FR_DIV_2 = 18, + SYN_INT_DIV = 19, + SYN_CFG_1 = 20, + SYN_CFG_2 = 21, + VAS_CFG = 22, + LO_MISC = 23, + XTAL_CFG = 24, + VCO_CFG = 25, + LO_GEN = 26, + PA_DRV = 27, + PA_DAC = 28, + TX_GAIN = 29, + TX_LO_IQ = 30, + TX_DC_CORR = 31, }; struct RXENABLE_Type { - reg_t RESERVED0 : 10; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 10; + reg_t RESERVED1 : 6; }; static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size"); struct RXRF_1_Type { - reg_t LNAband : 1; - reg_t RESERVED0 : 1; - reg_t MIMOmode : 1; - reg_t iqerr_trim : 5; - reg_t RESERVED1 : 6; + reg_t LNAband : 1; + reg_t RESERVED0 : 1; + reg_t MIMOmode : 1; + reg_t iqerr_trim : 5; + reg_t RESERVED1 : 6; }; static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size"); struct RXRF_2_Type { - reg_t LNAgain_SPI_EN : 1; - reg_t RESERVED0 : 1; - reg_t RX_IQERR_SPI_EN : 1; - reg_t RESERVED1 : 7; - reg_t RESERVED2 : 6; + reg_t LNAgain_SPI_EN : 1; + reg_t RESERVED0 : 1; + reg_t RX_IQERR_SPI_EN : 1; + reg_t RESERVED1 : 7; + reg_t RESERVED2 : 6; }; static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size"); struct RXRF_LPF_Type { - reg_t RESERVED0 : 10; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 10; + reg_t RESERVED1 : 6; }; static_assert(sizeof(RXRF_LPF_Type) == sizeof(reg_t), "RXRF_LPF_Type wrong size"); struct LPF_Type { - reg_t RESERVED0 : 2; - reg_t dF : 2; - reg_t RESERVED1 : 2; - reg_t FT : 4; - reg_t RESERVED2 : 6; + reg_t RESERVED0 : 2; + reg_t dF : 2; + reg_t RESERVED1 : 2; + reg_t FT : 4; + reg_t RESERVED2 : 6; }; static_assert(sizeof(LPF_Type) == sizeof(reg_t), "LPF_Type wrong size"); struct LPF_VGA_1_Type { - reg_t L : 2; - reg_t VGA : 6; - reg_t ModeCtrl : 2; - reg_t RESERVED0 : 6; + reg_t L : 2; + reg_t VGA : 6; + reg_t ModeCtrl : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LPF_VGA_1_Type) == sizeof(reg_t), "LPF_VGA_1_Type wrong size"); struct LPF_VGA_2_Type { - reg_t L : 2; - reg_t VGA : 6; - reg_t BUFF_VCM : 2; - reg_t RESERVED0 : 6; + reg_t L : 2; + reg_t VGA : 6; + reg_t BUFF_VCM : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(LPF_VGA_2_Type) == sizeof(reg_t), "LPF_VGA_2_Type wrong size"); struct RSSI_VGA_Type { - reg_t RESERVED0 : 1; - reg_t RSSI_MUX : 1; - reg_t RSSI_MODE : 1; - reg_t RESERVED1 : 4; - reg_t RXBB_OUT_SEL : 1; - reg_t RESERVED2 : 1; - reg_t RSSI_INPUT : 1; - reg_t RESERVED3 : 6; + reg_t RESERVED0 : 1; + reg_t RSSI_MUX : 1; + reg_t RSSI_MODE : 1; + reg_t RESERVED1 : 4; + reg_t RXBB_OUT_SEL : 1; + reg_t RESERVED2 : 1; + reg_t RSSI_INPUT : 1; + reg_t RESERVED3 : 6; }; static_assert(sizeof(RSSI_VGA_Type) == sizeof(reg_t), "RSSI_VGA_Type wrong size"); struct RX_TOP_1_Type { - reg_t RESERVED0 : 1; - reg_t VGAgain_SPI_EN : 1; - reg_t LPF_MODE_SEL : 1; - reg_t RESERVED1 : 7; - reg_t RESERVED2 : 6; + reg_t RESERVED0 : 1; + reg_t VGAgain_SPI_EN : 1; + reg_t LPF_MODE_SEL : 1; + reg_t RESERVED1 : 7; + reg_t RESERVED2 : 6; }; static_assert(sizeof(RX_TOP_1_Type) == sizeof(reg_t), "RX_TOP_1_Type wrong size"); struct RX_TOP_2_Type { - reg_t ts_adc_trigger : 1; - reg_t ts_en : 1; - reg_t RESERVED0 : 1; - reg_t DOUT_DRVH : 1; - reg_t DOUT_CSB_SEL : 1; - reg_t DOUT_SEL : 3; - reg_t RESERVED1 : 2; - reg_t RESERVED2 : 6; + reg_t ts_adc_trigger : 1; + reg_t ts_en : 1; + reg_t RESERVED0 : 1; + reg_t DOUT_DRVH : 1; + reg_t DOUT_CSB_SEL : 1; + reg_t DOUT_SEL : 3; + reg_t RESERVED1 : 2; + reg_t RESERVED2 : 6; }; static_assert(sizeof(RX_TOP_2_Type) == sizeof(reg_t), "RX_TOP_2_Type wrong size"); struct TX_TOP_1_Type { - reg_t TXCAL_GAIN : 2; - reg_t TXCAL_V2I_FILT : 3; - reg_t RESERVED1 : 5; - reg_t RESERVED2 : 6; + reg_t TXCAL_GAIN : 2; + reg_t TXCAL_V2I_FILT : 3; + reg_t RESERVED1 : 5; + reg_t RESERVED2 : 6; }; static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size"); struct TEMP_SENSE_Type { - reg_t RESERVED0 : 10; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 10; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size"); struct HPFSM_1_Type { - reg_t HPC_10M : 2; - reg_t HPC_10M_GAIN : 2; - reg_t HPC_600k : 3; - reg_t HPC_600k_GAIN : 3; - reg_t RESERVED0 : 6; + reg_t HPC_10M : 2; + reg_t HPC_10M_GAIN : 2; + reg_t HPC_600k : 3; + reg_t HPC_600k_GAIN : 3; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size"); struct HPFSM_2_Type { - reg_t HPC_100k : 2; - reg_t HPC_100k_GAIN : 2; - reg_t HPC_30k : 2; - reg_t HPC_30k_GAIN : 2; - reg_t HPC_1k : 2; - reg_t RESERVED0 : 6; + reg_t HPC_100k : 2; + reg_t HPC_100k_GAIN : 2; + reg_t HPC_30k : 2; + reg_t HPC_30k_GAIN : 2; + reg_t HPC_1k : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size"); struct HPFSM_3_Type { - reg_t HPC_1k_B7B6 : 2; - reg_t HPC_DELAY : 2; - reg_t HPC_STOP : 2; - reg_t HPC_STOP_M2 : 2; - reg_t HPC_RXGAIN_EN : 1; - reg_t TXGATE_EN : 1; - reg_t RESERVED0 : 6; + reg_t HPC_1k_B7B6 : 2; + reg_t HPC_DELAY : 2; + reg_t HPC_STOP : 2; + reg_t HPC_STOP_M2 : 2; + reg_t HPC_RXGAIN_EN : 1; + reg_t TXGATE_EN : 1; + reg_t RESERVED0 : 6; }; static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size"); struct HPFSM_4_Type { - reg_t HPC_DIVH : 1; - reg_t RESERVED0 : 5; - reg_t HPC_SEQ_BYP : 1; - reg_t RESERVED1 : 2; - reg_t HPC_MODE : 1; - reg_t RESERVED2 : 6; + reg_t HPC_DIVH : 1; + reg_t RESERVED0 : 5; + reg_t HPC_SEQ_BYP : 1; + reg_t RESERVED1 : 2; + reg_t HPC_MODE : 1; + reg_t RESERVED2 : 6; }; static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size"); struct SPI_EN_Type { - reg_t EN_SPI : 1; - reg_t CAL_SPI : 1; - reg_t RESERVED0 : 4; - reg_t PADAC_SPI_EN : 1; - reg_t PADAC_TX_EN : 1; - reg_t RESERVED1 : 2; - reg_t RESERVED2 : 6; + reg_t EN_SPI : 1; + reg_t CAL_SPI : 1; + reg_t RESERVED0 : 4; + reg_t PADAC_SPI_EN : 1; + reg_t PADAC_TX_EN : 1; + reg_t RESERVED1 : 2; + reg_t RESERVED2 : 6; }; static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size"); struct SYN_FR_DIV_1_Type { - reg_t SYN_FRDIV_9_0 : 10; - reg_t RESERVED0 : 6; + reg_t SYN_FRDIV_9_0 : 10; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size"); struct SYN_FR_DIV_2_Type { - reg_t SYN_FRDIV_19_10 : 10; - reg_t RESERVED0 : 6; + reg_t SYN_FRDIV_19_10 : 10; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size"); struct SYN_INT_DIV_Type { - reg_t SYN_INTDIV : 8; - reg_t LOGEN_BSW : 2; - reg_t RESERVED0 : 6; + reg_t SYN_INTDIV : 8; + reg_t LOGEN_BSW : 2; + reg_t RESERVED0 : 6; }; static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size"); struct SYN_CFG_1_Type { - reg_t RESERVED0 : 1; - reg_t SYN_REF_DIV_RATIO : 2; - reg_t RESERVED1 : 2; - reg_t SYN_CLOCKOUT_DRIVE : 1; - reg_t RESERVED2 : 4; - reg_t RESERVED3 : 6; + reg_t RESERVED0 : 1; + reg_t SYN_REF_DIV_RATIO : 2; + reg_t RESERVED1 : 2; + reg_t SYN_CLOCKOUT_DRIVE : 1; + reg_t RESERVED2 : 4; + reg_t RESERVED3 : 6; }; static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size"); struct SYN_CFG_2_Type { - reg_t RESERVED0 : 10; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 10; + reg_t RESERVED1 : 6; }; static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size"); struct VAS_CFG_Type { - reg_t VAS_MODE : 1; - reg_t VAS_RELOCK_SEL : 1; - reg_t VAS_DIV : 3; - reg_t VAS_DLY : 2; - reg_t VAS_TRIG_EN : 1; - reg_t RESERVED0 : 2; - reg_t RESERVED1 : 6; + reg_t VAS_MODE : 1; + reg_t VAS_RELOCK_SEL : 1; + reg_t VAS_DIV : 3; + reg_t VAS_DLY : 2; + reg_t VAS_TRIG_EN : 1; + reg_t RESERVED0 : 2; + reg_t RESERVED1 : 6; }; static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size"); struct LO_MISC_Type { - reg_t VAS_SPI : 5; - reg_t XTAL_BIAS_SEL : 2; - reg_t RESERVED0 : 3; - reg_t RESERVED1 : 6; + reg_t VAS_SPI : 5; + reg_t XTAL_BIAS_SEL : 2; + reg_t RESERVED0 : 3; + reg_t RESERVED1 : 6; }; static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size"); struct XTAL_CFG_Type { - reg_t XTAL_FTUNE : 7; - reg_t RESERVED0 : 1; - reg_t XTAL_CLKOUT_DIV : 1; - reg_t XTAL_CORE_EN : 1; - reg_t RESERVED1 : 6; + reg_t XTAL_FTUNE : 7; + reg_t RESERVED0 : 1; + reg_t XTAL_CLKOUT_DIV : 1; + reg_t XTAL_CORE_EN : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size"); struct VCO_CFG_Type { - reg_t RESERVED0 : 10; - reg_t RESERVED1 : 6; + reg_t RESERVED0 : 10; + reg_t RESERVED1 : 6; }; static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size"); struct LO_GEN_Type { - reg_t RESERVED0 : 3; - reg_t LOGEN_2GM : 1; - reg_t RESERVED1 : 2; - reg_t VAS_TST : 4; - reg_t RESERVED2 : 6; + reg_t RESERVED0 : 3; + reg_t LOGEN_2GM : 1; + reg_t RESERVED1 : 2; + reg_t VAS_TST : 4; + reg_t RESERVED2 : 6; }; static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size"); struct PA_DRV_Type { - reg_t TXLO_IQ_SPI : 6; - reg_t TXLO_IQ_SPI_EN : 1; - reg_t TXVGA_GAIN_SPI_EN : 1; - reg_t TX_DCCORR_SPI_EN : 1; - reg_t RESERVED0 : 1; - reg_t RESERVED1 : 6; + reg_t TXLO_IQ_SPI : 6; + reg_t TXLO_IQ_SPI_EN : 1; + reg_t TXVGA_GAIN_SPI_EN : 1; + reg_t TX_DCCORR_SPI_EN : 1; + reg_t RESERVED0 : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(PA_DRV_Type) == sizeof(reg_t), "PA_DRV_Type wrong size"); struct PA_DAC_Type { - reg_t PADAC_BIAS : 6; - reg_t PADAC_DLY : 4; - reg_t RESERVED0 : 6; + reg_t PADAC_BIAS : 6; + reg_t PADAC_DLY : 4; + reg_t RESERVED0 : 6; }; static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size"); struct TX_GAIN_Type { - reg_t TXVGA_GAIN_SPI : 6; - reg_t RESERVED0 : 4; - reg_t RESERVED1 : 6; + reg_t TXVGA_GAIN_SPI : 6; + reg_t RESERVED0 : 4; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size"); struct TX_LO_IQ_Type { - reg_t TX_DCCORR_I : 6; - reg_t RESERVED0 : 2; - reg_t PADAC_IV : 1; - reg_t PADAC_VMODE : 1; - reg_t RESERVED1 : 6; + reg_t TX_DCCORR_I : 6; + reg_t RESERVED0 : 2; + reg_t PADAC_IV : 1; + reg_t PADAC_VMODE : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size"); struct TX_DC_CORR_Type { - reg_t TX_DCCORR_Q : 6; - reg_t RESERVED0 : 3; - reg_t PADAC_DIVH : 1; - reg_t RESERVED1 : 6; + reg_t TX_DCCORR_Q : 6; + reg_t RESERVED0 : 3; + reg_t PADAC_DIVH : 1; + reg_t RESERVED1 : 6; }; static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size"); struct Register_Type { - RXENABLE_Type rxenable; /* 0 */ - RXRF_1_Type rxrf_1; - RXRF_2_Type rxrf_2; - RXRF_LPF_Type rxrf_lpf_1; - LPF_Type lpf; /* 4 */ - LPF_VGA_1_Type lpf_vga_1; - LPF_VGA_2_Type lpf_vga_2; - RSSI_VGA_Type rssi_vga; - RX_TOP_1_Type rx_top_1; /* 8 */ - RX_TOP_2_Type rx_top_2; - TX_TOP_1_Type tx_top_1; - TEMP_SENSE_Type temp_sense; - HPFSM_1_Type hpfsm_1; /* 12 */ - HPFSM_2_Type hpfsm_2; - HPFSM_3_Type hpfsm_3; - HPFSM_4_Type hpfsm_4; - SPI_EN_Type spi_en; /* 16 */ - SYN_FR_DIV_1_Type syn_fr_div_1; - SYN_FR_DIV_2_Type syn_fr_div_2; - SYN_INT_DIV_Type syn_int_div; - SYN_CFG_1_Type syn_cfg_1; /* 20 */ - SYN_CFG_2_Type syn_cfg_2; - VAS_CFG_Type vas_cfg; - LO_MISC_Type lo_misc; - XTAL_CFG_Type xtal_cfg; /* 24 */ - VCO_CFG_Type vco_cfg; - LO_GEN_Type lo_gen; - PA_DRV_Type pa_drv; - PA_DAC_Type pa_dac; /* 28 */ - TX_GAIN_Type tx_gain; - TX_LO_IQ_Type tx_lo_iq; - TX_DC_CORR_Type tx_dc_corr; + RXENABLE_Type rxenable; /* 0 */ + RXRF_1_Type rxrf_1; + RXRF_2_Type rxrf_2; + RXRF_LPF_Type rxrf_lpf_1; + LPF_Type lpf; /* 4 */ + LPF_VGA_1_Type lpf_vga_1; + LPF_VGA_2_Type lpf_vga_2; + RSSI_VGA_Type rssi_vga; + RX_TOP_1_Type rx_top_1; /* 8 */ + RX_TOP_2_Type rx_top_2; + TX_TOP_1_Type tx_top_1; + TEMP_SENSE_Type temp_sense; + HPFSM_1_Type hpfsm_1; /* 12 */ + HPFSM_2_Type hpfsm_2; + HPFSM_3_Type hpfsm_3; + HPFSM_4_Type hpfsm_4; + SPI_EN_Type spi_en; /* 16 */ + SYN_FR_DIV_1_Type syn_fr_div_1; + SYN_FR_DIV_2_Type syn_fr_div_2; + SYN_INT_DIV_Type syn_int_div; + SYN_CFG_1_Type syn_cfg_1; /* 20 */ + SYN_CFG_2_Type syn_cfg_2; + VAS_CFG_Type vas_cfg; + LO_MISC_Type lo_misc; + XTAL_CFG_Type xtal_cfg; /* 24 */ + VCO_CFG_Type vco_cfg; + LO_GEN_Type lo_gen; + PA_DRV_Type pa_drv; + PA_DAC_Type pa_dac; /* 28 */ + TX_GAIN_Type tx_gain; + TX_LO_IQ_Type tx_lo_iq; + TX_DC_CORR_Type tx_dc_corr; }; static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size"); struct RegisterMap { - constexpr RegisterMap( - Register_Type values - ) : r(values) - { - } + constexpr RegisterMap( + Register_Type values) + : r(values) { + } - union { - Register_Type r; - std::array w; - }; + union { + Register_Type r; + std::array w; + }; }; static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size"); -constexpr RegisterMap initial_register_values { Register_Type { - /* settings recommended by MAX2839 data sheet */ - .rxenable = { /* 0 */ - .RESERVED0 = 0, - .RESERVED1 = 0, - }, - .rxrf_1 = { /* 1 */ - .LNAband = 0, - .RESERVED0 = 0, - .MIMOmode = 1, - .iqerr_trim = 0b000001, - .RESERVED1 = 0, - }, - .rxrf_2 = { /* 2 */ - .LNAgain_SPI_EN = 0, - .RESERVED0 = 0, - .RX_IQERR_SPI_EN = 0, - .RESERVED1 = 0b0010000, - .RESERVED2 = 0, - }, - .rxrf_lpf_1 = { /* 3 */ - .RESERVED0 = 0b0110111001, - .RESERVED1 = 0, - }, - .lpf = { /* 4 */ - .RESERVED0 = 0b10, - .dF = 0b01, - .RESERVED1 = 0b10, - .FT = 0b1111, - .RESERVED2 = 0, - }, - .lpf_vga_1 = { /* 5 */ - .L = 0b00, - .VGA = 0b000000, - .ModeCtrl = 0b01, - .RESERVED0 = 0, - }, - .lpf_vga_2 = { /* 6 */ - .L = 0b00, - .VGA = 0b000000, - .BUFF_VCM = 0b00, - .RESERVED0 = 0, - }, - .rssi_vga = { /* 7 */ - .RESERVED0 = 0, - .RSSI_MUX = 0, - .RSSI_MODE = 0, - .RESERVED1 = 0b0001, - .RXBB_OUT_SEL = 0, - .RESERVED2 = 0, - .RSSI_INPUT = 1, - .RESERVED3 = 0, - }, - .rx_top_1 = { /* 8 */ - .RESERVED0 = 0, - .VGAgain_SPI_EN = 0, - .LPF_MODE_SEL = 0, - .RESERVED1 = 0b1000100, - .RESERVED2 = 0, - }, - .rx_top_2 = { /* 9 */ - .ts_adc_trigger = 0, - .ts_en = 0, - .RESERVED0 = 0, - .DOUT_DRVH = 1, - .DOUT_CSB_SEL = 1, - .DOUT_SEL = 0b000, - .RESERVED1 = 0b00, - .RESERVED2 = 0, - }, - .tx_top_1 = { /* 10 */ - .TXCAL_GAIN = 0b00, - .TXCAL_V2I_FILT = 0b011, - .RESERVED1 = 0b00000, - .RESERVED2 = 0, - }, - .temp_sense = { /* 11 */ - .RESERVED0 = 0b0000000100, - .RESERVED1 = 0, - }, - .hpfsm_1 = { /* 12 */ - .HPC_10M = 0b11, - .HPC_10M_GAIN = 0b11, - .HPC_600k = 0b100, - .HPC_600k_GAIN = 0b100, - .RESERVED0 = 0, - }, - .hpfsm_2 = { /* 13 */ - .HPC_100k = 0b00, - .HPC_100k_GAIN = 0b00, - .HPC_30k = 0b01, - .HPC_30k_GAIN = 0b01, - .HPC_1k = 0b01, - .RESERVED0 = 0, - }, - .hpfsm_3 = { /* 14 */ - .HPC_1k_B7B6 = 0b01, - .HPC_DELAY = 0b01, - .HPC_STOP = 0b00, - .HPC_STOP_M2 = 0b11, - .HPC_RXGAIN_EN = 1, - .TXGATE_EN = 1, - .RESERVED0 = 0, - }, - .hpfsm_4 = { /* 15 */ - .HPC_DIVH = 1, - .RESERVED0 = 0b00000, - .HPC_SEQ_BYP = 0, - .RESERVED1 = 0b00, - .HPC_MODE = 1, - .RESERVED2 = 0, - }, - .spi_en = { /* 16 */ - .EN_SPI = 0, - .CAL_SPI = 0, - .RESERVED0 = 0b0111, - .PADAC_SPI_EN = 0, - .PADAC_TX_EN = 0, - .RESERVED1 = 0b00, - .RESERVED2 = 0, - }, - .syn_fr_div_1 = { /* 17 */ - .SYN_FRDIV_9_0 = 0b0101010101, - .RESERVED0 = 0, - }, - .syn_fr_div_2 = { /* 18 */ - .SYN_FRDIV_19_10 = 0b0101010101, - .RESERVED0 = 0, - }, - .syn_int_div = { /* 19 */ - .SYN_INTDIV = 0b01010011, - .LOGEN_BSW = 0b01, - .RESERVED0 = 0, - }, - .syn_cfg_1 = { /* 20 */ - .RESERVED0 = 1, - .SYN_REF_DIV_RATIO = 0b00, - .RESERVED1 = 0b01, - .SYN_CLOCKOUT_DRIVE = 0, - .RESERVED2 = 0b1001, - .RESERVED3 = 0, - }, - .syn_cfg_2 = { /* 21 */ - .RESERVED0 = 0b0000101101, - .RESERVED1 = 0, - }, - .vas_cfg = { /* 22 */ - .VAS_MODE = 1, - .VAS_RELOCK_SEL = 0, - .VAS_DIV = 0b010, - .VAS_DLY = 0b01, - .VAS_TRIG_EN = 1, - .RESERVED0 = 0b01, - .RESERVED1 = 0, - }, - .lo_misc = { /* 23 */ - .VAS_SPI = 0b01111, - .XTAL_BIAS_SEL = 0b10, - .RESERVED0 = 0b100, - .RESERVED1 = 0, - }, - .xtal_cfg = { /* 24 */ - .XTAL_FTUNE = 0b0000000, - .RESERVED0 = 1, - .XTAL_CLKOUT_DIV = 1, - .XTAL_CORE_EN = 0, - .RESERVED1 = 0, - }, - .vco_cfg = { /* 25 */ - .RESERVED0 = 0b0000000000, - .RESERVED1 = 0, - }, - .lo_gen = { /* 26 */ - .RESERVED0 = 0b000, - .LOGEN_2GM = 0, - .RESERVED1 = 0b00, - .VAS_TST = 0b1111, - .RESERVED2 = 0, - }, - .pa_drv = { /* 27 */ - .TXLO_IQ_SPI = 0b000000, - .TXLO_IQ_SPI_EN = 0, - .TXVGA_GAIN_SPI_EN = 0, - .TX_DCCORR_SPI_EN = 0, - .RESERVED0 = 1, - .RESERVED1 = 0, - }, - .pa_dac = { /* 28 */ - .PADAC_BIAS = 0b000000, - .PADAC_DLY = 0b0011, - .RESERVED0 = 0, - }, - .tx_gain = { /* 29 */ - .TXVGA_GAIN_SPI = 0b111111, - .RESERVED0 = 0b0000, - .RESERVED1 = 0, - }, - .tx_lo_iq = { /* 30 */ - .TX_DCCORR_I = 0b000000, - .RESERVED0 = 0b00, - .PADAC_IV = 1, - .PADAC_VMODE = 1, - .RESERVED1 = 0, - }, - .tx_dc_corr = { /* 31 */ - .TX_DCCORR_Q = 0b000000, - .RESERVED0 = 0b101, - .PADAC_DIVH = 1, - .RESERVED1 = 0, - }, -} }; +constexpr RegisterMap initial_register_values{Register_Type{ + /* settings recommended by MAX2839 data sheet */ + .rxenable = { + /* 0 */ + .RESERVED0 = 0, + .RESERVED1 = 0, + }, + .rxrf_1 = { + /* 1 */ + .LNAband = 0, + .RESERVED0 = 0, + .MIMOmode = 1, + .iqerr_trim = 0b000001, + .RESERVED1 = 0, + }, + .rxrf_2 = { + /* 2 */ + .LNAgain_SPI_EN = 0, + .RESERVED0 = 0, + .RX_IQERR_SPI_EN = 0, + .RESERVED1 = 0b0010000, + .RESERVED2 = 0, + }, + .rxrf_lpf_1 = { + /* 3 */ + .RESERVED0 = 0b0110111001, + .RESERVED1 = 0, + }, + .lpf = { + /* 4 */ + .RESERVED0 = 0b10, + .dF = 0b01, + .RESERVED1 = 0b10, + .FT = 0b1111, + .RESERVED2 = 0, + }, + .lpf_vga_1 = { + /* 5 */ + .L = 0b00, + .VGA = 0b000000, + .ModeCtrl = 0b01, + .RESERVED0 = 0, + }, + .lpf_vga_2 = { + /* 6 */ + .L = 0b00, + .VGA = 0b000000, + .BUFF_VCM = 0b00, + .RESERVED0 = 0, + }, + .rssi_vga = { + /* 7 */ + .RESERVED0 = 0, + .RSSI_MUX = 0, + .RSSI_MODE = 0, + .RESERVED1 = 0b0001, + .RXBB_OUT_SEL = 0, + .RESERVED2 = 0, + .RSSI_INPUT = 1, + .RESERVED3 = 0, + }, + .rx_top_1 = { + /* 8 */ + .RESERVED0 = 0, + .VGAgain_SPI_EN = 0, + .LPF_MODE_SEL = 0, + .RESERVED1 = 0b1000100, + .RESERVED2 = 0, + }, + .rx_top_2 = { + /* 9 */ + .ts_adc_trigger = 0, + .ts_en = 0, + .RESERVED0 = 0, + .DOUT_DRVH = 1, + .DOUT_CSB_SEL = 1, + .DOUT_SEL = 0b000, + .RESERVED1 = 0b00, + .RESERVED2 = 0, + }, + .tx_top_1 = { + /* 10 */ + .TXCAL_GAIN = 0b00, + .TXCAL_V2I_FILT = 0b011, + .RESERVED1 = 0b00000, + .RESERVED2 = 0, + }, + .temp_sense = { + /* 11 */ + .RESERVED0 = 0b0000000100, + .RESERVED1 = 0, + }, + .hpfsm_1 = { + /* 12 */ + .HPC_10M = 0b11, + .HPC_10M_GAIN = 0b11, + .HPC_600k = 0b100, + .HPC_600k_GAIN = 0b100, + .RESERVED0 = 0, + }, + .hpfsm_2 = { + /* 13 */ + .HPC_100k = 0b00, + .HPC_100k_GAIN = 0b00, + .HPC_30k = 0b01, + .HPC_30k_GAIN = 0b01, + .HPC_1k = 0b01, + .RESERVED0 = 0, + }, + .hpfsm_3 = { + /* 14 */ + .HPC_1k_B7B6 = 0b01, + .HPC_DELAY = 0b01, + .HPC_STOP = 0b00, + .HPC_STOP_M2 = 0b11, + .HPC_RXGAIN_EN = 1, + .TXGATE_EN = 1, + .RESERVED0 = 0, + }, + .hpfsm_4 = { + /* 15 */ + .HPC_DIVH = 1, + .RESERVED0 = 0b00000, + .HPC_SEQ_BYP = 0, + .RESERVED1 = 0b00, + .HPC_MODE = 1, + .RESERVED2 = 0, + }, + .spi_en = { + /* 16 */ + .EN_SPI = 0, + .CAL_SPI = 0, + .RESERVED0 = 0b0111, + .PADAC_SPI_EN = 0, + .PADAC_TX_EN = 0, + .RESERVED1 = 0b00, + .RESERVED2 = 0, + }, + .syn_fr_div_1 = { + /* 17 */ + .SYN_FRDIV_9_0 = 0b0101010101, + .RESERVED0 = 0, + }, + .syn_fr_div_2 = { + /* 18 */ + .SYN_FRDIV_19_10 = 0b0101010101, + .RESERVED0 = 0, + }, + .syn_int_div = { + /* 19 */ + .SYN_INTDIV = 0b01010011, + .LOGEN_BSW = 0b01, + .RESERVED0 = 0, + }, + .syn_cfg_1 = { + /* 20 */ + .RESERVED0 = 1, + .SYN_REF_DIV_RATIO = 0b00, + .RESERVED1 = 0b01, + .SYN_CLOCKOUT_DRIVE = 0, + .RESERVED2 = 0b1001, + .RESERVED3 = 0, + }, + .syn_cfg_2 = { + /* 21 */ + .RESERVED0 = 0b0000101101, + .RESERVED1 = 0, + }, + .vas_cfg = { + /* 22 */ + .VAS_MODE = 1, + .VAS_RELOCK_SEL = 0, + .VAS_DIV = 0b010, + .VAS_DLY = 0b01, + .VAS_TRIG_EN = 1, + .RESERVED0 = 0b01, + .RESERVED1 = 0, + }, + .lo_misc = { + /* 23 */ + .VAS_SPI = 0b01111, + .XTAL_BIAS_SEL = 0b10, + .RESERVED0 = 0b100, + .RESERVED1 = 0, + }, + .xtal_cfg = { + /* 24 */ + .XTAL_FTUNE = 0b0000000, + .RESERVED0 = 1, + .XTAL_CLKOUT_DIV = 1, + .XTAL_CORE_EN = 0, + .RESERVED1 = 0, + }, + .vco_cfg = { + /* 25 */ + .RESERVED0 = 0b0000000000, + .RESERVED1 = 0, + }, + .lo_gen = { + /* 26 */ + .RESERVED0 = 0b000, + .LOGEN_2GM = 0, + .RESERVED1 = 0b00, + .VAS_TST = 0b1111, + .RESERVED2 = 0, + }, + .pa_drv = { + /* 27 */ + .TXLO_IQ_SPI = 0b000000, + .TXLO_IQ_SPI_EN = 0, + .TXVGA_GAIN_SPI_EN = 0, + .TX_DCCORR_SPI_EN = 0, + .RESERVED0 = 1, + .RESERVED1 = 0, + }, + .pa_dac = { + /* 28 */ + .PADAC_BIAS = 0b000000, + .PADAC_DLY = 0b0011, + .RESERVED0 = 0, + }, + .tx_gain = { + /* 29 */ + .TXVGA_GAIN_SPI = 0b111111, + .RESERVED0 = 0b0000, + .RESERVED1 = 0, + }, + .tx_lo_iq = { + /* 30 */ + .TX_DCCORR_I = 0b000000, + .RESERVED0 = 0b00, + .PADAC_IV = 1, + .PADAC_VMODE = 1, + .RESERVED1 = 0, + }, + .tx_dc_corr = { + /* 31 */ + .TX_DCCORR_Q = 0b000000, + .RESERVED0 = 0b101, + .PADAC_DIVH = 1, + .RESERVED1 = 0, + }, +}}; class MAX2839 : public MAX283x { -public: - constexpr MAX2839( - spi::arbiter::Target& target - ) : _target(target) - { - } + public: + constexpr MAX2839( + spi::arbiter::Target& target) + : _target(target) { + } - void init() override; - void set_mode(const Mode mode) override; + void init() override; + void set_mode(const Mode mode) override; - void set_tx_vga_gain(const int_fast8_t db) override; - void set_lna_gain(const int_fast8_t db) override; - void set_vga_gain(const int_fast8_t db) override; - void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override; - bool set_frequency(const rf::Frequency lo_frequency) override; - void set_rx_lo_iq_calibration(const size_t v) override; - void set_rx_buff_vcm(const size_t v) override; + void set_tx_vga_gain(const int_fast8_t db) override; + void set_lna_gain(const int_fast8_t db) override; + void set_vga_gain(const int_fast8_t db) override; + void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override; + bool set_frequency(const rf::Frequency lo_frequency) override; + void set_rx_lo_iq_calibration(const size_t v) override; + void set_rx_buff_vcm(const size_t v) override; - reg_t temp_sense() override; + reg_t temp_sense() override; - reg_t read(const address_t reg_num) override; + reg_t read(const address_t reg_num) override; -private: - spi::arbiter::Target& _target; + private: + spi::arbiter::Target& _target; - RegisterMap _map { initial_register_values }; - DirtyRegisters _dirty { }; + RegisterMap _map{initial_register_values}; + DirtyRegisters _dirty{}; - void flush_one(const Register reg); + void flush_one(const Register reg); - void write(const address_t reg_num, const reg_t value); + void write(const address_t reg_num, const reg_t value); - void write(const Register reg, const reg_t value); - reg_t read(const Register reg); + void write(const Register reg, const reg_t value); + reg_t read(const Register reg); - void flush(); + void flush(); - void configure_rx_gain(); + void configure_rx_gain(); }; -} +} // namespace max2839 -#endif/*__MAX2839_H__*/ \ No newline at end of file +#endif /*__MAX2839_H__*/ \ No newline at end of file diff --git a/firmware/application/hw/max283x.hpp b/firmware/application/hw/max283x.hpp index e8b4b3970..042b53d8e 100644 --- a/firmware/application/hw/max283x.hpp +++ b/firmware/application/hw/max283x.hpp @@ -30,12 +30,12 @@ namespace max283x { namespace lo { -constexpr std::array band { { - { 2300000000, 2400000000 }, - { 2400000000, 2500000000 }, - { 2500000000, 2600000000 }, - { 2600000000, 2700000000 }, -} }; +constexpr std::array band{{ + {2300000000, 2400000000}, + {2400000000, 2500000000}, + {2500000000, 2600000000}, + {2600000000, 2700000000}, +}}; } /* namespace lo */ @@ -43,13 +43,13 @@ constexpr std::array band { { namespace lna { -constexpr range_t gain_db_range { 0, 40 }; +constexpr range_t gain_db_range{0, 40}; constexpr int8_t gain_db_step = 8; -constexpr std::array band { { - { 2300000000, 2500000000 }, - { 2500000000, 2700000000 }, -} }; +constexpr std::array band{{ + {2300000000, 2500000000}, + {2500000000, 2700000000}, +}}; } /* namespace lna */ @@ -57,7 +57,7 @@ constexpr std::array band { { namespace vga { -constexpr range_t gain_db_range { 0, 62 }; +constexpr range_t gain_db_range{0, 62}; constexpr int8_t gain_db_step = 2; } /* namespace vga */ @@ -66,32 +66,32 @@ constexpr int8_t gain_db_step = 2; namespace tx { -constexpr range_t gain_db_range { 0, 47 }; +constexpr range_t gain_db_range{0, 47}; constexpr int8_t gain_db_step = 1; -} +} // namespace tx /*************************************************************************/ namespace filter { -constexpr std::array bandwidths { - /* Assumption: these values are in ascending order */ - 1750000, - 2500000, /* Some documentation says 2.25MHz */ - 3500000, - 5000000, - 5500000, - 6000000, - 7000000, - 8000000, - 9000000, - 10000000, - 12000000, - 14000000, - 15000000, - 20000000, - 24000000, - 28000000, +constexpr std::array bandwidths{ + /* Assumption: these values are in ascending order */ + 1750000, + 2500000, /* Some documentation says 2.25MHz */ + 3500000, + 5000000, + 5500000, + 6000000, + 7000000, + 8000000, + 9000000, + 10000000, + 12000000, + 14000000, + 15000000, + 20000000, + 24000000, + 28000000, }; constexpr auto bandwidth_minimum = bandwidths[0]; @@ -102,37 +102,37 @@ constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1]; /*************************************************************************/ enum Mode { - Shutdown, - Standby, - Receive, - Transmit, + Shutdown, + Standby, + Receive, + Transmit, }; using reg_t = uint16_t; using address_t = uint8_t; class MAX283x { -public: - virtual ~MAX283x() = default; + public: + virtual ~MAX283x() = default; - virtual void init(); - virtual void set_mode(const Mode mode); + virtual void init(); + virtual void set_mode(const Mode mode); - virtual void set_tx_vga_gain(const int_fast8_t db); - virtual void set_lna_gain(const int_fast8_t db); - virtual void set_vga_gain(const int_fast8_t db); - virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum); + virtual void set_tx_vga_gain(const int_fast8_t db); + virtual void set_lna_gain(const int_fast8_t db); + virtual void set_vga_gain(const int_fast8_t db); + virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum); - virtual bool set_frequency(const rf::Frequency lo_frequency); + virtual bool set_frequency(const rf::Frequency lo_frequency); - virtual void set_rx_lo_iq_calibration(const size_t v); - virtual void set_rx_buff_vcm(const size_t v); + virtual void set_rx_lo_iq_calibration(const size_t v); + virtual void set_rx_buff_vcm(const size_t v); - virtual reg_t temp_sense(); + virtual reg_t temp_sense(); - virtual reg_t read(const address_t reg_num); + virtual reg_t read(const address_t reg_num); }; -} +} // namespace max283x -#endif/*__MAX283X_H__*/ \ No newline at end of file +#endif /*__MAX283X_H__*/ \ No newline at end of file diff --git a/firmware/application/hw/max5864.cpp b/firmware/application/hw/max5864.cpp index 8c7459ba6..cae2ba5fb 100644 --- a/firmware/application/hw/max5864.cpp +++ b/firmware/application/hw/max5864.cpp @@ -29,8 +29,8 @@ namespace max5864 { void MAX5864::set_mode(const Mode mode) { - std::array command { toUType(mode) }; - _target.transfer(command.data(), command.size()); + std::array command{toUType(mode)}; + _target.transfer(command.data(), command.size()); } -} +} // namespace max5864 diff --git a/firmware/application/hw/max5864.hpp b/firmware/application/hw/max5864.hpp index 86b5c3a51..00b52b407 100644 --- a/firmware/application/hw/max5864.hpp +++ b/firmware/application/hw/max5864.hpp @@ -27,33 +27,32 @@ namespace max5864 { enum class Mode : uint8_t { - Shutdown = 0x00, - Idle = 0x01, - Receive = 0x02, - Transmit = 0x03, - Transceiver = 0x04, - Standby = 0x05, + Shutdown = 0x00, + Idle = 0x01, + Receive = 0x02, + Transmit = 0x03, + Transceiver = 0x04, + Standby = 0x05, }; class MAX5864 { -public: - constexpr MAX5864( - spi::arbiter::Target& target - ) : _target(target) - { - } - - void init() { - /* Shut down explicitly, as there is no other reset mechanism. */ - set_mode(Mode::Shutdown); - } - - void set_mode(const Mode mode); - -private: - spi::arbiter::Target& _target; + public: + constexpr MAX5864( + spi::arbiter::Target& target) + : _target(target) { + } + + void init() { + /* Shut down explicitly, as there is no other reset mechanism. */ + set_mode(Mode::Shutdown); + } + + void set_mode(const Mode mode); + + private: + spi::arbiter::Target& _target; }; -} +} // namespace max5864 -#endif/*__MAX5864_H__*/ +#endif /*__MAX5864_H__*/ diff --git a/firmware/application/hw/rffc507x.cpp b/firmware/application/hw/rffc507x.cpp index fc5a585d7..448187054 100644 --- a/firmware/application/hw/rffc507x.cpp +++ b/firmware/application/hw/rffc507x.cpp @@ -51,7 +51,7 @@ constexpr auto reference_frequency = rffc5072_reference_f; namespace vco { -constexpr rf::FrequencyRange range { 2700000000, 5400000000 }; +constexpr rf::FrequencyRange range{2700000000, 5400000000}; } /* namespace vco */ @@ -63,24 +63,24 @@ constexpr size_t divider_log2_max = 5; constexpr size_t divider_min = 1U << divider_log2_min; constexpr size_t divider_max = 1U << divider_log2_max; -constexpr rf::FrequencyRange range { vco::range.minimum / divider_max, vco::range.maximum / divider_min }; +constexpr rf::FrequencyRange range{vco::range.minimum / divider_max, vco::range.maximum / divider_min}; size_t divider_log2(const rf::Frequency lo_frequency) { - /* TODO: Error */ - /* - if( lo::range.out_of_range(lo_frequency) ) { - return; - } - */ - /* Compute LO divider. */ - auto lo_divider_log2 = lo::divider_log2_min; - auto vco_frequency = lo_frequency; - while( vco::range.below_range(vco_frequency) ) { - vco_frequency <<= 1; - lo_divider_log2 += 1; - } - - return lo_divider_log2; + /* TODO: Error */ + /* + if( lo::range.out_of_range(lo_frequency) ) { + return; + } + */ + /* Compute LO divider. */ + auto lo_divider_log2 = lo::divider_log2_min; + auto vco_frequency = lo_frequency; + while (vco::range.below_range(vco_frequency)) { + vco_frequency <<= 1; + lo_divider_log2 += 1; + } + + return lo_divider_log2; } } /* namespace lo */ @@ -96,42 +96,40 @@ constexpr size_t divider_min = 1U << divider_log2_min; constexpr size_t divider_max = 1U << divider_log2_max; constexpr size_t divider_log2(const rf::Frequency vco_frequency) { - return (vco_frequency > (prescaler::divider_min * prescaler::max_frequency)) - ? prescaler::divider_log2_max - : prescaler::divider_log2_min - ; + return (vco_frequency > (prescaler::divider_min * prescaler::max_frequency)) + ? prescaler::divider_log2_max + : prescaler::divider_log2_min; } } /* namespace prescaler */ struct SynthConfig { - const size_t lo_divider_log2; - const size_t prescaler_divider_log2; - const uint64_t n_divider_q24; - - static SynthConfig calculate( - const rf::Frequency lo_frequency - ) { - /* RFFC507x frequency synthesizer is is accurate to about 2ppb (two parts - * per BILLION). There's not much point to worrying about rounding and - * tuning error, when it amounts to 8Hz at 5GHz! - */ - const size_t lo_divider_log2 = lo::divider_log2(lo_frequency); - const size_t lo_divider = 1U << lo_divider_log2; - - const rf::Frequency vco_frequency = lo_frequency * lo_divider; - - const size_t prescaler_divider_log2 = prescaler::divider_log2(vco_frequency); - - const uint64_t prescaled_lo_q24 = vco_frequency << (24 - prescaler_divider_log2); - const uint64_t n_divider_q24 = prescaled_lo_q24 / reference_frequency; - - return { - lo_divider_log2, - prescaler_divider_log2, - n_divider_q24, - }; - } + const size_t lo_divider_log2; + const size_t prescaler_divider_log2; + const uint64_t n_divider_q24; + + static SynthConfig calculate( + const rf::Frequency lo_frequency) { + /* RFFC507x frequency synthesizer is is accurate to about 2ppb (two parts + * per BILLION). There's not much point to worrying about rounding and + * tuning error, when it amounts to 8Hz at 5GHz! + */ + const size_t lo_divider_log2 = lo::divider_log2(lo_frequency); + const size_t lo_divider = 1U << lo_divider_log2; + + const rf::Frequency vco_frequency = lo_frequency * lo_divider; + + const size_t prescaler_divider_log2 = prescaler::divider_log2(vco_frequency); + + const uint64_t prescaled_lo_q24 = vco_frequency << (24 - prescaler_divider_log2); + const uint64_t n_divider_q24 = prescaled_lo_q24 / reference_frequency; + + return { + lo_divider_log2, + prescaler_divider_log2, + n_divider_q24, + }; + } }; /* Readback values, RFFC5072 rev A: @@ -146,136 +144,136 @@ struct SynthConfig { */ void RFFC507x::init() { - gpio_rffc5072_resetx.set(); - gpio_rffc5072_resetx.output(); - reset(); + gpio_rffc5072_resetx.set(); + gpio_rffc5072_resetx.output(); + reset(); - _bus.init(); + _bus.init(); - _dirty.set(); - flush(); + _dirty.set(); + flush(); } void RFFC507x::reset() { - /* TODO: Is RESETB pin ignored if sdi_ctrl.sipin=1? Programming guide - * description of sdi_ctrl.sipin suggests the pin is not ignored. - */ - gpio_rffc5072_resetx.clear(); - halPolledDelay(ticks_during_reset); - gpio_rffc5072_resetx.set(); - halPolledDelay(ticks_after_reset); + /* TODO: Is RESETB pin ignored if sdi_ctrl.sipin=1? Programming guide + * description of sdi_ctrl.sipin suggests the pin is not ignored. + */ + gpio_rffc5072_resetx.clear(); + halPolledDelay(ticks_during_reset); + gpio_rffc5072_resetx.set(); + halPolledDelay(ticks_after_reset); } void RFFC507x::flush() { - if( _dirty ) { - for(size_t i=0; i<_map.w.size(); i++) { - if( _dirty[i] ) { - write(i, _map.w[i]); - } - } - _dirty.clear(); - } + if (_dirty) { + for (size_t i = 0; i < _map.w.size(); i++) { + if (_dirty[i]) { + write(i, _map.w[i]); + } + } + _dirty.clear(); + } } void RFFC507x::write(const address_t reg_num, const spi::reg_t value) { - _bus.write(reg_num, value); + _bus.write(reg_num, value); } spi::reg_t RFFC507x::read(const address_t reg_num) { - return _bus.read(reg_num); + return _bus.read(reg_num); } void RFFC507x::write(const Register reg, const spi::reg_t value) { - write(toUType(reg), value); + write(toUType(reg), value); } spi::reg_t RFFC507x::read(const Register reg) { - return read(toUType(reg)); + return read(toUType(reg)); } void RFFC507x::flush_one(const Register reg) { - const auto reg_num = toUType(reg); - write(reg_num, _map.w[reg_num]); - _dirty.clear(reg_num); + const auto reg_num = toUType(reg); + write(reg_num, _map.w[reg_num]); + _dirty.clear(reg_num); } void RFFC507x::enable() { - _map.r.sdi_ctrl.enbl = 1; - flush_one(Register::SDI_CTRL); + _map.r.sdi_ctrl.enbl = 1; + flush_one(Register::SDI_CTRL); - /* TODO: Reset PLLCPL after CT_CAL? */ + /* TODO: Reset PLLCPL after CT_CAL? */ - /* TODO: After device is enabled and CT_cal is complete and VCO > 3.2GHz, - * change prescaler divider to 2, update synthesizer ratio, change - * lf.pllcpl from 3 to 2. - */ + /* TODO: After device is enabled and CT_cal is complete and VCO > 3.2GHz, + * change prescaler divider to 2, update synthesizer ratio, change + * lf.pllcpl from 3 to 2. + */ } void RFFC507x::disable() { - _map.r.sdi_ctrl.enbl = 0; - flush_one(Register::SDI_CTRL); + _map.r.sdi_ctrl.enbl = 0; + flush_one(Register::SDI_CTRL); } void RFFC507x::set_mixer_current(const uint8_t value) { - /* MIX IDD = 0b000 appears to turn the mixer completely off */ - /* TODO: Adjust mixer current. Graphs in datasheet suggest: - * MIX_IDD=1 has lowest noise figure (10.1dB vs 13dB @ MIX_IDD=7). - * MIX_IDD=5 has highest IP3 (24dBm vs 10.3dBm @ MIX_IDD=1). - * MIX_IDD=5 has highest P1dB (11.8dBm vs 1.5dBm @ MIX_IDD=1). - * Mixer input impedance ~85 Ohms at MIX_IDD=4. - * Mixer input impedance inversely proportional to MIX_IDD. - * Balun balanced (mixer) side is 100 Ohms. Perhaps reduce MIX_IDD - * a bit to get 100 Ohms from mixer. - */ - _map.r.mix_cont.p1mixidd = value; - _map.r.mix_cont.p2mixidd = value; - flush_one(Register::MIX_CONT); + /* MIX IDD = 0b000 appears to turn the mixer completely off */ + /* TODO: Adjust mixer current. Graphs in datasheet suggest: + * MIX_IDD=1 has lowest noise figure (10.1dB vs 13dB @ MIX_IDD=7). + * MIX_IDD=5 has highest IP3 (24dBm vs 10.3dBm @ MIX_IDD=1). + * MIX_IDD=5 has highest P1dB (11.8dBm vs 1.5dBm @ MIX_IDD=1). + * Mixer input impedance ~85 Ohms at MIX_IDD=4. + * Mixer input impedance inversely proportional to MIX_IDD. + * Balun balanced (mixer) side is 100 Ohms. Perhaps reduce MIX_IDD + * a bit to get 100 Ohms from mixer. + */ + _map.r.mix_cont.p1mixidd = value; + _map.r.mix_cont.p2mixidd = value; + flush_one(Register::MIX_CONT); } void RFFC507x::set_frequency(const rf::Frequency lo_frequency) { - const SynthConfig synth_config = SynthConfig::calculate(lo_frequency); - - /* Boost charge pump leakage if VCO frequency > 3.2GHz, indicated by - * prescaler divider set to 4 (log2=2) instead of 2 (log2=1). - */ - if( synth_config.prescaler_divider_log2 == 2 ) { - _map.r.lf.pllcpl = 3; - } else { - _map.r.lf.pllcpl = 2; - } - flush_one(Register::LF); - - _map.r.p2_freq1.p2n = synth_config.n_divider_q24 >> 24; - _map.r.p2_freq1.p2lodiv = synth_config.lo_divider_log2; - _map.r.p2_freq1.p2presc = synth_config.prescaler_divider_log2; - _map.r.p2_freq2.p2nmsb = (synth_config.n_divider_q24 >> 8) & 0xffff; - _map.r.p2_freq3.p2nlsb = synth_config.n_divider_q24 & 0xff; - _dirty[Register::P2_FREQ1] = 1; - _dirty[Register::P2_FREQ2] = 1; - _dirty[Register::P2_FREQ3] = 1; - flush(); + const SynthConfig synth_config = SynthConfig::calculate(lo_frequency); + + /* Boost charge pump leakage if VCO frequency > 3.2GHz, indicated by + * prescaler divider set to 4 (log2=2) instead of 2 (log2=1). + */ + if (synth_config.prescaler_divider_log2 == 2) { + _map.r.lf.pllcpl = 3; + } else { + _map.r.lf.pllcpl = 2; + } + flush_one(Register::LF); + + _map.r.p2_freq1.p2n = synth_config.n_divider_q24 >> 24; + _map.r.p2_freq1.p2lodiv = synth_config.lo_divider_log2; + _map.r.p2_freq1.p2presc = synth_config.prescaler_divider_log2; + _map.r.p2_freq2.p2nmsb = (synth_config.n_divider_q24 >> 8) & 0xffff; + _map.r.p2_freq3.p2nlsb = synth_config.n_divider_q24 & 0xff; + _dirty[Register::P2_FREQ1] = 1; + _dirty[Register::P2_FREQ2] = 1; + _dirty[Register::P2_FREQ3] = 1; + flush(); } void RFFC507x::set_gpo1(const bool new_value) { - if( new_value ) { - _map.r.gpo.p2gpo |= 1; - _map.r.gpo.p1gpo |= 1; - } else { - _map.r.gpo.p2gpo &= ~1; - _map.r.gpo.p1gpo &= ~1; - } - - flush_one(Register::GPO); + if (new_value) { + _map.r.gpo.p2gpo |= 1; + _map.r.gpo.p1gpo |= 1; + } else { + _map.r.gpo.p2gpo &= ~1; + _map.r.gpo.p1gpo &= ~1; + } + + flush_one(Register::GPO); } spi::reg_t RFFC507x::readback(const Readback readback) { - /* TODO: This clobbers the rest of the DEV_CTRL register - * Time to implement bitfields for registers. - */ - _map.r.dev_ctrl.readsel = toUType(readback); - flush_one(Register::DEV_CTRL); + /* TODO: This clobbers the rest of the DEV_CTRL register + * Time to implement bitfields for registers. + */ + _map.r.dev_ctrl.readsel = toUType(readback); + flush_one(Register::DEV_CTRL); - return read(Register::READBACK); + return read(Register::READBACK); } } /* namespace rffc507x */ diff --git a/firmware/application/hw/rffc507x.hpp b/firmware/application/hw/rffc507x.hpp index e6c903a94..ed76f6462 100644 --- a/firmware/application/hw/rffc507x.hpp +++ b/firmware/application/hw/rffc507x.hpp @@ -38,482 +38,481 @@ using address_t = spi::address_t; constexpr size_t reg_count = 31; enum class Register : address_t { - LF = 0x00, - XO = 0x01, - CAL_TIME = 0x02, - VCO_CTRL = 0x03, - CT_CAL1 = 0x04, - CT_CAL2 = 0x05, - PLL_CAL1 = 0x06, - PLL_CAL2 = 0x07, - VCO_AUTO = 0x08, - PLL_CTRL = 0x09, - PLL_BIAS = 0x0a, - MIX_CONT = 0x0b, - P1_FREQ1 = 0x0c, - P1_FREQ2 = 0x0d, - P1_FREQ3 = 0x0e, - P2_FREQ1 = 0x0f, - P2_FREQ2 = 0x10, - P2_FREQ3 = 0x11, - FN_CTRL = 0x12, - EXT_MOD = 0x13, - FMOD = 0x14, - SDI_CTRL = 0x15, - GPO = 0x16, - T_VCO = 0x17, - IQMOD1 = 0x18, - IQMOD2 = 0x19, - IQMOD3 = 0x1a, - IQMOD4 = 0x1b, - T_CTRL = 0x1c, - DEV_CTRL = 0x1d, - TEST = 0x1e, - READBACK = 0x1f, + LF = 0x00, + XO = 0x01, + CAL_TIME = 0x02, + VCO_CTRL = 0x03, + CT_CAL1 = 0x04, + CT_CAL2 = 0x05, + PLL_CAL1 = 0x06, + PLL_CAL2 = 0x07, + VCO_AUTO = 0x08, + PLL_CTRL = 0x09, + PLL_BIAS = 0x0a, + MIX_CONT = 0x0b, + P1_FREQ1 = 0x0c, + P1_FREQ2 = 0x0d, + P1_FREQ3 = 0x0e, + P2_FREQ1 = 0x0f, + P2_FREQ2 = 0x10, + P2_FREQ3 = 0x11, + FN_CTRL = 0x12, + EXT_MOD = 0x13, + FMOD = 0x14, + SDI_CTRL = 0x15, + GPO = 0x16, + T_VCO = 0x17, + IQMOD1 = 0x18, + IQMOD2 = 0x19, + IQMOD3 = 0x1a, + IQMOD4 = 0x1b, + T_CTRL = 0x1c, + DEV_CTRL = 0x1d, + TEST = 0x1e, + READBACK = 0x1f, }; enum class Readback : uint8_t { - DeviceID = 0b0000, - TuningCalibration = 0b0001, - TuningVoltage = 0b0010, - StateMachine = 0b0011, - VCOCountL = 0b0100, - VCOCountH = 0b0101, - DCOffsetCal = 0b0110, - VCOMode = 0b0111, + DeviceID = 0b0000, + TuningCalibration = 0b0001, + TuningVoltage = 0b0010, + StateMachine = 0b0011, + VCOCountL = 0b0100, + VCOCountH = 0b0101, + DCOffsetCal = 0b0110, + VCOMode = 0b0111, }; struct LF_Type { - reg_t pllcpl : 3; - reg_t p1cpdef : 6; - reg_t p2cpdef : 6; - reg_t lfact : 1; + reg_t pllcpl : 3; + reg_t p1cpdef : 6; + reg_t p2cpdef : 6; + reg_t lfact : 1; }; static_assert(sizeof(LF_Type) == sizeof(reg_t), "LF_Type type wrong size"); struct XO_Type { - reg_t suwait : 10; - reg_t xocf : 1; - reg_t xoc : 4; - reg_t xoch : 1; + reg_t suwait : 10; + reg_t xocf : 1; + reg_t xoc : 4; + reg_t xoch : 1; }; static_assert(sizeof(XO_Type) == sizeof(reg_t), "XO_Type type wrong size"); struct CAL_TIME_Type { - reg_t tkv2 : 4; - reg_t tkv1 : 4; - reg_t reserved0 : 2; - reg_t tct : 5; - reg_t wait : 1; + reg_t tkv2 : 4; + reg_t tkv1 : 4; + reg_t reserved0 : 2; + reg_t tct : 5; + reg_t wait : 1; }; static_assert(sizeof(CAL_TIME_Type) == sizeof(reg_t), "CAL_TIME_Type type wrong size"); struct VCO_CTRL_Type { - reg_t reserved0 : 1; - reg_t icpup : 2; - reg_t refst : 1; - reg_t xoi3 : 1; - reg_t xoi2 : 1; - reg_t xoi1 : 1; - reg_t kvpol : 1; - reg_t kvrng : 1; - reg_t kvavg : 2; - reg_t clkpl : 1; - reg_t ctpol : 1; - reg_t ctavg : 2; - reg_t xtvco : 1; + reg_t reserved0 : 1; + reg_t icpup : 2; + reg_t refst : 1; + reg_t xoi3 : 1; + reg_t xoi2 : 1; + reg_t xoi1 : 1; + reg_t kvpol : 1; + reg_t kvrng : 1; + reg_t kvavg : 2; + reg_t clkpl : 1; + reg_t ctpol : 1; + reg_t ctavg : 2; + reg_t xtvco : 1; }; static_assert(sizeof(VCO_CTRL_Type) == sizeof(reg_t), "VCO_CTRL_Type type wrong size"); struct CT_CAL1_Type { - reg_t p1ctdef : 7; - reg_t p1ct : 1; - reg_t p1ctv : 5; - reg_t p1ctgain : 3; + reg_t p1ctdef : 7; + reg_t p1ct : 1; + reg_t p1ctv : 5; + reg_t p1ctgain : 3; }; static_assert(sizeof(CT_CAL1_Type) == sizeof(reg_t), "CT_CAL1_Type type wrong size"); struct CT_CAL2_Type { - reg_t p2ctdef : 7; - reg_t p2ct : 1; - reg_t p2ctv : 5; - reg_t p2ctgain : 3; + reg_t p2ctdef : 7; + reg_t p2ct : 1; + reg_t p2ctv : 5; + reg_t p2ctgain : 3; }; static_assert(sizeof(CT_CAL2_Type) == sizeof(reg_t), "CT_CAL2_Type type wrong size"); struct PLL_CAL1_Type { - reg_t reserved0 : 2; - reg_t p1sgn : 1; - reg_t p1kvgain : 3; - reg_t p1dn : 9; - reg_t p1kv : 1; + reg_t reserved0 : 2; + reg_t p1sgn : 1; + reg_t p1kvgain : 3; + reg_t p1dn : 9; + reg_t p1kv : 1; }; static_assert(sizeof(PLL_CAL1_Type) == sizeof(reg_t), "PLL_CAL1_Type type wrong size"); struct PLL_CAL2_Type { - reg_t reserved0 : 2; - reg_t p2sgn : 1; - reg_t p2kvgain : 3; - reg_t p2dn : 9; - reg_t p2kv : 1; + reg_t reserved0 : 2; + reg_t p2sgn : 1; + reg_t p2kvgain : 3; + reg_t p2dn : 9; + reg_t p2kv : 1; }; static_assert(sizeof(PLL_CAL2_Type) == sizeof(reg_t), "PLL_CAL2_Type type wrong size"); struct VCO_AUTO_Type { - reg_t reserved0 : 1; - reg_t ctmin : 7; - reg_t ctmax : 7; - reg_t auto_ : 1; + reg_t reserved0 : 1; + reg_t ctmin : 7; + reg_t ctmax : 7; + reg_t auto_ : 1; }; static_assert(sizeof(VCO_AUTO_Type) == sizeof(reg_t), "VCO_AUTO_Type type wrong size"); struct PLL_CTRL_Type { - reg_t plldy : 2; - reg_t aloi : 1; - reg_t relok : 1; - reg_t ldlev : 1; - reg_t lden : 1; - reg_t tvco : 5; - reg_t pllst : 1; - reg_t clkdiv : 3; - reg_t divby : 1; + reg_t plldy : 2; + reg_t aloi : 1; + reg_t relok : 1; + reg_t ldlev : 1; + reg_t lden : 1; + reg_t tvco : 5; + reg_t pllst : 1; + reg_t clkdiv : 3; + reg_t divby : 1; }; static_assert(sizeof(PLL_CTRL_Type) == sizeof(reg_t), "PLL_CTRL_Type type wrong size"); struct PLL_BIAS_Type { - reg_t p2vcoi : 3; - reg_t p2loi : 4; - reg_t reserved0 : 1; - reg_t p1vcoi : 3; - reg_t p1loi : 4; - reg_t reserved1 : 1; + reg_t p2vcoi : 3; + reg_t p2loi : 4; + reg_t reserved0 : 1; + reg_t p1vcoi : 3; + reg_t p1loi : 4; + reg_t reserved1 : 1; }; static_assert(sizeof(PLL_BIAS_Type) == sizeof(reg_t), "PLL_BIAS_Type type wrong size"); struct MIX_CONT_Type { - reg_t reserved0 : 9; - reg_t p2mixidd : 3; - reg_t p1mixidd : 3; - reg_t fulld : 1; + reg_t reserved0 : 9; + reg_t p2mixidd : 3; + reg_t p1mixidd : 3; + reg_t fulld : 1; }; static_assert(sizeof(MIX_CONT_Type) == sizeof(reg_t), "MIX_CONT_Type type wrong size"); struct P1_FREQ1_Type { - reg_t p1vcosel : 2; - reg_t p1presc : 2; - reg_t p1lodiv : 3; - reg_t p1n : 9; + reg_t p1vcosel : 2; + reg_t p1presc : 2; + reg_t p1lodiv : 3; + reg_t p1n : 9; }; static_assert(sizeof(P1_FREQ1_Type) == sizeof(reg_t), "P1_FREQ1_Type type wrong size"); struct P1_FREQ2_Type { - reg_t p1nmsb : 16; + reg_t p1nmsb : 16; }; static_assert(sizeof(P1_FREQ2_Type) == sizeof(reg_t), "P1_FREQ2_Type type wrong size"); struct P1_FREQ3_Type { - reg_t reserved0 : 8; - reg_t p1nlsb : 8; + reg_t reserved0 : 8; + reg_t p1nlsb : 8; }; static_assert(sizeof(P1_FREQ3_Type) == sizeof(reg_t), "P1_FREQ3_Type type wrong size"); struct P2_FREQ1_Type { - reg_t p2vcosel : 2; - reg_t p2presc : 2; - reg_t p2lodiv : 3; - reg_t p2n : 9; + reg_t p2vcosel : 2; + reg_t p2presc : 2; + reg_t p2lodiv : 3; + reg_t p2n : 9; }; static_assert(sizeof(P2_FREQ1_Type) == sizeof(reg_t), "P2_FREQ1_Type type wrong size"); struct P2_FREQ2_Type { - reg_t p2nmsb : 16; + reg_t p2nmsb : 16; }; static_assert(sizeof(P2_FREQ2_Type) == sizeof(reg_t), "P2_FREQ2_Type type wrong size"); struct P2_FREQ3_Type { - reg_t reserved0 : 8; - reg_t p2nlsb : 8; + reg_t reserved0 : 8; + reg_t p2nlsb : 8; }; static_assert(sizeof(P2_FREQ3_Type) == sizeof(reg_t), "P2_FREQ3_Type type wrong size"); struct FN_CTRL_Type { - reg_t reserved0 : 1; - reg_t tzps : 1; - reg_t dmode : 1; - reg_t fm : 1; - reg_t dith : 1; - reg_t mode : 1; - reg_t phsalndly : 2; - reg_t phsalngain : 3; - reg_t phaln : 1; - reg_t sdm : 2; - reg_t dithr : 1; - reg_t fnz : 1; + reg_t reserved0 : 1; + reg_t tzps : 1; + reg_t dmode : 1; + reg_t fm : 1; + reg_t dith : 1; + reg_t mode : 1; + reg_t phsalndly : 2; + reg_t phsalngain : 3; + reg_t phaln : 1; + reg_t sdm : 2; + reg_t dithr : 1; + reg_t fnz : 1; }; static_assert(sizeof(FN_CTRL_Type) == sizeof(reg_t), "FN_CTRL_Type type wrong size"); struct EXT_MOD_Type { - reg_t reserved0 : 10; - reg_t modstep : 4; - reg_t modsetup : 2; + reg_t reserved0 : 10; + reg_t modstep : 4; + reg_t modsetup : 2; }; static_assert(sizeof(EXT_MOD_Type) == sizeof(reg_t), "EXT_MOD_Type type wrong size"); struct FMOD_Type { - reg_t modulation : 16; + reg_t modulation : 16; }; static_assert(sizeof(FMOD_Type) == sizeof(reg_t), "FMOD_Type type wrong size"); struct SDI_CTRL_Type { - reg_t reserved0 : 1; - reg_t reset : 1; - reg_t reserved1 : 9; - reg_t addr : 1; - reg_t fourwire : 1; - reg_t mode : 1; - reg_t enbl : 1; - reg_t sipin : 1; + reg_t reserved0 : 1; + reg_t reset : 1; + reg_t reserved1 : 9; + reg_t addr : 1; + reg_t fourwire : 1; + reg_t mode : 1; + reg_t enbl : 1; + reg_t sipin : 1; }; static_assert(sizeof(SDI_CTRL_Type) == sizeof(reg_t), "SDI_CTRL_Type type wrong size"); struct GPO_Type { - reg_t lock : 1; - reg_t gate : 1; - reg_t p1gpo : 7; - reg_t p2gpo : 7; + reg_t lock : 1; + reg_t gate : 1; + reg_t p1gpo : 7; + reg_t p2gpo : 7; }; static_assert(sizeof(GPO_Type) == sizeof(reg_t), "GPO_Type type wrong size"); struct T_VCO_Type { - reg_t reserved0 : 7; - reg_t curve_vco3 : 3; - reg_t curve_vco2 : 3; - reg_t curve_vco1 : 3; + reg_t reserved0 : 7; + reg_t curve_vco3 : 3; + reg_t curve_vco2 : 3; + reg_t curve_vco1 : 3; }; static_assert(sizeof(T_VCO_Type) == sizeof(reg_t), "T_VCO_Type type wrong size"); struct IQMOD1_Type { - reg_t bufdc : 2; - reg_t divbias : 1; - reg_t calblk : 1; - reg_t calnul : 1; - reg_t calon : 1; - reg_t lobias : 2; - reg_t modbias : 3; - /* Also defined as ctrl : 5 */ - reg_t modiv : 1; - reg_t mod : 1; - reg_t txlo : 1; - reg_t bbgm : 1; - reg_t ctrl : 1; + reg_t bufdc : 2; + reg_t divbias : 1; + reg_t calblk : 1; + reg_t calnul : 1; + reg_t calon : 1; + reg_t lobias : 2; + reg_t modbias : 3; + /* Also defined as ctrl : 5 */ + reg_t modiv : 1; + reg_t mod : 1; + reg_t txlo : 1; + reg_t bbgm : 1; + reg_t ctrl : 1; }; static_assert(sizeof(IQMOD1_Type) == sizeof(reg_t), "IQMOD1_Type type wrong size"); struct IQMOD2_Type { - reg_t modbuf : 2; - reg_t mod : 2; - reg_t calatten : 2; - reg_t rctune : 6; - reg_t bbatten : 4; + reg_t modbuf : 2; + reg_t mod : 2; + reg_t calatten : 2; + reg_t rctune : 6; + reg_t bbatten : 4; }; static_assert(sizeof(IQMOD2_Type) == sizeof(reg_t), "IQMOD2_Type type wrong size"); struct IQMOD3_Type { - /* Documentation error */ - reg_t reserved0 : 3; - reg_t dacen : 1; - reg_t bufdacq : 6; - reg_t bufdaci : 6; + /* Documentation error */ + reg_t reserved0 : 3; + reg_t dacen : 1; + reg_t bufdacq : 6; + reg_t bufdaci : 6; }; static_assert(sizeof(IQMOD3_Type) == sizeof(reg_t), "IQMOD3_Type type wrong size"); struct IQMOD4_Type { - /* Documentation error */ - reg_t bufbias2 : 2; - reg_t bufbias1 : 2; - reg_t moddacq : 6; - reg_t moddaci : 6; + /* Documentation error */ + reg_t bufbias2 : 2; + reg_t bufbias1 : 2; + reg_t moddacq : 6; + reg_t moddaci : 6; }; static_assert(sizeof(IQMOD4_Type) == sizeof(reg_t), "IQMOD4_Type type wrong size"); struct T_CTRL_Type { - reg_t reserved0 : 5; - reg_t v_test : 1; - reg_t ldo_by : 1; - reg_t ext_filt : 1; - reg_t ref_sel : 1; - reg_t filt_ctrl : 2; - reg_t fc_en : 1; - reg_t tbl_sel : 2; - reg_t tc_en : 2; + reg_t reserved0 : 5; + reg_t v_test : 1; + reg_t ldo_by : 1; + reg_t ext_filt : 1; + reg_t ref_sel : 1; + reg_t filt_ctrl : 2; + reg_t fc_en : 1; + reg_t tbl_sel : 2; + reg_t tc_en : 2; }; static_assert(sizeof(T_CTRL_Type) == sizeof(reg_t), "T_CTRL_Type type wrong size"); struct DEV_CTRL_Type { - reg_t reserved0 : 1; - reg_t bypas : 1; - reg_t ctclk : 1; - reg_t dac : 1; - reg_t cpd : 1; - reg_t cpu : 1; - reg_t rsmstopst : 5; - reg_t rsmst : 1; - reg_t readsel : 4; + reg_t reserved0 : 1; + reg_t bypas : 1; + reg_t ctclk : 1; + reg_t dac : 1; + reg_t cpd : 1; + reg_t cpu : 1; + reg_t rsmstopst : 5; + reg_t rsmst : 1; + reg_t readsel : 4; }; static_assert(sizeof(DEV_CTRL_Type) == sizeof(reg_t), "DEV_CTRL_Type type wrong size"); struct TEST_Type { - reg_t lfsrd : 1; - reg_t rcbyp : 1; - reg_t rgbyp : 1; - reg_t lfsrt : 1; - reg_t lfsrgatetime : 4; - reg_t lfsrp : 1; - reg_t lfsr : 1; - reg_t tsel : 2; - reg_t tmux : 3; - reg_t ten : 1; + reg_t lfsrd : 1; + reg_t rcbyp : 1; + reg_t rgbyp : 1; + reg_t lfsrt : 1; + reg_t lfsrgatetime : 4; + reg_t lfsrp : 1; + reg_t lfsr : 1; + reg_t tsel : 2; + reg_t tmux : 3; + reg_t ten : 1; }; static_assert(sizeof(TEST_Type) == sizeof(reg_t), "TEST_Type type wrong size"); struct READBACK_0000_Type { - reg_t mrev_id : 3; - reg_t dev_id : 13; + reg_t mrev_id : 3; + reg_t dev_id : 13; }; static_assert(sizeof(READBACK_0000_Type) == sizeof(reg_t), "READBACK_0000_Type type wrong size"); struct READBACK_0001_Type { - reg_t reserved0 : 1; - reg_t ctfail : 1; - reg_t cp_cal : 6; - reg_t ct_cal : 7; - reg_t lock : 1; + reg_t reserved0 : 1; + reg_t ctfail : 1; + reg_t cp_cal : 6; + reg_t ct_cal : 7; + reg_t lock : 1; }; static_assert(sizeof(READBACK_0001_Type) == sizeof(reg_t), "READBACK_0001_Type type wrong size"); struct READBACK_0010_Type { - reg_t v1_cal : 8; - reg_t v0_cal : 8; + reg_t v1_cal : 8; + reg_t v0_cal : 8; }; static_assert(sizeof(READBACK_0010_Type) == sizeof(reg_t), "READBACK_0010_Type type wrong size"); struct READBACK_0011_Type { - reg_t reserved0 : 9; - reg_t f_errflag : 2; - reg_t rsm_state : 5; + reg_t reserved0 : 9; + reg_t f_errflag : 2; + reg_t rsm_state : 5; }; static_assert(sizeof(READBACK_0011_Type) == sizeof(reg_t), "READBACK_0011_Type type wrong size"); struct READBACK_0100_Type { - reg_t vco_count_l : 16; + reg_t vco_count_l : 16; }; static_assert(sizeof(READBACK_0100_Type) == sizeof(reg_t), "READBACK_0100_Type type wrong size"); struct READBACK_0101_Type { - reg_t vco_count_h : 16; + reg_t vco_count_h : 16; }; static_assert(sizeof(READBACK_0101_Type) == sizeof(reg_t), "READBACK_0101_Type type wrong size"); struct READBACK_0110_Type { - reg_t reserved0 : 14; - reg_t cal_fbq : 1; - reg_t cal_fbi : 1; + reg_t reserved0 : 14; + reg_t cal_fbq : 1; + reg_t cal_fbi : 1; }; static_assert(sizeof(READBACK_0110_Type) == sizeof(reg_t), "READBACK_0110_Type type wrong size"); struct READBACK_0111_Type { - reg_t reserved0 : 11; - reg_t vco_tc_curve : 3; - reg_t vco_sel : 2; + reg_t reserved0 : 11; + reg_t vco_tc_curve : 3; + reg_t vco_sel : 2; }; static_assert(sizeof(READBACK_0111_Type) == sizeof(reg_t), "READBACK_0111_Type type wrong size"); struct Register_Type { - LF_Type lf; - XO_Type xo; - CAL_TIME_Type cal_time; - VCO_CTRL_Type vco_ctrl; - CT_CAL1_Type ct_cal1; - CT_CAL2_Type ct_cal2; - PLL_CAL1_Type pll_cal1; - PLL_CAL2_Type pll_cal2; - VCO_AUTO_Type vco_auto; - PLL_CTRL_Type pll_ctrl; - PLL_BIAS_Type pll_bias; - MIX_CONT_Type mix_cont; - P1_FREQ1_Type p1_freq1; - P1_FREQ2_Type p1_freq2; - P1_FREQ3_Type p1_freq3; - P2_FREQ1_Type p2_freq1; - P2_FREQ2_Type p2_freq2; - P2_FREQ3_Type p2_freq3; - FN_CTRL_Type fn_ctrl; - EXT_MOD_Type ext_mod; - FMOD_Type fmod; - SDI_CTRL_Type sdi_ctrl; - GPO_Type gpo; - T_VCO_Type t_vco; - IQMOD1_Type iqmod1; - IQMOD2_Type iqmod2; - IQMOD3_Type iqmod3; - IQMOD4_Type iqmod4; - T_CTRL_Type t_ctrl; - DEV_CTRL_Type dev_ctrl; - TEST_Type test; + LF_Type lf; + XO_Type xo; + CAL_TIME_Type cal_time; + VCO_CTRL_Type vco_ctrl; + CT_CAL1_Type ct_cal1; + CT_CAL2_Type ct_cal2; + PLL_CAL1_Type pll_cal1; + PLL_CAL2_Type pll_cal2; + VCO_AUTO_Type vco_auto; + PLL_CTRL_Type pll_ctrl; + PLL_BIAS_Type pll_bias; + MIX_CONT_Type mix_cont; + P1_FREQ1_Type p1_freq1; + P1_FREQ2_Type p1_freq2; + P1_FREQ3_Type p1_freq3; + P2_FREQ1_Type p2_freq1; + P2_FREQ2_Type p2_freq2; + P2_FREQ3_Type p2_freq3; + FN_CTRL_Type fn_ctrl; + EXT_MOD_Type ext_mod; + FMOD_Type fmod; + SDI_CTRL_Type sdi_ctrl; + GPO_Type gpo; + T_VCO_Type t_vco; + IQMOD1_Type iqmod1; + IQMOD2_Type iqmod2; + IQMOD3_Type iqmod3; + IQMOD4_Type iqmod4; + T_CTRL_Type t_ctrl; + DEV_CTRL_Type dev_ctrl; + TEST_Type test; }; static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size"); struct RegisterMap { - constexpr RegisterMap( - Register_Type values - ) : r(values) - { - } + constexpr RegisterMap( + Register_Type values) + : r(values) { + } - union { - Register_Type r; - std::array w; - }; + union { + Register_Type r; + std::array w; + }; }; static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size"); @@ -561,275 +560,306 @@ constexpr RegisterMap default_revision_2 { std::array { 0xc840, 0x1000, 0x0005, } }; #endif -constexpr RegisterMap default_hackrf_one { Register_Type { - /* Started with recommended defaults for revision 1 devices - * (mrev_id = 001), RFFC2071/2072/5071/5072, RFMD2080/2081. - * Modified according to mixer programming guide. - */ - .lf = { /* 0 */ - .pllcpl = 0b010, - .p1cpdef = 0b011111, - .p2cpdef = 0b011111, - .lfact = 1, - }, - .xo = { /* 1 */ - .suwait = 0b0001100100, - .xocf = 0b0, - .xoc = 0b1000, - .xoch = 0b0, - }, - .cal_time = { /* 2 */ - .tkv2 = 0b0101, - .tkv1 = 0b0101, - .reserved0 = 0b00, - .tct = 0b00100, - .wait = 0b1, - }, - .vco_ctrl = { /* 3 */ - .reserved0 = 0b0, - .icpup = 0b01, - .refst = 0b0, - .xoi3 = 0b0, - .xoi2 = 0b0, - .xoi1 = 0b0, - .kvpol = 0b0, - .kvrng = 0b1, - .kvavg = 0b10, - .clkpl = 0b1, - .ctpol = 0b0, - .ctavg = 0b01, - .xtvco = 0b0, - }, - .ct_cal1 = { /* 4 */ - .p1ctdef = 0b0111111, - .p1ct = 0b1, - .p1ctv = 12, /* RFMD recommneded change: 16 -> 12 */ - .p1ctgain = 0b101, - }, - .ct_cal2 = { /* 5 */ - .p2ctdef = 0b0111111, - .p2ct = 0b1, - .p2ctv = 12, /* RFMD recommneded change: 16 -> 12 */ - .p2ctgain = 0b101, - }, - .pll_cal1 = { /* 6 */ - .reserved0 = 0b00, - .p1sgn = 0b0, - .p1kvgain = 0b101, - .p1dn = 0b000000000, - .p1kv = 0b0, - }, - .pll_cal2 = { /* 7 */ - .reserved0 = 0b00, - .p2sgn = 0b0, - .p2kvgain = 0b101, - .p2dn = 0b000000000, - .p2kv = 0b0, - }, - .vco_auto = { /* 8 */ - .reserved0 = 0b0, - .ctmin = 0, /* RFMD recommended change: 3 -> 0 */ - .ctmax = 127, /* RFMD recommended change: 124 -> 127 */ - .auto_ = 0b1, - }, - .pll_ctrl = { /* 9 */ - .plldy = 0b00, - .aloi = 0b0, - .relok = 0b0, - .ldlev = 0b0, - .lden = 0b1, - .tvco = 0b01000, - .pllst = 0b0, - .clkdiv = 0b000, - .divby = 0b1, - }, - .pll_bias = { /* 10 */ - .p2vcoi = 0b010, - .p2loi = 0b0000, - .reserved0 = 0b0, - .p1vcoi = 0b010, - .p1loi = 0b0000, - .reserved1 = 0b0, - }, - .mix_cont = { /* 11 */ - .reserved0 = 0b000000000, - .p2mixidd = 4, - .p1mixidd = 4, - .fulld = 0b0, /* Part on HackRF is half-duplex (single mixer) */ - }, - .p1_freq1 = { /* 12 */ - .p1vcosel = 0b00, /* RFMD VCO bank 1 configuration from A series part */ - .p1presc = 0b01, - .p1lodiv = 0b001, - .p1n = 0b000110101, - }, - .p1_freq2 = { /* 13 */ - .p1nmsb = 0xd89d, /* RFMD VCO bank 1 configuration from A series part */ - }, - .p1_freq3 = { /* 14 */ - .reserved0 = 0b00000000, /* RFMD VCO bank 1 configuration from A series part */ - .p1nlsb = 0x89, - }, - .p2_freq1 = { /* 15 */ - .p2vcosel = 0b00, /* RFMD VCO bank 2 configuration from A series part */ - .p2presc = 0b01, - .p2lodiv = 0b000, - .p2n = 0b000111101, - }, - .p2_freq2 = { /* 16 */ - .p2nmsb = 0x89d8, /* RFMD VCO bank 2 configuration from A series part */ - }, - .p2_freq3 = { /* 17 */ - .reserved0 = 0b00000000, /* RFMD VCO bank 2 configuration from A series part */ - .p2nlsb = 0x9d, - }, - .fn_ctrl = { /* 18 */ - .reserved0 = 0b0, - .tzps = 0b0, - .dmode = 0b0, - .fm = 0b0, - .dith = 0b0, - .mode = 0b0, - .phsalndly = 0b10, - .phsalngain = 0b010, - .phaln = 0b1, - .sdm = 0b10, - .dithr = 0b0, - .fnz = 0b0, - }, - .ext_mod = { /* 19 */ - .reserved0 = 0b0000000000, - .modstep = 0b0000, - .modsetup = 0b00, - }, - .fmod = { /* 20 */ - .modulation = 0x0000, - }, - .sdi_ctrl = { /* 21 */ - .reserved0 = 0b0, - .reset = 0b0, - .reserved1 = 0b000000000, - .addr = 0b0, - .fourwire = 0b0, /* Use three pin SPI mode */ - .mode = 0b1, /* Active PLL register bank 2, active mixer 2 */ - .enbl = 0b0, /* Part is initially disabled */ - .sipin = 0b1, /* Control MODE, ENBL from SPI bus */ - }, - .gpo = { /* 22 */ - .lock = 0b1, /* Present LOCK signal on GPIO4/LD/DO, HackRF One test point P6 */ - .gate = 0b1, /* GPOs active even when part is disabled (ENBL=0) */ - .p1gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */ - .p2gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */ - }, - .t_vco = { /* 23 */ - .reserved0 = 0b0000000, - .curve_vco3 = 0b000, - .curve_vco2 = 0b000, - .curve_vco1 = 0b000, - }, - .iqmod1 = { /* 24 */ - .bufdc = 0b11, - .divbias = 0b0, - .calblk = 0b0, - .calnul = 0b0, - .calon = 0b0, - .lobias = 0b10, - .modbias = 0b010, - .modiv = 0b0, - .mod = 0b0, - .txlo = 0b0, - .bbgm = 0b0, - .ctrl = 0b0, - }, - .iqmod2 = { /* 25 */ - .modbuf = 0b11, - .mod = 0b11, - .calatten = 0b00, - .rctune = 0b000000, - .bbatten = 0b1111, - }, - .iqmod3 = { /* 26 */ - .reserved0 = 0b000, - .dacen = 0b0, - .bufdacq = 0b000000, - .bufdaci = 0b000000, - }, - .iqmod4 = { /* 27 */ - .bufbias2 = 0b11, - .bufbias1 = 0b11, - .moddacq = 0b000000, - .moddaci = 0b000000, - }, - .t_ctrl = { /* 28 */ - .reserved0 = 0b00000, - .v_test = 0b0, - .ldo_by = 0b0, - .ext_filt = 0b0, - .ref_sel = 0b0, - .filt_ctrl = 0b00, - .fc_en = 0b0, - .tbl_sel = 0b00, - .tc_en = 0b00, - }, - .dev_ctrl = { /* 29 */ - .reserved0 = 0b0, - .bypas = 0b0, - .ctclk = 0b0, - .dac = 0b0, - .cpd = 0b0, - .cpu = 0b0, - .rsmstopst = 0b00000, - .rsmst = 0b0, - .readsel = 0b0001, - }, - .test = { /* 30 */ - .lfsrd = 0b1, - .rcbyp = 0b0, - .rgbyp = 0b1, /* RFMD recommended change: 0 -> 1 */ - .lfsrt = 0b0, - .lfsrgatetime = 0b0000, - .lfsrp = 0b0, - .lfsr = 0b0, - .tsel = 0b00, - .tmux = 0b000, - .ten = 0b0, - }, -} }; +constexpr RegisterMap default_hackrf_one{Register_Type{ + /* Started with recommended defaults for revision 1 devices + * (mrev_id = 001), RFFC2071/2072/5071/5072, RFMD2080/2081. + * Modified according to mixer programming guide. + */ + .lf = { + /* 0 */ + .pllcpl = 0b010, + .p1cpdef = 0b011111, + .p2cpdef = 0b011111, + .lfact = 1, + }, + .xo = { + /* 1 */ + .suwait = 0b0001100100, + .xocf = 0b0, + .xoc = 0b1000, + .xoch = 0b0, + }, + .cal_time = { + /* 2 */ + .tkv2 = 0b0101, + .tkv1 = 0b0101, + .reserved0 = 0b00, + .tct = 0b00100, + .wait = 0b1, + }, + .vco_ctrl = { + /* 3 */ + .reserved0 = 0b0, + .icpup = 0b01, + .refst = 0b0, + .xoi3 = 0b0, + .xoi2 = 0b0, + .xoi1 = 0b0, + .kvpol = 0b0, + .kvrng = 0b1, + .kvavg = 0b10, + .clkpl = 0b1, + .ctpol = 0b0, + .ctavg = 0b01, + .xtvco = 0b0, + }, + .ct_cal1 = { + /* 4 */ + .p1ctdef = 0b0111111, + .p1ct = 0b1, + .p1ctv = 12, /* RFMD recommneded change: 16 -> 12 */ + .p1ctgain = 0b101, + }, + .ct_cal2 = { + /* 5 */ + .p2ctdef = 0b0111111, + .p2ct = 0b1, + .p2ctv = 12, /* RFMD recommneded change: 16 -> 12 */ + .p2ctgain = 0b101, + }, + .pll_cal1 = { + /* 6 */ + .reserved0 = 0b00, + .p1sgn = 0b0, + .p1kvgain = 0b101, + .p1dn = 0b000000000, + .p1kv = 0b0, + }, + .pll_cal2 = { + /* 7 */ + .reserved0 = 0b00, + .p2sgn = 0b0, + .p2kvgain = 0b101, + .p2dn = 0b000000000, + .p2kv = 0b0, + }, + .vco_auto = { + /* 8 */ + .reserved0 = 0b0, + .ctmin = 0, /* RFMD recommended change: 3 -> 0 */ + .ctmax = 127, /* RFMD recommended change: 124 -> 127 */ + .auto_ = 0b1, + }, + .pll_ctrl = { + /* 9 */ + .plldy = 0b00, + .aloi = 0b0, + .relok = 0b0, + .ldlev = 0b0, + .lden = 0b1, + .tvco = 0b01000, + .pllst = 0b0, + .clkdiv = 0b000, + .divby = 0b1, + }, + .pll_bias = { + /* 10 */ + .p2vcoi = 0b010, + .p2loi = 0b0000, + .reserved0 = 0b0, + .p1vcoi = 0b010, + .p1loi = 0b0000, + .reserved1 = 0b0, + }, + .mix_cont = { + /* 11 */ + .reserved0 = 0b000000000, + .p2mixidd = 4, + .p1mixidd = 4, + .fulld = 0b0, /* Part on HackRF is half-duplex (single mixer) */ + }, + .p1_freq1 = { + /* 12 */ + .p1vcosel = 0b00, /* RFMD VCO bank 1 configuration from A series part */ + .p1presc = 0b01, + .p1lodiv = 0b001, + .p1n = 0b000110101, + }, + .p1_freq2 = { + /* 13 */ + .p1nmsb = 0xd89d, /* RFMD VCO bank 1 configuration from A series part */ + }, + .p1_freq3 = { + /* 14 */ + .reserved0 = 0b00000000, /* RFMD VCO bank 1 configuration from A series part */ + .p1nlsb = 0x89, + }, + .p2_freq1 = { + /* 15 */ + .p2vcosel = 0b00, /* RFMD VCO bank 2 configuration from A series part */ + .p2presc = 0b01, + .p2lodiv = 0b000, + .p2n = 0b000111101, + }, + .p2_freq2 = { + /* 16 */ + .p2nmsb = 0x89d8, /* RFMD VCO bank 2 configuration from A series part */ + }, + .p2_freq3 = { + /* 17 */ + .reserved0 = 0b00000000, /* RFMD VCO bank 2 configuration from A series part */ + .p2nlsb = 0x9d, + }, + .fn_ctrl = { + /* 18 */ + .reserved0 = 0b0, + .tzps = 0b0, + .dmode = 0b0, + .fm = 0b0, + .dith = 0b0, + .mode = 0b0, + .phsalndly = 0b10, + .phsalngain = 0b010, + .phaln = 0b1, + .sdm = 0b10, + .dithr = 0b0, + .fnz = 0b0, + }, + .ext_mod = { + /* 19 */ + .reserved0 = 0b0000000000, + .modstep = 0b0000, + .modsetup = 0b00, + }, + .fmod = { + /* 20 */ + .modulation = 0x0000, + }, + .sdi_ctrl = { + /* 21 */ + .reserved0 = 0b0, + .reset = 0b0, + .reserved1 = 0b000000000, + .addr = 0b0, + .fourwire = 0b0, /* Use three pin SPI mode */ + .mode = 0b1, /* Active PLL register bank 2, active mixer 2 */ + .enbl = 0b0, /* Part is initially disabled */ + .sipin = 0b1, /* Control MODE, ENBL from SPI bus */ + }, + .gpo = { + /* 22 */ + .lock = 0b1, /* Present LOCK signal on GPIO4/LD/DO, HackRF One test point P6 */ + .gate = 0b1, /* GPOs active even when part is disabled (ENBL=0) */ + .p1gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */ + .p2gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */ + }, + .t_vco = { + /* 23 */ + .reserved0 = 0b0000000, + .curve_vco3 = 0b000, + .curve_vco2 = 0b000, + .curve_vco1 = 0b000, + }, + .iqmod1 = { + /* 24 */ + .bufdc = 0b11, + .divbias = 0b0, + .calblk = 0b0, + .calnul = 0b0, + .calon = 0b0, + .lobias = 0b10, + .modbias = 0b010, + .modiv = 0b0, + .mod = 0b0, + .txlo = 0b0, + .bbgm = 0b0, + .ctrl = 0b0, + }, + .iqmod2 = { + /* 25 */ + .modbuf = 0b11, + .mod = 0b11, + .calatten = 0b00, + .rctune = 0b000000, + .bbatten = 0b1111, + }, + .iqmod3 = { + /* 26 */ + .reserved0 = 0b000, + .dacen = 0b0, + .bufdacq = 0b000000, + .bufdaci = 0b000000, + }, + .iqmod4 = { + /* 27 */ + .bufbias2 = 0b11, + .bufbias1 = 0b11, + .moddacq = 0b000000, + .moddaci = 0b000000, + }, + .t_ctrl = { + /* 28 */ + .reserved0 = 0b00000, + .v_test = 0b0, + .ldo_by = 0b0, + .ext_filt = 0b0, + .ref_sel = 0b0, + .filt_ctrl = 0b00, + .fc_en = 0b0, + .tbl_sel = 0b00, + .tc_en = 0b00, + }, + .dev_ctrl = { + /* 29 */ + .reserved0 = 0b0, + .bypas = 0b0, + .ctclk = 0b0, + .dac = 0b0, + .cpd = 0b0, + .cpu = 0b0, + .rsmstopst = 0b00000, + .rsmst = 0b0, + .readsel = 0b0001, + }, + .test = { + /* 30 */ + .lfsrd = 0b1, + .rcbyp = 0b0, + .rgbyp = 0b1, /* RFMD recommended change: 0 -> 1 */ + .lfsrt = 0b0, + .lfsrgatetime = 0b0000, + .lfsrp = 0b0, + .lfsr = 0b0, + .tsel = 0b00, + .tmux = 0b000, + .ten = 0b0, + }, +}}; class RFFC507x { -public: - void init(); - void reset(); + public: + void init(); + void reset(); + + void flush(); - void flush(); + void enable(); + void disable(); - void enable(); - void disable(); + void set_mixer_current(const uint8_t value); + void set_frequency(const rf::Frequency lo_frequency); + void set_gpo1(const bool new_value); - void set_mixer_current(const uint8_t value); - void set_frequency(const rf::Frequency lo_frequency); - void set_gpo1(const bool new_value); - - reg_t read(const address_t reg_num); + reg_t read(const address_t reg_num); -private: - spi::SPI _bus { }; + private: + spi::SPI _bus{}; - RegisterMap _map { default_hackrf_one }; - DirtyRegisters _dirty { }; + RegisterMap _map{default_hackrf_one}; + DirtyRegisters _dirty{}; - void write(const address_t reg_num, const reg_t value); + void write(const address_t reg_num, const reg_t value); - void write(const Register reg, const reg_t value); - reg_t read(const Register reg); + void write(const Register reg, const reg_t value); + reg_t read(const Register reg); - void flush_one(const Register reg); + void flush_one(const Register reg); - reg_t readback(const Readback readback); + reg_t readback(const Readback readback); - void init_for_best_performance(); + void init_for_best_performance(); }; -} /* rffc507x */ +} // namespace rffc507x -#endif/*__RFFC507X_H__*/ +#endif /*__RFFC507X_H__*/ diff --git a/firmware/application/hw/rffc507x_spi.cpp b/firmware/application/hw/rffc507x_spi.cpp index 049fa0336..af5f9c8fc 100644 --- a/firmware/application/hw/rffc507x_spi.cpp +++ b/firmware/application/hw/rffc507x_spi.cpp @@ -30,80 +30,78 @@ namespace rffc507x { namespace spi { void SPI::init() { - gpio_rffc5072_select.set(); - gpio_rffc5072_clock.clear(); + gpio_rffc5072_select.set(); + gpio_rffc5072_clock.clear(); - gpio_rffc5072_select.output(); - gpio_rffc5072_clock.output(); - gpio_rffc5072_data.input(); + gpio_rffc5072_select.output(); + gpio_rffc5072_clock.output(); + gpio_rffc5072_data.input(); - gpio_rffc5072_data.clear(); + gpio_rffc5072_data.clear(); } inline void SPI::select(const bool active) { - gpio_rffc5072_select.write(!active); + gpio_rffc5072_select.write(!active); } inline void SPI::direction_out() { - gpio_rffc5072_data.output(); + gpio_rffc5072_data.output(); } inline void SPI::direction_in() { - gpio_rffc5072_data.input(); + gpio_rffc5072_data.input(); } inline void SPI::write_bit(const bit_t value) { - gpio_rffc5072_data.write(value); + gpio_rffc5072_data.write(value); } inline bit_t SPI::read_bit() { - return gpio_rffc5072_data.read() & 1; + return gpio_rffc5072_data.read() & 1; } inline bit_t SPI::transfer_bit(const bit_t bit_out) { - gpio_rffc5072_clock.clear(); - write_bit(bit_out); - const bit_t bit_in = read_bit(); - gpio_rffc5072_clock.set(); - return bit_in; + gpio_rffc5072_clock.clear(); + write_bit(bit_out); + const bit_t bit_in = read_bit(); + gpio_rffc5072_clock.set(); + return bit_in; } data_t SPI::transfer_bits(const data_t data_out, const size_t count) { - data_t data_in = 0; - for(size_t i=0; i> (count - i - 1)) & 1); - } - return data_in; + data_t data_in = 0; + for (size_t i = 0; i < count; i++) { + data_in = (data_in << 1) | transfer_bit((data_out >> (count - i - 1)) & 1); + } + return data_in; } data_t SPI::transfer_word(const Direction direction, const address_t address, const data_t data_out) { - select(true); + select(true); - const data_t address_word = - ((direction == Direction::Read) ? (1 << 7) : 0) - | (address & 0x7f) - ; + const data_t address_word = + ((direction == Direction::Read) ? (1 << 7) : 0) | (address & 0x7f); - direction_out(); - transfer_bits(address_word, 9); + direction_out(); + transfer_bits(address_word, 9); - if( direction == Direction::Read ) { - direction_in(); - transfer_bits(0, 2); - } + if (direction == Direction::Read) { + direction_in(); + transfer_bits(0, 2); + } - const data_t data_in = transfer_bits(data_out, 16); + const data_t data_in = transfer_bits(data_out, 16); - if( direction == Direction::Write ) { - direction_in(); - } + if (direction == Direction::Write) { + direction_in(); + } - select(false); + select(false); - transfer_bits(0, 2); + transfer_bits(0, 2); - return data_in; + return data_in; } -} -} +} // namespace spi +} // namespace rffc507x diff --git a/firmware/application/hw/rffc507x_spi.hpp b/firmware/application/hw/rffc507x_spi.hpp index ec9f6d680..b1b168306 100644 --- a/firmware/application/hw/rffc507x_spi.hpp +++ b/firmware/application/hw/rffc507x_spi.hpp @@ -34,37 +34,37 @@ using bit_t = uint_fast8_t; using data_t = uint32_t; class SPI { -public: - enum class Direction { - Write = 0, - Read = 1, - }; + public: + enum class Direction { + Write = 0, + Read = 1, + }; - void init(); + void init(); - reg_t read(const address_t address) { - return transfer_word(Direction::Read, address, 0); - } + reg_t read(const address_t address) { + return transfer_word(Direction::Read, address, 0); + } - void write(const address_t address, const reg_t value) { - transfer_word(Direction::Write, address, value); - } + void write(const address_t address, const reg_t value) { + transfer_word(Direction::Write, address, value); + } -private: - void select(const bool active); + private: + void select(const bool active); - void direction_out(); - void direction_in(); + void direction_out(); + void direction_in(); - void write_bit(const bit_t value); - bit_t read_bit(); + void write_bit(const bit_t value); + bit_t read_bit(); - bit_t transfer_bit(const bit_t bit_out); - data_t transfer_bits(const data_t data_out, const size_t count); - data_t transfer_word(const Direction direction, const address_t address, const data_t data_out); + bit_t transfer_bit(const bit_t bit_out); + data_t transfer_bits(const data_t data_out, const size_t count); + data_t transfer_word(const Direction direction, const address_t address, const data_t data_out); }; -} /* spi */ -} /* rffc507x */ +} // namespace spi +} // namespace rffc507x -#endif/*__RFFC507X_SPI_H__*/ +#endif /*__RFFC507X_SPI_H__*/ diff --git a/firmware/application/hw/si5351.cpp b/firmware/application/hw/si5351.cpp index db8e6bc7c..236150aef 100644 --- a/firmware/application/hw/si5351.cpp +++ b/firmware/application/hw/si5351.cpp @@ -29,77 +29,71 @@ namespace si5351 { void Si5351::reset() { - wait_for_device_ready(); + wait_for_device_ready(); - write_register(Register::InterruptStatusSticky, 0x00); - write_register(Register::InterruptStatusMask, 0xf0); + write_register(Register::InterruptStatusSticky, 0x00); + write_register(Register::InterruptStatusMask, 0xf0); - disable_output_mask(0xff); - write_register(Register::OEBPinEnableControlMask, 0xff); - write_register(Register::PLLInputSource, 0x00); + disable_output_mask(0xff); + write_register(Register::OEBPinEnableControlMask, 0xff); + write_register(Register::PLLInputSource, 0x00); - set_clock_control({ - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off() - }); + set_clock_control({ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off()}); - write(std::array { Register::CLK3_0DisableState }); + write(std::array{Register::CLK3_0DisableState}); - write(std::array{ - Register::SpreadSpectrumParameters_Base - }); + write(std::array{ + Register::SpreadSpectrumParameters_Base}); - write(std::array{ - Register::VCXOParameters_Base - }); + write(std::array{ + Register::VCXOParameters_Base}); - write(std::array{ - Register::CLKInitialPhaseOffset_Base - }); + write(std::array{ + Register::CLKInitialPhaseOffset_Base}); - write_register(Register::CrystalInternalLoadCapacitance, 0b11010010); - write_register(Register::FanoutEnable, 0x00); + write_register(Register::CrystalInternalLoadCapacitance, 0b11010010); + write_register(Register::FanoutEnable, 0x00); - reset_plls(); + reset_plls(); } Si5351::regvalue_t Si5351::read_register(const uint8_t reg) { - const std::array tx { reg }; - std::array rx { 0x00 }; - _bus.transmit(_address, tx.data(), tx.size()); - _bus.receive(_address, rx.data(), rx.size()); - return rx[0]; + const std::array tx{reg}; + std::array rx{0x00}; + _bus.transmit(_address, tx.data(), tx.size()); + _bus.receive(_address, rx.data(), rx.size()); + return rx[0]; } void Si5351::set_ms_frequency( - const size_t ms_number, - const uint32_t frequency, - const uint32_t vco_frequency, - const size_t r_div -) { - /* TODO: Factor out the VCO frequency, which should be an attribute held - * by the Si5351 object. - */ - const uint32_t a = vco_frequency / frequency; - const uint32_t remainder = vco_frequency - (frequency * a); - const uint32_t denom = gcd(remainder, frequency); - const uint32_t b = remainder / denom; - const uint32_t c = frequency / denom; - - /* TODO: Switch between integer and fractional modes depending on the - * values of a and b. - */ - const MultisynthFractional ms { - .f_src = vco_frequency, - .a = a, - .b = b, - .c = c, - .r_div = r_div, - }; - const auto regs = ms.reg(ms_number); - write(regs); + const size_t ms_number, + const uint32_t frequency, + const uint32_t vco_frequency, + const size_t r_div) { + /* TODO: Factor out the VCO frequency, which should be an attribute held + * by the Si5351 object. + */ + const uint32_t a = vco_frequency / frequency; + const uint32_t remainder = vco_frequency - (frequency * a); + const uint32_t denom = gcd(remainder, frequency); + const uint32_t b = remainder / denom; + const uint32_t c = frequency / denom; + + /* TODO: Switch between integer and fractional modes depending on the + * values of a and b. + */ + const MultisynthFractional ms{ + .f_src = vco_frequency, + .a = a, + .b = b, + .c = c, + .r_div = r_div, + }; + const auto regs = ms.reg(ms_number); + write(regs); } } /* namespace si5351 */ diff --git a/firmware/application/hw/si5351.hpp b/firmware/application/hw/si5351.hpp index 3064798a2..8c3c03c55 100644 --- a/firmware/application/hw/si5351.hpp +++ b/firmware/application/hw/si5351.hpp @@ -36,158 +36,157 @@ namespace si5351 { using reg_t = uint8_t; namespace Register { - enum { - DeviceStatus = 0, - InterruptStatusSticky = 1, - InterruptStatusMask = 2, - OutputEnableControl = 3, - OEBPinEnableControlMask = 9, - PLLInputSource = 15, - CLKControl_Base = 16, - CLKControl0 = 16, - CLKControl1 = 17, - CLKControl2 = 18, - CLKControl3 = 19, - CLKControl4 = 20, - CLKControl5 = 21, - CLKControl6 = 22, - CLKControl7 = 23, - CLK3_0DisableState = 24, - CLK7_4DisableState = 25, - MultisynthNAParameters_Base = 26, - MultisynthNBParameters_Base = 34, - Multisynth0Parameters_Base = 42, - Multisynth1Parameters_Base = 50, - Multisynth2Parameters_Base = 58, - Multisynth3Parameters_Base = 66, - Multisynth4Parameters_Base = 74, - Multisynth5Parameters_Base = 82, - Multisynth6Parameters = 90, - Multisynth7Parameters = 91, - Clock6And7OutputDivider = 92, - SpreadSpectrumParameters_Base = 149, - VCXOParameters_Base = 162, - CLKInitialPhaseOffset_Base = 165, - PLLReset = 177, - CrystalInternalLoadCapacitance = 183, - FanoutEnable = 187, - }; +enum { + DeviceStatus = 0, + InterruptStatusSticky = 1, + InterruptStatusMask = 2, + OutputEnableControl = 3, + OEBPinEnableControlMask = 9, + PLLInputSource = 15, + CLKControl_Base = 16, + CLKControl0 = 16, + CLKControl1 = 17, + CLKControl2 = 18, + CLKControl3 = 19, + CLKControl4 = 20, + CLKControl5 = 21, + CLKControl6 = 22, + CLKControl7 = 23, + CLK3_0DisableState = 24, + CLK7_4DisableState = 25, + MultisynthNAParameters_Base = 26, + MultisynthNBParameters_Base = 34, + Multisynth0Parameters_Base = 42, + Multisynth1Parameters_Base = 50, + Multisynth2Parameters_Base = 58, + Multisynth3Parameters_Base = 66, + Multisynth4Parameters_Base = 74, + Multisynth5Parameters_Base = 82, + Multisynth6Parameters = 90, + Multisynth7Parameters = 91, + Clock6And7OutputDivider = 92, + SpreadSpectrumParameters_Base = 149, + VCXOParameters_Base = 162, + CLKInitialPhaseOffset_Base = 165, + PLLReset = 177, + CrystalInternalLoadCapacitance = 183, + FanoutEnable = 187, +}; } namespace DeviceStatus { - using Type = uint8_t; +using Type = uint8_t; - enum { - REVID_Mask = (0b11 << 0), +enum { + REVID_Mask = (0b11 << 0), - LOS_Mask = (1 << 4), - LOS_ValidClockAtCLKIN = (0 << 4), - LOS_LossOfSignalAtCLKIN = (1 << 4), + LOS_Mask = (1 << 4), + LOS_ValidClockAtCLKIN = (0 << 4), + LOS_LossOfSignalAtCLKIN = (1 << 4), - LOL_A_Mask = (1 << 5), - LOL_A_PLLALocked = (0 << 5), - LOL_A_PLLAUnlocked = (1 << 5), + LOL_A_Mask = (1 << 5), + LOL_A_PLLALocked = (0 << 5), + LOL_A_PLLAUnlocked = (1 << 5), - LOL_B_Mask = (1 << 6), - LOL_B_PLLBLocked = (0 << 6), - LOL_B_PLLBUnlocked = (1 << 6), + LOL_B_Mask = (1 << 6), + LOL_B_PLLBLocked = (0 << 6), + LOL_B_PLLBUnlocked = (1 << 6), - SYS_INIT_Mask = (1 << 7), - SYS_INIT_Complete = (0 << 7), - SYS_INIT_Initializing = (1 << 7), - }; -} + SYS_INIT_Mask = (1 << 7), + SYS_INIT_Complete = (0 << 7), + SYS_INIT_Initializing = (1 << 7), +}; +} // namespace DeviceStatus struct ClockControl { - enum ClockCurrentDrive { - _2mA = 0b00, - _4mA = 0b01, - _6mA = 0b10, - _8mA = 0b11, - }; - - enum ClockSource { - Xtal = 0b00, - CLKIN = 0b01, - MS_Group = 0b10, - MS_Self = 0b11, - }; - - enum ClockInvert { - Normal = 0, - Invert = 1, - }; - - enum MultiSynthSource { - PLLA = 0, - PLLB = 1, - }; - - enum MultiSynthMode { - Fractional = 0, - Integer = 1, - }; - - enum ClockPowerDown { - Power_On = 0, - Power_Off = 1, - }; - - reg_t CLK_IDRV : 2; - reg_t CLK_SRC : 2; - reg_t CLK_INV : 1; - reg_t MS_SRC : 1; - reg_t MS_INT : 1; - reg_t CLK_PDN : 1; - - constexpr ClockControl( - ClockCurrentDrive clk_idrv, - ClockSource clk_src, - ClockInvert clk_inv, - MultiSynthSource ms_src, - MultiSynthMode ms_int, - ClockPowerDown clk_pdn - ) : CLK_IDRV(clk_idrv), - CLK_SRC(clk_src), - CLK_INV(clk_inv), - MS_SRC(ms_src), - MS_INT(ms_int), - CLK_PDN(clk_pdn) - { - } - - ClockControl clk_src(const ClockSource value) const { - auto result = *this; - result.CLK_SRC = value; - return result; - } - - ClockControl ms_src(const MultiSynthSource value) const { - auto result = *this; - result.MS_SRC = value; - return result; - } - - ClockControl clk_pdn(const ClockPowerDown value) const { - auto result = *this; - result.CLK_PDN = value; - return result; - } - - constexpr operator reg_t() { - return *reinterpret_cast(this); - } - - static constexpr ClockControl power_off() { - return { - ClockCurrentDrive::_2mA, - ClockSource::Xtal, - ClockInvert::Normal, - MultiSynthSource::PLLA, - MultiSynthMode::Fractional, - ClockPowerDown::Power_Off, - }; - } + enum ClockCurrentDrive { + _2mA = 0b00, + _4mA = 0b01, + _6mA = 0b10, + _8mA = 0b11, + }; + + enum ClockSource { + Xtal = 0b00, + CLKIN = 0b01, + MS_Group = 0b10, + MS_Self = 0b11, + }; + + enum ClockInvert { + Normal = 0, + Invert = 1, + }; + + enum MultiSynthSource { + PLLA = 0, + PLLB = 1, + }; + + enum MultiSynthMode { + Fractional = 0, + Integer = 1, + }; + + enum ClockPowerDown { + Power_On = 0, + Power_Off = 1, + }; + + reg_t CLK_IDRV : 2; + reg_t CLK_SRC : 2; + reg_t CLK_INV : 1; + reg_t MS_SRC : 1; + reg_t MS_INT : 1; + reg_t CLK_PDN : 1; + + constexpr ClockControl( + ClockCurrentDrive clk_idrv, + ClockSource clk_src, + ClockInvert clk_inv, + MultiSynthSource ms_src, + MultiSynthMode ms_int, + ClockPowerDown clk_pdn) + : CLK_IDRV(clk_idrv), + CLK_SRC(clk_src), + CLK_INV(clk_inv), + MS_SRC(ms_src), + MS_INT(ms_int), + CLK_PDN(clk_pdn) { + } + + ClockControl clk_src(const ClockSource value) const { + auto result = *this; + result.CLK_SRC = value; + return result; + } + + ClockControl ms_src(const MultiSynthSource value) const { + auto result = *this; + result.MS_SRC = value; + return result; + } + + ClockControl clk_pdn(const ClockPowerDown value) const { + auto result = *this; + result.CLK_PDN = value; + return result; + } + + constexpr operator reg_t() { + return *reinterpret_cast(this); + } + + static constexpr ClockControl power_off() { + return { + ClockCurrentDrive::_2mA, + ClockSource::Xtal, + ClockInvert::Normal, + MultiSynthSource::PLLA, + MultiSynthMode::Fractional, + ClockPowerDown::Power_Off, + }; + } }; static_assert(sizeof(ClockControl) == 1, "ClockControl size is not eight bits"); @@ -195,302 +194,294 @@ static_assert(sizeof(ClockControl) == 1, "ClockControl size is not eight bits"); using ClockControls = std::array; namespace CrystalInternalLoadCapacitance { - using Type = uint8_t; - - enum { - XTAL_CL_Mask = (0b11 << 6), - XTAL_CL_6pF = (0b01 << 6), - XTAL_CL_8pF = (0b10 << 6), - XTAL_CL_10pF = (0b11 << 6), - }; -} +using Type = uint8_t; + +enum { + XTAL_CL_Mask = (0b11 << 6), + XTAL_CL_6pF = (0b01 << 6), + XTAL_CL_8pF = (0b10 << 6), + XTAL_CL_10pF = (0b11 << 6), +}; +} // namespace CrystalInternalLoadCapacitance namespace PLLInputSource { - using Type = uint8_t; - - enum { - PLLA_Source_Mask = (1 << 2), - PLLA_Source_XTAL = (0 << 2), - PLLA_Source_CLKIN = (1 << 2), - - PLLB_Source_Mask = (1 << 3), - PLLB_Source_XTAL = (0 << 3), - PLLB_Source_CLKIN = (1 << 3), - - CLKIN_Div_Mask = (0b11 << 6), - CLKIN_Div1 = (0b00 << 6), - CLKIN_Div2 = (0b01 << 6), - CLKIN_Div4 = (0b10 << 6), - CLKIN_Div8 = (0b11 << 6), - }; -} +using Type = uint8_t; + +enum { + PLLA_Source_Mask = (1 << 2), + PLLA_Source_XTAL = (0 << 2), + PLLA_Source_CLKIN = (1 << 2), + + PLLB_Source_Mask = (1 << 3), + PLLB_Source_XTAL = (0 << 3), + PLLB_Source_CLKIN = (1 << 3), + + CLKIN_Div_Mask = (0b11 << 6), + CLKIN_Div1 = (0b00 << 6), + CLKIN_Div2 = (0b01 << 6), + CLKIN_Div4 = (0b10 << 6), + CLKIN_Div8 = (0b11 << 6), +}; +} // namespace PLLInputSource struct Inputs { - const uint32_t f_xtal; - const uint32_t f_clkin; - const uint32_t clkin_div; + const uint32_t f_xtal; + const uint32_t f_clkin; + const uint32_t clkin_div; - constexpr uint32_t f_clkin_out() const { - return f_clkin / clkin_div; - } + constexpr uint32_t f_clkin_out() const { + return f_clkin / clkin_div; + } }; using PLLReg = std::array; struct PLL { - const uint32_t f_in; - const uint32_t a; - const uint32_t b; - const uint32_t c; - - constexpr uint32_t f_vco() const { - return f_in * (a + (float)b / (float)c); - } - - constexpr uint32_t p1() const { - return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; - } - - constexpr uint32_t p2() const { - return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); - } - - constexpr uint32_t p3() const { - return c; - } - - constexpr PLLReg reg(const uint8_t pll_n) const { - return { - uint8_t(26 + (pll_n * 8)), - uint8_t((p3() >> 8) & 0xff), - uint8_t((p3() >> 0) & 0xff), - uint8_t((p1() >> 16) & 0x03), - uint8_t((p1() >> 8) & 0xff), - uint8_t((p1() >> 0) & 0xff), - uint8_t( - (((p3() >> 16) & 0x0f) << 4) - | ((p2() >> 16) & 0x0f) - ), - uint8_t((p2() >> 8) & 0xff), - uint8_t((p2() >> 0) & 0xff), - }; - } + const uint32_t f_in; + const uint32_t a; + const uint32_t b; + const uint32_t c; + + constexpr uint32_t f_vco() const { + return f_in * (a + (float)b / (float)c); + } + + constexpr uint32_t p1() const { + return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; + } + + constexpr uint32_t p2() const { + return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); + } + + constexpr uint32_t p3() const { + return c; + } + + constexpr PLLReg reg(const uint8_t pll_n) const { + return { + uint8_t(26 + (pll_n * 8)), + uint8_t((p3() >> 8) & 0xff), + uint8_t((p3() >> 0) & 0xff), + uint8_t((p1() >> 16) & 0x03), + uint8_t((p1() >> 8) & 0xff), + uint8_t((p1() >> 0) & 0xff), + uint8_t( + (((p3() >> 16) & 0x0f) << 4) | ((p2() >> 16) & 0x0f)), + uint8_t((p2() >> 8) & 0xff), + uint8_t((p2() >> 0) & 0xff), + }; + } }; using MultisynthFractionalReg = std::array; struct MultisynthFractional { - const uint32_t f_src; - const uint32_t a; - const uint32_t b; - const uint32_t c; - const uint32_t r_div; - - constexpr uint32_t p1() const { - return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; - } - - constexpr uint32_t p2() const { - return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); - } - - constexpr uint32_t p3() const { - return c; - } - - constexpr uint32_t f_out() const { - return f_src / (a + (float)b / (float)c) / (1 << r_div); - } - - constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) const { - return { - uint8_t(42 + (multisynth_n * 8)), - uint8_t((p3() >> 8) & 0xFF), - uint8_t((p3() >> 0) & 0xFF), - uint8_t((r_div << 4) | (0 << 2) | ((p1() >> 16) & 0x3)), - uint8_t((p1() >> 8) & 0xFF), - uint8_t((p1() >> 0) & 0xFF), - uint8_t((((p3() >> 16) & 0xF) << 4) | (((p2() >> 16) & 0xF) << 0)), - uint8_t((p2() >> 8) & 0xFF), - uint8_t((p2() >> 0) & 0xFF) - }; - } + const uint32_t f_src; + const uint32_t a; + const uint32_t b; + const uint32_t c; + const uint32_t r_div; + + constexpr uint32_t p1() const { + return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; + } + + constexpr uint32_t p2() const { + return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); + } + + constexpr uint32_t p3() const { + return c; + } + + constexpr uint32_t f_out() const { + return f_src / (a + (float)b / (float)c) / (1 << r_div); + } + + constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) const { + return { + uint8_t(42 + (multisynth_n * 8)), + uint8_t((p3() >> 8) & 0xFF), + uint8_t((p3() >> 0) & 0xFF), + uint8_t((r_div << 4) | (0 << 2) | ((p1() >> 16) & 0x3)), + uint8_t((p1() >> 8) & 0xFF), + uint8_t((p1() >> 0) & 0xFF), + uint8_t((((p3() >> 16) & 0xF) << 4) | (((p2() >> 16) & 0xF) << 0)), + uint8_t((p2() >> 8) & 0xFF), + uint8_t((p2() >> 0) & 0xFF)}; + } }; struct MultisynthInteger { - const uint32_t f_src; - const uint32_t a; - const uint32_t r_div; + const uint32_t f_src; + const uint32_t a; + const uint32_t r_div; - constexpr uint8_t p1() const { - return a; - } + constexpr uint8_t p1() const { + return a; + } - constexpr uint32_t f_out() const { - return f_src / a / (1 << r_div); - } + constexpr uint32_t f_out() const { + return f_src / a / (1 << r_div); + } }; using Multisynth6And7Reg = std::array; constexpr Multisynth6And7Reg ms6_7_reg( - const MultisynthInteger& ms6, - const MultisynthInteger& ms7 -) { - return { - Register::Multisynth6Parameters, - uint8_t(ms6.p1() & 0xff), - uint8_t(ms7.p1() & 0xff), - uint8_t(((ms7.r_div & 7) << 4) | ((ms6.r_div & 7) << 0)), - }; + const MultisynthInteger& ms6, + const MultisynthInteger& ms7) { + return { + Register::Multisynth6Parameters, + uint8_t(ms6.p1() & 0xff), + uint8_t(ms7.p1() & 0xff), + uint8_t(((ms7.r_div & 7) << 4) | ((ms6.r_div & 7) << 0)), + }; } class Si5351 { -public: - using regvalue_t = uint8_t; - - constexpr Si5351(I2C& bus, I2C::address_t address) : - _clock_control({ - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off(), - ClockControl::power_off(), ClockControl::power_off() - }), - _bus(bus), - _address(address), - _output_enable(0x00) - { - } - - void reset(); - - uint8_t device_status() { - return read_register(Register::DeviceStatus); - } - - void wait_for_device_ready() { - while(device_status() & 0x80); - } - - bool plla_loss_of_signal() { - return (device_status() >> 5) & 1; - } - - bool clkin_loss_of_signal() { - return (device_status() >> 4) & 1; - } - - void enable_fanout() { - write_register(Register::FanoutEnable, 0b11010000); - } - - void reset_plls() { - // Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619. - write_register(Register::PLLReset, 0xac); - } - - regvalue_t read_register(const uint8_t reg); - - template - void write(const std::array& values) { - _bus.transmit(_address, values.data(), values.size()); - } - - void write_register(const uint8_t reg, const regvalue_t value) { - write(std::array{ - reg, value - }); - } - - void write(const size_t ms_number, const MultisynthFractional& config) { - write(config.reg(ms_number)); - } - - void set_ms_frequency( - const size_t ms_number, - const uint32_t frequency, - const uint32_t vco_frequency, - const size_t r_div - ); - - void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) { - write_register(Register::CrystalInternalLoadCapacitance, xtal_cl); - } - - void set_pll_input_sources(const PLLInputSource::Type value) { - write_register(Register::PLLInputSource, value); - } - - void enable_output_mask(const uint8_t mask) { - _output_enable |= mask; - update_output_enable_control(); - } - - void enable_output(const size_t n) { - enable_output_mask(1 << n); - } - - void disable_output_mask(const uint8_t mask) { - _output_enable &= ~mask; - update_output_enable_control(); - } - - void disable_output(const size_t n) { - disable_output_mask(1 << n); - } - - void set_clock_control(const ClockControls& clock_control) { - _clock_control = clock_control; - update_all_clock_control(); - } - - void set_clock_control(const size_t n, const ClockControl clock_control) { - _clock_control[n] = clock_control; - write_register(Register::CLKControl_Base + n, _clock_control[n]); - } - - void enable_clock(const size_t n) { - _clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_On; - write_register(Register::CLKControl_Base + n, _clock_control[n]); - } - - void disable_clock(const size_t n) { - _clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_Off; - write_register(Register::CLKControl_Base + n, _clock_control[n]); - } - - template - void write_registers(const uint8_t reg, const std::array& values) { - std::array data; - data[0] = reg; - std::copy(values.cbegin(), values.cend(), data.begin() + 1); - write(data); - } - -private: - ClockControls _clock_control; - I2C& _bus; - const I2C::address_t _address; - uint8_t _output_enable; - - void update_output_enable_control() { - write_register(Register::OutputEnableControl, ~_output_enable); - } - - void update_all_clock_control() { - write_registers(Register::CLKControl_Base, std::array { { - _clock_control[0], - _clock_control[1], - _clock_control[2], - _clock_control[3], - _clock_control[4], - _clock_control[5], - _clock_control[6], - _clock_control[7], - } }); - } + public: + using regvalue_t = uint8_t; + + constexpr Si5351(I2C& bus, I2C::address_t address) + : _clock_control({ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off(), + ClockControl::power_off(), ClockControl::power_off()}), + _bus(bus), + _address(address), + _output_enable(0x00) { + } + + void reset(); + + uint8_t device_status() { + return read_register(Register::DeviceStatus); + } + + void wait_for_device_ready() { + while (device_status() & 0x80) + ; + } + + bool plla_loss_of_signal() { + return (device_status() >> 5) & 1; + } + + bool clkin_loss_of_signal() { + return (device_status() >> 4) & 1; + } + + void enable_fanout() { + write_register(Register::FanoutEnable, 0b11010000); + } + + void reset_plls() { + // Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619. + write_register(Register::PLLReset, 0xac); + } + + regvalue_t read_register(const uint8_t reg); + + template + void write(const std::array& values) { + _bus.transmit(_address, values.data(), values.size()); + } + + void write_register(const uint8_t reg, const regvalue_t value) { + write(std::array{ + reg, value}); + } + + void write(const size_t ms_number, const MultisynthFractional& config) { + write(config.reg(ms_number)); + } + + void set_ms_frequency( + const size_t ms_number, + const uint32_t frequency, + const uint32_t vco_frequency, + const size_t r_div); + + void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) { + write_register(Register::CrystalInternalLoadCapacitance, xtal_cl); + } + + void set_pll_input_sources(const PLLInputSource::Type value) { + write_register(Register::PLLInputSource, value); + } + + void enable_output_mask(const uint8_t mask) { + _output_enable |= mask; + update_output_enable_control(); + } + + void enable_output(const size_t n) { + enable_output_mask(1 << n); + } + + void disable_output_mask(const uint8_t mask) { + _output_enable &= ~mask; + update_output_enable_control(); + } + + void disable_output(const size_t n) { + disable_output_mask(1 << n); + } + + void set_clock_control(const ClockControls& clock_control) { + _clock_control = clock_control; + update_all_clock_control(); + } + + void set_clock_control(const size_t n, const ClockControl clock_control) { + _clock_control[n] = clock_control; + write_register(Register::CLKControl_Base + n, _clock_control[n]); + } + + void enable_clock(const size_t n) { + _clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_On; + write_register(Register::CLKControl_Base + n, _clock_control[n]); + } + + void disable_clock(const size_t n) { + _clock_control[n].CLK_PDN = ClockControl::ClockPowerDown::Power_Off; + write_register(Register::CLKControl_Base + n, _clock_control[n]); + } + + template + void write_registers(const uint8_t reg, const std::array& values) { + std::array data; + data[0] = reg; + std::copy(values.cbegin(), values.cend(), data.begin() + 1); + write(data); + } + + private: + ClockControls _clock_control; + I2C& _bus; + const I2C::address_t _address; + uint8_t _output_enable; + + void update_output_enable_control() { + write_register(Register::OutputEnableControl, ~_output_enable); + } + + void update_all_clock_control() { + write_registers(Register::CLKControl_Base, std::array{{ + _clock_control[0], + _clock_control[1], + _clock_control[2], + _clock_control[3], + _clock_control[4], + _clock_control[5], + _clock_control[6], + _clock_control[7], + }}); + } }; -} +} // namespace si5351 -#endif/*__SI5351_H__*/ +#endif /*__SI5351_H__*/ diff --git a/firmware/application/hw/spi_arbiter.hpp b/firmware/application/hw/spi_arbiter.hpp index 6d48bfbac..dd0c2a394 100644 --- a/firmware/application/hw/spi_arbiter.hpp +++ b/firmware/application/hw/spi_arbiter.hpp @@ -30,48 +30,46 @@ namespace spi { namespace arbiter { class Arbiter { -public: - constexpr Arbiter( - SPI& bus - ) : _bus(bus), - _config(nullptr) - { - } + public: + constexpr Arbiter( + SPI& bus) + : _bus(bus), + _config(nullptr) { + } - void transfer(const SPIConfig* const config, void* const data, const size_t count) { - if( config != _config ) { - _bus.stop(); - _bus.start(*config); - _config = config; - } - _bus.transfer(data, count); - } + void transfer(const SPIConfig* const config, void* const data, const size_t count) { + if (config != _config) { + _bus.stop(); + _bus.start(*config); + _config = config; + } + _bus.transfer(data, count); + } -private: - SPI& _bus; - const SPIConfig* _config; + private: + SPI& _bus; + const SPIConfig* _config; }; class Target { -public: - constexpr Target( - Arbiter& arbiter, - const SPIConfig& config - ) : _arbiter(arbiter), - _config(config) - { - } + public: + constexpr Target( + Arbiter& arbiter, + const SPIConfig& config) + : _arbiter(arbiter), + _config(config) { + } - void transfer(void* const data, const size_t count) { - _arbiter.transfer(&_config, data, count); - } + void transfer(void* const data, const size_t count) { + _arbiter.transfer(&_config, data, count); + } -private: - Arbiter& _arbiter; - const SPIConfig _config; + private: + Arbiter& _arbiter; + const SPIConfig _config; }; -} /* arbiter */ -} /* spi */ +} // namespace arbiter +} // namespace spi -#endif/*__SPI_ARBITER_H__*/ +#endif /*__SPI_ARBITER_H__*/ diff --git a/firmware/application/hw/spi_pp.hpp b/firmware/application/hw/spi_pp.hpp index 9fc3ad547..9aec810f5 100644 --- a/firmware/application/hw/spi_pp.hpp +++ b/firmware/application/hw/spi_pp.hpp @@ -28,29 +28,29 @@ #include "hal.h" class SPI { -public: - constexpr SPI(SPIDriver* const driver) : - _driver(driver) { - } - - void start(const SPIConfig& config) { - spiStart(_driver, &config); - } - - void stop() { - spiStop(_driver); - } - - void transfer(void* const data, const size_t count) { - spiAcquireBus(_driver); - spiSelect(_driver); - spiExchange(_driver, count, data, data); - spiUnselect(_driver); - spiReleaseBus(_driver); - } - -private: - SPIDriver* const _driver; + public: + constexpr SPI(SPIDriver* const driver) + : _driver(driver) { + } + + void start(const SPIConfig& config) { + spiStart(_driver, &config); + } + + void stop() { + spiStop(_driver); + } + + void transfer(void* const data, const size_t count) { + spiAcquireBus(_driver); + spiSelect(_driver); + spiExchange(_driver, count, data, data); + spiUnselect(_driver); + spiReleaseBus(_driver); + } + + private: + SPIDriver* const _driver; }; -#endif/*__SPI_PP_H__*/ +#endif /*__SPI_PP_H__*/ diff --git a/firmware/application/hw/touch_adc.cpp b/firmware/application/hw/touch_adc.cpp index 91748ff28..7254f156c 100644 --- a/firmware/application/hw/touch_adc.cpp +++ b/firmware/application/hw/touch_adc.cpp @@ -39,47 +39,43 @@ namespace touch { namespace adc { constexpr uint8_t adc0_sel = - (1 << portapack::adc0_touch_xp_input) - | (1 << portapack::adc0_touch_xn_input) - | (1 << portapack::adc0_touch_yp_input) - | (1 << portapack::adc0_touch_yn_input) - ; + (1 << portapack::adc0_touch_xp_input) | (1 << portapack::adc0_touch_xn_input) | (1 << portapack::adc0_touch_yp_input) | (1 << portapack::adc0_touch_yn_input); const auto adc0_interrupt_mask = flp2(adc0_sel); -constexpr lpc43xx::adc::CR adc0_cr { - .sel = adc0_sel, - .clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */ - .resolution = 9, /* Ten clocks */ - .edge = 0, +constexpr lpc43xx::adc::CR adc0_cr{ + .sel = adc0_sel, + .clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */ + .resolution = 9, /* Ten clocks */ + .edge = 0, }; -constexpr lpc43xx::adc::Config adc0_config { - .cr = adc0_cr, +constexpr lpc43xx::adc::Config adc0_config{ + .cr = adc0_cr, }; void init() { - adc0::clock_enable(); - adc0::interrupts_disable(); - adc0::power_up(adc0_config); - adc0::interrupts_enable(adc0_interrupt_mask); + adc0::clock_enable(); + adc0::interrupts_disable(); + adc0::power_up(adc0_config); + adc0::interrupts_enable(adc0_interrupt_mask); } void start() { - adc0::start_burst(); + adc0::start_burst(); } // static constexpr bool monitor_overruns_and_not_dones = false; Samples get() { - const auto xp_reg = LPC_ADC0->DR[portapack::adc0_touch_xp_input]; - const auto xn_reg = LPC_ADC0->DR[portapack::adc0_touch_xn_input]; - const auto yp_reg = LPC_ADC0->DR[portapack::adc0_touch_yp_input]; - const auto yn_reg = LPC_ADC0->DR[portapack::adc0_touch_yn_input]; - return { - (xp_reg >> 6) & 0x3ff, - (xn_reg >> 6) & 0x3ff, - (yp_reg >> 6) & 0x3ff, - (yn_reg >> 6) & 0x3ff, - }; + const auto xp_reg = LPC_ADC0->DR[portapack::adc0_touch_xp_input]; + const auto xn_reg = LPC_ADC0->DR[portapack::adc0_touch_xn_input]; + const auto yp_reg = LPC_ADC0->DR[portapack::adc0_touch_yp_input]; + const auto yn_reg = LPC_ADC0->DR[portapack::adc0_touch_yn_input]; + return { + (xp_reg >> 6) & 0x3ff, + (xn_reg >> 6) & 0x3ff, + (yp_reg >> 6) & 0x3ff, + (yn_reg >> 6) & 0x3ff, + }; } } /* namespace adc */ diff --git a/firmware/application/hw/touch_adc.hpp b/firmware/application/hw/touch_adc.hpp index a7b45ff80..2a14317b3 100644 --- a/firmware/application/hw/touch_adc.hpp +++ b/firmware/application/hw/touch_adc.hpp @@ -35,4 +35,4 @@ Samples get(); } /* namespace adc */ } /* namespace touch */ -#endif/*__TOUCH_ADC_H__*/ +#endif /*__TOUCH_ADC_H__*/ diff --git a/firmware/application/io.hpp b/firmware/application/io.hpp index 5c29da9d5..ebbf9c45c 100644 --- a/firmware/application/io.hpp +++ b/firmware/application/io.hpp @@ -26,15 +26,15 @@ namespace stream { class Reader { -public: - virtual File::Result read(void* const buffer, const File::Size bytes) = 0; - virtual ~Reader() = default; + public: + virtual File::Result read(void* const buffer, const File::Size bytes) = 0; + virtual ~Reader() = default; }; class Writer { -public: - virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; - virtual ~Writer() = default; + public: + virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; + virtual ~Writer() = default; }; } /* namespace stream */ diff --git a/firmware/application/io_file.cpp b/firmware/application/io_file.cpp index 665833083..f30e5db3d 100644 --- a/firmware/application/io_file.cpp +++ b/firmware/application/io_file.cpp @@ -22,17 +22,17 @@ #include "io_file.hpp" File::Result FileReader::read(void* const buffer, const File::Size bytes) { - auto read_result = file.read(buffer, bytes) ; - if( read_result.is_ok() ) { - bytes_read += read_result.value(); - } - return read_result; + auto read_result = file.read(buffer, bytes); + if (read_result.is_ok()) { + bytes_read += read_result.value(); + } + return read_result; } File::Result FileWriter::write(const void* const buffer, const File::Size bytes) { - auto write_result = file.write(buffer, bytes) ; - if( write_result.is_ok() ) { - bytes_written += write_result.value(); - } - return write_result; + auto write_result = file.write(buffer, bytes); + if (write_result.is_ok()) { + bytes_written += write_result.value(); + } + return write_result; } diff --git a/firmware/application/io_file.hpp b/firmware/application/io_file.hpp index 25cf0f761..a65c1cd86 100644 --- a/firmware/application/io_file.hpp +++ b/firmware/application/io_file.hpp @@ -29,43 +29,43 @@ #include class FileReader : public stream::Reader { -public: - FileReader() = default; + public: + FileReader() = default; - FileReader(const FileReader&) = delete; - FileReader& operator=(const FileReader&) = delete; - FileReader(FileReader&& file) = delete; - FileReader& operator=(FileReader&&) = delete; + FileReader(const FileReader&) = delete; + FileReader& operator=(const FileReader&) = delete; + FileReader(FileReader&& file) = delete; + FileReader& operator=(FileReader&&) = delete; - Optional open(const std::filesystem::path& filename) { - return file.open(filename); - } - - File::Result read(void* const buffer, const File::Size bytes) override; - -protected: - File file { }; - uint64_t bytes_read { 0 }; + Optional open(const std::filesystem::path& filename) { + return file.open(filename); + } + + File::Result read(void* const buffer, const File::Size bytes) override; + + protected: + File file{}; + uint64_t bytes_read{0}; }; class FileWriter : public stream::Writer { -public: - FileWriter() = default; + public: + FileWriter() = default; + + FileWriter(const FileWriter&) = delete; + FileWriter& operator=(const FileWriter&) = delete; + FileWriter(FileWriter&& file) = delete; + FileWriter& operator=(FileWriter&&) = delete; - FileWriter(const FileWriter&) = delete; - FileWriter& operator=(const FileWriter&) = delete; - FileWriter(FileWriter&& file) = delete; - FileWriter& operator=(FileWriter&&) = delete; + Optional create(const std::filesystem::path& filename) { + return file.create(filename); + } - Optional create(const std::filesystem::path& filename) { - return file.create(filename); - } + File::Result write(const void* const buffer, const File::Size bytes) override; - File::Result write(const void* const buffer, const File::Size bytes) override; - -protected: - File file { }; - uint64_t bytes_written { 0 }; + protected: + File file{}; + uint64_t bytes_written{0}; }; using RawFileWriter = FileWriter; diff --git a/firmware/application/io_wave.cpp b/firmware/application/io_wave.cpp index b39975887..337820e01 100644 --- a/firmware/application/io_wave.cpp +++ b/firmware/application/io_wave.cpp @@ -23,160 +23,159 @@ #include "io_wave.hpp" bool WAVFileReader::open(const std::filesystem::path& path) { - size_t i = 0; - char ch; - const uint8_t tag_INAM[4] = { 'I', 'N', 'A', 'M' }; - char title_buffer[32]; - uint32_t riff_size, data_end, title_size; - size_t search_limit = 0; - - // Already open ? - if (path.string() == last_path.string()) { - rewind(); - return true; - } - - auto error = file.open(path); - - if (!error.is_valid()) { - file.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE) - - riff_size = header.cksize + 8; - data_start = header.fmt.cksize + 28; - data_size_ = header.data.cksize; - data_end = data_start + data_size_ + 1; - - // Look for INAM (title) tag - if (data_end < riff_size) { - file.seek(data_end); - while(file.read((void*)&ch, 1).is_ok()) { - if (ch == tag_INAM[i++]) { - if (i == 4) { - // Tag found, copy title - file.read((void*)&title_size, sizeof(uint32_t)); - if (title_size > 32) title_size = 32; - file.read((void*)&title_buffer, title_size); - title_string = title_buffer; - break; - } - } else { - if (ch == tag_INAM[0]) - i = 1; - else - i = 0; - } - if (search_limit == 256) - break; - else - search_limit++; - } - } - - sample_rate_ = header.fmt.nSamplesPerSec; - bytes_per_sample = header.fmt.wBitsPerSample / 8; - - rewind(); - - last_path = path; - - return true; - } else { - return false; - } + size_t i = 0; + char ch; + const uint8_t tag_INAM[4] = {'I', 'N', 'A', 'M'}; + char title_buffer[32]; + uint32_t riff_size, data_end, title_size; + size_t search_limit = 0; + + // Already open ? + if (path.string() == last_path.string()) { + rewind(); + return true; + } + + auto error = file.open(path); + + if (!error.is_valid()) { + file.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE) + + riff_size = header.cksize + 8; + data_start = header.fmt.cksize + 28; + data_size_ = header.data.cksize; + data_end = data_start + data_size_ + 1; + + // Look for INAM (title) tag + if (data_end < riff_size) { + file.seek(data_end); + while (file.read((void*)&ch, 1).is_ok()) { + if (ch == tag_INAM[i++]) { + if (i == 4) { + // Tag found, copy title + file.read((void*)&title_size, sizeof(uint32_t)); + if (title_size > 32) title_size = 32; + file.read((void*)&title_buffer, title_size); + title_string = title_buffer; + break; + } + } else { + if (ch == tag_INAM[0]) + i = 1; + else + i = 0; + } + if (search_limit == 256) + break; + else + search_limit++; + } + } + + sample_rate_ = header.fmt.nSamplesPerSec; + bytes_per_sample = header.fmt.wBitsPerSample / 8; + + rewind(); + + last_path = path; + + return true; + } else { + return false; + } } void WAVFileReader::rewind() { - file.seek(data_start); + file.seek(data_start); } std::string WAVFileReader::title() { - return title_string; + return title_string; } uint32_t WAVFileReader::ms_duration() { - return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample; + return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample; } void WAVFileReader::data_seek(const uint64_t Offset) { - file.seek(data_start + (Offset * bytes_per_sample)); + file.seek(data_start + (Offset * bytes_per_sample)); } - + /*int WAVFileReader::seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples) { - const auto result = file.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample); + const auto result = file.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample); + + if (result.is_error()) + return 0; - if (result.is_error()) - return 0; - - return 1; + return 1; }*/ uint16_t WAVFileReader::channels() { - return header.fmt.nChannels; + return header.fmt.nChannels; } uint32_t WAVFileReader::sample_rate() { - return sample_rate_; + return sample_rate_; } uint32_t WAVFileReader::data_size() { - return data_size_; + return data_size_; } uint32_t WAVFileReader::sample_count() { - return data_size_ / bytes_per_sample; + return data_size_ / bytes_per_sample; } uint16_t WAVFileReader::bits_per_sample() { - return header.fmt.wBitsPerSample; + return header.fmt.wBitsPerSample; } Optional WAVFileWriter::create( - const std::filesystem::path& filename, - size_t sampling_rate_set, - const std::string& title_set -) { - sampling_rate = sampling_rate_set; - title = title_set; - const auto create_error = FileWriter::create(filename); - if( create_error.is_valid() ) { - return create_error; - } else { - return update_header(); - } + const std::filesystem::path& filename, + size_t sampling_rate_set, + const std::string& title_set) { + sampling_rate = sampling_rate_set; + title = title_set; + const auto create_error = FileWriter::create(filename); + if (create_error.is_valid()) { + return create_error; + } else { + return update_header(); + } } Optional WAVFileWriter::update_header() { - header_t header { sampling_rate, (uint32_t)bytes_written - sizeof(header_t), info_chunk_size }; - - const auto seek_0_result = file.seek(0); - if( seek_0_result.is_error() ) { - return seek_0_result.error(); - } - - const auto old_position = seek_0_result.value(); - - const auto write_result = file.write(&header, sizeof(header)); - if( write_result.is_error() ) { - return write_result.error(); - } - - const auto seek_old_result = file.seek(old_position); - if( seek_old_result.is_error() ) { - return seek_old_result.error(); - } - - return { }; + header_t header{sampling_rate, (uint32_t)bytes_written - sizeof(header_t), info_chunk_size}; + + const auto seek_0_result = file.seek(0); + if (seek_0_result.is_error()) { + return seek_0_result.error(); + } + + const auto old_position = seek_0_result.value(); + + const auto write_result = file.write(&header, sizeof(header)); + if (write_result.is_error()) { + return write_result.error(); + } + + const auto seek_old_result = file.seek(old_position); + if (seek_old_result.is_error()) { + return seek_old_result.error(); + } + + return {}; } Optional WAVFileWriter::write_tags() { - tags_t tags { title }; - - const auto write_result = file.write(&tags, sizeof(tags)); - if( write_result.is_error() ) { - return write_result.error(); - } - - info_chunk_size = sizeof(tags); - - return { }; + tags_t tags{title}; + + const auto write_result = file.write(&tags, sizeof(tags)); + if (write_result.is_error()) { + return write_result.error(); + } + + info_chunk_size = sizeof(tags); + + return {}; } diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index 47d2b1d66..0293e5f98 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -30,159 +30,154 @@ #include struct fmt_pcm_t { - constexpr fmt_pcm_t( - const uint32_t sampling_rate - ) : nSamplesPerSec { sampling_rate }, - nAvgBytesPerSec { nSamplesPerSec * 2 } // nBlockAlign = 2 - { - } - -private: - uint8_t ckID[4] { 'f', 'm', 't', ' ' }; - uint32_t cksize { 16 }; - uint16_t wFormatTag { 0x0001 }; - uint16_t nChannels { 1 }; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign { 2 }; - uint16_t wBitsPerSample { 16 }; + constexpr fmt_pcm_t( + const uint32_t sampling_rate) + : nSamplesPerSec{sampling_rate}, + nAvgBytesPerSec{nSamplesPerSec * 2} // nBlockAlign = 2 + { + } + + private: + uint8_t ckID[4]{'f', 'm', 't', ' '}; + uint32_t cksize{16}; + uint16_t wFormatTag{0x0001}; + uint16_t nChannels{1}; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign{2}; + uint16_t wBitsPerSample{16}; }; struct data_t { - constexpr data_t( - const uint32_t size - ) : cksize { size } - { - } - -private: - uint8_t ckID[4] { 'd', 'a', 't', 'a' }; - uint32_t cksize { 0 }; + constexpr data_t( + const uint32_t size) + : cksize{size} { + } + + private: + uint8_t ckID[4]{'d', 'a', 't', 'a'}; + uint32_t cksize{0}; }; struct header_t { - constexpr header_t( - const uint32_t sampling_rate, - const uint32_t data_chunk_size, - const uint32_t info_chunk_size - ) : cksize { sizeof(header_t) + data_chunk_size + info_chunk_size - 8 }, - fmt { sampling_rate }, - data { data_chunk_size } - { - } - -private: - uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; - uint32_t cksize { 0 }; - uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; - fmt_pcm_t fmt; - data_t data; + constexpr header_t( + const uint32_t sampling_rate, + const uint32_t data_chunk_size, + const uint32_t info_chunk_size) + : cksize{sizeof(header_t) + data_chunk_size + info_chunk_size - 8}, + fmt{sampling_rate}, + data{data_chunk_size} { + } + + private: + uint8_t riff_id[4]{'R', 'I', 'F', 'F'}; + uint32_t cksize{0}; + uint8_t wave_id[4]{'W', 'A', 'V', 'E'}; + fmt_pcm_t fmt; + data_t data; }; struct tags_t { - constexpr tags_t( - const std::string& title_str - ) - { - strcpy(title, title_str.c_str()); - cksize = sizeof(tags_t) - 8; - } - -private: - uint8_t list_id[4] { 'L', 'I', 'S', 'T' }; - uint32_t cksize { 0 }; - uint8_t info_id[4] { 'I', 'N', 'F', 'O' }; - uint8_t iart_id[4] { 'I', 'A', 'R', 'T' }; - uint32_t sckiart_size { 12 }; - char artist[12] { "PortaPack\0\0" }; - uint8_t inam_id[4] { 'I', 'N', 'A', 'M' }; - uint32_t sckinam_size { 64 }; - char title[64] { 0 }; + constexpr tags_t( + const std::string& title_str) { + strcpy(title, title_str.c_str()); + cksize = sizeof(tags_t) - 8; + } + + private: + uint8_t list_id[4]{'L', 'I', 'S', 'T'}; + uint32_t cksize{0}; + uint8_t info_id[4]{'I', 'N', 'F', 'O'}; + uint8_t iart_id[4]{'I', 'A', 'R', 'T'}; + uint32_t sckiart_size{12}; + char artist[12]{"PortaPack\0\0"}; + uint8_t inam_id[4]{'I', 'N', 'A', 'M'}; + uint32_t sckinam_size{64}; + char title[64]{0}; }; class WAVFileReader : public FileReader { -public: - WAVFileReader() = default; - - WAVFileReader(const WAVFileReader&) = delete; - WAVFileReader& operator=(const WAVFileReader&) = delete; - WAVFileReader(WAVFileReader&&) = delete; - WAVFileReader& operator=(WAVFileReader&&) = delete; - - virtual ~WAVFileReader() = default; - - bool open(const std::filesystem::path& path); - void data_seek(const uint64_t Offset); - void rewind(); - uint32_t ms_duration(); - //int seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples); - uint16_t channels(); - uint32_t sample_rate(); - uint32_t data_size(); - uint32_t sample_count(); - uint16_t bits_per_sample(); - std::string title(); - -private: - struct fmt_pcm_t { - uint8_t ckID[4]; // fmt - uint32_t cksize; - uint16_t wFormatTag; - uint16_t nChannels; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign; - uint16_t wBitsPerSample; - }; - - struct data_t { - uint8_t ckID[4]; // data - uint32_t cksize; - }; - - struct header_t { - uint8_t riff_id[4]; // RIFF - uint32_t cksize; - uint8_t wave_id[4]; // WAVE - fmt_pcm_t fmt; - data_t data; - }; - - header_t header { }; - - uint32_t data_start { }; - uint32_t bytes_per_sample { }; - uint32_t data_size_ { 0 }; - uint32_t sample_rate_ { }; - std::string title_string { }; - std::filesystem::path last_path { }; + public: + WAVFileReader() = default; + + WAVFileReader(const WAVFileReader&) = delete; + WAVFileReader& operator=(const WAVFileReader&) = delete; + WAVFileReader(WAVFileReader&&) = delete; + WAVFileReader& operator=(WAVFileReader&&) = delete; + + virtual ~WAVFileReader() = default; + + bool open(const std::filesystem::path& path); + void data_seek(const uint64_t Offset); + void rewind(); + uint32_t ms_duration(); + // int seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples); + uint16_t channels(); + uint32_t sample_rate(); + uint32_t data_size(); + uint32_t sample_count(); + uint16_t bits_per_sample(); + std::string title(); + + private: + struct fmt_pcm_t { + uint8_t ckID[4]; // fmt + uint32_t cksize; + uint16_t wFormatTag; + uint16_t nChannels; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign; + uint16_t wBitsPerSample; + }; + + struct data_t { + uint8_t ckID[4]; // data + uint32_t cksize; + }; + + struct header_t { + uint8_t riff_id[4]; // RIFF + uint32_t cksize; + uint8_t wave_id[4]; // WAVE + fmt_pcm_t fmt; + data_t data; + }; + + header_t header{}; + + uint32_t data_start{}; + uint32_t bytes_per_sample{}; + uint32_t data_size_{0}; + uint32_t sample_rate_{}; + std::string title_string{}; + std::filesystem::path last_path{}; }; class WAVFileWriter : public FileWriter { -public: - WAVFileWriter() = default; - - WAVFileWriter(const WAVFileWriter&) = delete; - WAVFileWriter& operator=(const WAVFileWriter&) = delete; - WAVFileWriter(WAVFileWriter&&) = delete; - WAVFileWriter& operator=(WAVFileWriter&&) = delete; - - ~WAVFileWriter() { - write_tags(); - update_header(); - } - - Optional create( - const std::filesystem::path& filename, - size_t sampling_rate, - const std::string& title_set - ); - -private: - uint32_t sampling_rate { 0 }; - uint32_t info_chunk_size { 0 }; - std::string title { }; - - Optional update_header(); - Optional write_tags(); + public: + WAVFileWriter() = default; + + WAVFileWriter(const WAVFileWriter&) = delete; + WAVFileWriter& operator=(const WAVFileWriter&) = delete; + WAVFileWriter(WAVFileWriter&&) = delete; + WAVFileWriter& operator=(WAVFileWriter&&) = delete; + + ~WAVFileWriter() { + write_tags(); + update_header(); + } + + Optional create( + const std::filesystem::path& filename, + size_t sampling_rate, + const std::string& title_set); + + private: + uint32_t sampling_rate{0}; + uint32_t info_chunk_size{0}; + std::string title{}; + + Optional update_header(); + Optional write_tags(); }; diff --git a/firmware/application/irq_controls.cpp b/firmware/application/irq_controls.cpp index d10b2b881..8619d4bb8 100644 --- a/firmware/application/irq_controls.cpp +++ b/firmware/application/irq_controls.cpp @@ -46,9 +46,9 @@ static std::array switch_debounce; static Encoder encoder; -static volatile uint32_t encoder_position { 0 }; +static volatile uint32_t encoder_position{0}; -static volatile uint32_t touch_phase { 0 }; +static volatile uint32_t touch_phase{0}; /* TODO: Change how touch scanning works. It produces a decent amount of noise * when changing potential on the resistive touch pad. Among other things, I @@ -60,11 +60,11 @@ static volatile uint32_t touch_phase { 0 }; * Noise will only occur when the panel is being touched. Not ideal, but * an acceptable improvement. */ -static std::array touch_pins_configs { - /* State machine will pause here until touch is detected. */ - portapack::IO::TouchPinsConfig::SensePressure, - portapack::IO::TouchPinsConfig::SenseX, - portapack::IO::TouchPinsConfig::SenseY, +static std::array touch_pins_configs{ + /* State machine will pause here until touch is detected. */ + portapack::IO::TouchPinsConfig::SensePressure, + portapack::IO::TouchPinsConfig::SenseX, + portapack::IO::TouchPinsConfig::SenseY, }; static touch::Frame temp_frame; @@ -76,93 +76,91 @@ static bool touch_detected = false; static bool touch_cycle = false; static bool touch_update() { - const auto samples = touch::adc::get(); - const auto current_phase = touch_pins_configs[touch_phase]; - - switch(current_phase) { - case portapack::IO::TouchPinsConfig::SensePressure: - { - const auto z1 = samples.xp - samples.xn; - const auto z2 = samples.yp - samples.yn; - const auto touch_raw = (z1 > touch::touch_threshold) || (z2 > touch::touch_threshold); - touch_debounce = (touch_debounce << 1) | (touch_raw ? 1U : 0U); - touch_detected = ((touch_debounce & touch_debounce_mask) == touch_debounce_mask); - if( !touch_detected && !touch_cycle ) { - temp_frame.pressure = { }; - return false; - } else { - temp_frame.pressure += samples; - } - } - break; - - case portapack::IO::TouchPinsConfig::SenseX: - temp_frame.x += samples; - break; - - case portapack::IO::TouchPinsConfig::SenseY: - temp_frame.y += samples; - break; - - default: - break; - } - - touch_phase++; - if( touch_phase >= touch_pins_configs.size() ) { - /* New iteration, calculate values and flag touch event */ - touch_phase = 0; - temp_frame.touch = touch_detected; - touch_cycle = touch_detected; - touch_frame = temp_frame; - temp_frame = {}; - return true; - } else { - return false; - } + const auto samples = touch::adc::get(); + const auto current_phase = touch_pins_configs[touch_phase]; + + switch (current_phase) { + case portapack::IO::TouchPinsConfig::SensePressure: { + const auto z1 = samples.xp - samples.xn; + const auto z2 = samples.yp - samples.yn; + const auto touch_raw = (z1 > touch::touch_threshold) || (z2 > touch::touch_threshold); + touch_debounce = (touch_debounce << 1) | (touch_raw ? 1U : 0U); + touch_detected = ((touch_debounce & touch_debounce_mask) == touch_debounce_mask); + if (!touch_detected && !touch_cycle) { + temp_frame.pressure = {}; + return false; + } else { + temp_frame.pressure += samples; + } + } break; + + case portapack::IO::TouchPinsConfig::SenseX: + temp_frame.x += samples; + break; + + case portapack::IO::TouchPinsConfig::SenseY: + temp_frame.y += samples; + break; + + default: + break; + } + + touch_phase++; + if (touch_phase >= touch_pins_configs.size()) { + /* New iteration, calculate values and flag touch event */ + touch_phase = 0; + temp_frame.touch = touch_detected; + touch_cycle = touch_detected; + touch_frame = temp_frame; + temp_frame = {}; + return true; + } else { + return false; + } } static uint8_t switches_raw = 0; static bool switches_update(const uint8_t raw) { - // TODO: Only fire event on press, not release? - bool switch_changed = false; - for(size_t i=0; i> i) & 1); - } + // TODO: Only fire event on press, not release? + bool switch_changed = false; + for (size_t i = 0; i < switch_debounce.size(); i++) { + switch_changed |= switch_debounce[i].feed((raw >> i) & 1); + } - return switch_changed; + return switch_changed; } static bool encoder_read() { - const auto delta = encoder.update( - switch_debounce[5].state(), - switch_debounce[6].state() - ); - - if( delta != 0 ) { - encoder_position += delta; - return true;; - } else { - return false; - } + const auto delta = encoder.update( + switch_debounce[5].state(), + switch_debounce[6].state()); + + if (delta != 0) { + encoder_position += delta; + return true; + ; + } else { + return false; + } } void timer0_callback(GPTDriver* const) { - eventmask_t event_mask = 0; - if( touch_update() ) event_mask |= EVT_MASK_TOUCH; - switches_raw = portapack::io.io_update(touch_pins_configs[touch_phase]); - if( switches_update(switches_raw) ) { - event_mask |= EVT_MASK_SWITCHES; - if( encoder_read() ) event_mask |= EVT_MASK_ENCODER; - } - - /* Signal event loop */ - if( event_mask ) { - chSysLockFromIsr(); - chEvtSignalI(thread_controls_event, event_mask); - chSysUnlockFromIsr(); - } + eventmask_t event_mask = 0; + if (touch_update()) event_mask |= EVT_MASK_TOUCH; + switches_raw = portapack::io.io_update(touch_pins_configs[touch_phase]); + if (switches_update(switches_raw)) { + event_mask |= EVT_MASK_SWITCHES; + if (encoder_read()) event_mask |= EVT_MASK_ENCODER; + } + + /* Signal event loop */ + if (event_mask) { + chSysLockFromIsr(); + chEvtSignalI(thread_controls_event, event_mask); + chSysUnlockFromIsr(); + } } /* TODO: Refactor some/all of this to appropriate shared headers? */ @@ -174,49 +172,49 @@ static constexpr uint32_t timer0_match_count = timer0_count_f / ui_interrupt_rat /* GPT driver refers to configuration structure during runtime, so make sure * it sticks around. */ -static GPTConfig timer0_config { - .callback = timer0_callback, - .pr = timer0_prescaler_ratio - 1, +static GPTConfig timer0_config{ + .callback = timer0_callback, + .pr = timer0_prescaler_ratio - 1, }; void controls_init() { - thread_controls_event = chThdSelf(); + thread_controls_event = chThdSelf(); - touch::adc::start(); + touch::adc::start(); - /* GPT timer 0 is used to scan user interface controls -- touch screen, - * navigation switches. - */ - gptStart(&GPTD1, &timer0_config); - gptStartContinuous(&GPTD1, timer0_match_count); + /* GPT timer 0 is used to scan user interface controls -- touch screen, + * navigation switches. + */ + gptStart(&GPTD1, &timer0_config); + gptStartContinuous(&GPTD1, timer0_match_count); } SwitchesState get_switches_state() { - SwitchesState result; - for(size_t i=0; i; @@ -50,7 +50,7 @@ namespace debug { uint8_t switches(); -} /* debug */ -} /* control */ +} // namespace debug +} // namespace control -#endif/*__IRQ_CONTROLS_H__*/ +#endif /*__IRQ_CONTROLS_H__*/ diff --git a/firmware/application/irq_lcd_frame.cpp b/firmware/application/irq_lcd_frame.cpp index 59bce755c..86e11b6d8 100644 --- a/firmware/application/irq_lcd_frame.cpp +++ b/firmware/application/irq_lcd_frame.cpp @@ -31,38 +31,34 @@ static Thread* thread_lcd_frame_event = NULL; static void pin_int4_interrupt_enable() { - thread_lcd_frame_event = chThdSelf(); - nvicEnableVector(PIN_INT4_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_PIN_INT4_IRQ_PRIORITY)); + thread_lcd_frame_event = chThdSelf(); + nvicEnableVector(PIN_INT4_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_PIN_INT4_IRQ_PRIORITY)); } void lcd_frame_sync_configure() { - /* Positive edge sensitivity */ - LPC_GPIO_INT->ISEL &= ~(1U << 4); - LPC_GPIO_INT->SIENR = (1U << 4); - LPC_GPIO_INT->CIENF = (1U << 4); - LPC_GPIO_INT->IST = (1U << 4); + /* Positive edge sensitivity */ + LPC_GPIO_INT->ISEL &= ~(1U << 4); + LPC_GPIO_INT->SIENR = (1U << 4); + LPC_GPIO_INT->CIENF = (1U << 4); + LPC_GPIO_INT->IST = (1U << 4); - LPC_SCU->PINTSEL1 = - (LPC_SCU->PINTSEL1 & ~(0xffU << 0)) - | (portapack::gpio_lcd_te.pad() << 0) - | (portapack::gpio_lcd_te.port() << 5) - ; + LPC_SCU->PINTSEL1 = + (LPC_SCU->PINTSEL1 & ~(0xffU << 0)) | (portapack::gpio_lcd_te.pad() << 0) | (portapack::gpio_lcd_te.port() << 5); - pin_int4_interrupt_enable(); + pin_int4_interrupt_enable(); } extern "C" { CH_IRQ_HANDLER(PIN_INT4_IRQHandler) { - CH_IRQ_PROLOGUE(); + CH_IRQ_PROLOGUE(); - chSysLockFromIsr(); - chEvtSignalI(thread_lcd_frame_event, EVT_MASK_LCD_FRAME_SYNC); - chSysUnlockFromIsr(); + chSysLockFromIsr(); + chEvtSignalI(thread_lcd_frame_event, EVT_MASK_LCD_FRAME_SYNC); + chSysUnlockFromIsr(); - LPC_GPIO_INT->IST = (1U << 4); + LPC_GPIO_INT->IST = (1U << 4); - CH_IRQ_EPILOGUE(); + CH_IRQ_EPILOGUE(); } - } diff --git a/firmware/application/irq_lcd_frame.hpp b/firmware/application/irq_lcd_frame.hpp index 619083e4d..7f41fe9b8 100644 --- a/firmware/application/irq_lcd_frame.hpp +++ b/firmware/application/irq_lcd_frame.hpp @@ -24,4 +24,4 @@ void lcd_frame_sync_configure(); -#endif/*__IRQ_LCD_FRAME_H__*/ +#endif /*__IRQ_LCD_FRAME_H__*/ diff --git a/firmware/application/irq_rtc.cpp b/firmware/application/irq_rtc.cpp index 5bc6c7354..d48ea065a 100644 --- a/firmware/application/irq_rtc.cpp +++ b/firmware/application/irq_rtc.cpp @@ -31,23 +31,22 @@ using namespace lpc43xx; static Thread* thread_rtc_event = NULL; void rtc_interrupt_enable() { - thread_rtc_event = chThdSelf(); - rtc::interrupt::enable_second_inc(); - nvicEnableVector(RTC_IRQn, CORTEX_PRIORITY_MASK(LPC_RTC_IRQ_PRIORITY)); + thread_rtc_event = chThdSelf(); + rtc::interrupt::enable_second_inc(); + nvicEnableVector(RTC_IRQn, CORTEX_PRIORITY_MASK(LPC_RTC_IRQ_PRIORITY)); } extern "C" { CH_IRQ_HANDLER(RTC_IRQHandler) { - CH_IRQ_PROLOGUE(); + CH_IRQ_PROLOGUE(); - chSysLockFromIsr(); - chEvtSignalI(thread_rtc_event, EVT_MASK_RTC_TICK); - chSysUnlockFromIsr(); + chSysLockFromIsr(); + chEvtSignalI(thread_rtc_event, EVT_MASK_RTC_TICK); + chSysUnlockFromIsr(); - rtc::interrupt::clear_all(); + rtc::interrupt::clear_all(); - CH_IRQ_EPILOGUE(); + CH_IRQ_EPILOGUE(); } - } diff --git a/firmware/application/irq_rtc.hpp b/firmware/application/irq_rtc.hpp index b2b67f81e..bfaab2900 100644 --- a/firmware/application/irq_rtc.hpp +++ b/firmware/application/irq_rtc.hpp @@ -24,4 +24,4 @@ void rtc_interrupt_enable(); -#endif/*__IPC_RTC_H__*/ +#endif /*__IPC_RTC_H__*/ diff --git a/firmware/application/log_file.cpp b/firmware/application/log_file.cpp index fa82f4b1e..68956dae2 100644 --- a/firmware/application/log_file.cpp +++ b/firmware/application/log_file.cpp @@ -24,14 +24,14 @@ #include "string_format.hpp" Optional LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) { - std::string timestamp = to_string_timestamp(datetime); - return write_line(timestamp + " " + entry); + std::string timestamp = to_string_timestamp(datetime); + return write_line(timestamp + " " + entry); } Optional LogFile::write_line(const std::string& message) { - auto error = file.write_line(message); - if( !error.is_valid() ) { - file.sync(); - } - return error; + auto error = file.write_line(message); + if (!error.is_valid()) { + file.sync(); + } + return error; } diff --git a/firmware/application/log_file.hpp b/firmware/application/log_file.hpp index 4d5fd4520..78568cea1 100644 --- a/firmware/application/log_file.hpp +++ b/firmware/application/log_file.hpp @@ -33,21 +33,21 @@ using namespace lpc43xx; #define LOG_ROOT_DIR "LOGS" class LogFile { -public: - Optional append(const std::filesystem::path& filename) { - auto result = ensure_directory(filename.parent_path()); - if (result.code()) - return { result }; - - return file.append(filename); - } + public: + Optional append(const std::filesystem::path& filename) { + auto result = ensure_directory(filename.parent_path()); + if (result.code()) + return {result}; - Optional write_entry(const rtc::RTC& datetime, const std::string& entry); + return file.append(filename); + } -private: - File file { }; + Optional write_entry(const rtc::RTC& datetime, const std::string& entry); - Optional write_line(const std::string& message); + private: + File file{}; + + Optional write_line(const std::string& message); }; -#endif/*__LOG_FILE_H__*/ +#endif /*__LOG_FILE_H__*/ diff --git a/firmware/application/lz4.h b/firmware/application/lz4.h index 6453afc45..2464ac60e 100644 --- a/firmware/application/lz4.h +++ b/firmware/application/lz4.h @@ -28,7 +28,7 @@ #ifdef __cplusplus extern "C" { #endif - extern void unlz4_len(const void *aSource, void *aDestination, uint32_t aLength); +extern void unlz4_len(const void* aSource, void* aDestination, uint32_t aLength); #ifdef __cplusplus } #endif diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 7af337ec4..103228343 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -33,83 +33,83 @@ // Note about matched filters: see proc_sonde.hpp -//TEST: Goertzel tone detect -//TEST: Menuview refresh, seems to blink a lot -//TEST: Check AFSK transmit end, skips last bits ? -//TEST: Imperial in whipcalc - -//BUG: Console lock-up if first string to be printed starts with escape character ? -//BUG: (Workaround ok) CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay -//BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one -//BUG: SCANNER Multiple slices -//GLITCH: The about view scroller sometimes misses lines because of a race condition between the display scrolling and drawing the line -//GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?) +// TEST: Goertzel tone detect +// TEST: Menuview refresh, seems to blink a lot +// TEST: Check AFSK transmit end, skips last bits ? +// TEST: Imperial in whipcalc + +// BUG: Console lock-up if first string to be printed starts with escape character ? +// BUG: (Workaround ok) CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay +// BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one +// BUG: SCANNER Multiple slices +// GLITCH: The about view scroller sometimes misses lines because of a race condition between the display scrolling and drawing the line +// GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?) // See fifo.reset_in() ? -//FIXED: Update button in signal gen doesn't work for shape change -//BUG: Signal gen noise shape doesn't work -//TODO: Continue acars receiver. See matched filter, probably doesn't shift the spectrum correctly -//TODO: Add larger description text field in frequency load, under menuview -//TODO: Allow apps to select a preferred FREQMAN file -//TODO: Make play button larger in Replay -//TODO: Add default headphones volume setting in Audio settings -//TODO: Put LNA and VGA controls in Soundboard -//TODO: Make CTCSS display only when squelch is opened -//TODO: DCS decoder -//TODO: Increase resolution of audio FFT view ? Currently 48k/(256/2) (375Hz) because of the use of real values (half of FFT output) -//TODO: Move Touchtunes remote to Custom remote -//TODO: Use escapes \x1B to set colors in text, it works ! -//TODO: Open files in File Manager -//TODO: Ask for filename after record -//TODO: Super simple text file viewer -//TODO: Clean up ReplayThread -//TODO: Cap Wav viewer position -//TODO: Adapt wav viewer position step -//TODO: Use unit_auto_scale -//TODO: Remove make_bistream from encoders.cpp, too complex, stinks. bitstream_append should be enough. -//TODO: Continue work on proc_afskrx_corr, see python script (it works !) -//TODO: De bruijn sequence scanner for encoders -//TODO: FILEMAN Move files -//TODO: Use separate thread for scanning in EPAR TX -//TODO: Make freqman refresh simpler (use previous black rectangle method) -//TODO: Merge AFSK and TONES procs ? -//TODO: NFM RX mode: nav.pop on squelch -//TODO: MORSE use prosigns -//TODO: MORSE live keying mode -//TODO: Use to_string_short_freq wherever possible -//TODO: SCANNER Waveform widget as FFT view ? -//TODO: Optimize (and group ?) CTCSS tone gen code +// FIXED: Update button in signal gen doesn't work for shape change +// BUG: Signal gen noise shape doesn't work +// TODO: Continue acars receiver. See matched filter, probably doesn't shift the spectrum correctly +// TODO: Add larger description text field in frequency load, under menuview +// TODO: Allow apps to select a preferred FREQMAN file +// TODO: Make play button larger in Replay +// TODO: Add default headphones volume setting in Audio settings +// TODO: Put LNA and VGA controls in Soundboard +// TODO: Make CTCSS display only when squelch is opened +// TODO: DCS decoder +// TODO: Increase resolution of audio FFT view ? Currently 48k/(256/2) (375Hz) because of the use of real values (half of FFT output) +// TODO: Move Touchtunes remote to Custom remote +// TODO: Use escapes \x1B to set colors in text, it works ! +// TODO: Open files in File Manager +// TODO: Ask for filename after record +// TODO: Super simple text file viewer +// TODO: Clean up ReplayThread +// TODO: Cap Wav viewer position +// TODO: Adapt wav viewer position step +// TODO: Use unit_auto_scale +// TODO: Remove make_bistream from encoders.cpp, too complex, stinks. bitstream_append should be enough. +// TODO: Continue work on proc_afskrx_corr, see python script (it works !) +// TODO: De bruijn sequence scanner for encoders +// TODO: FILEMAN Move files +// TODO: Use separate thread for scanning in EPAR TX +// TODO: Make freqman refresh simpler (use previous black rectangle method) +// TODO: Merge AFSK and TONES procs ? +// TODO: NFM RX mode: nav.pop on squelch +// TODO: MORSE use prosigns +// TODO: MORSE live keying mode +// TODO: Use to_string_short_freq wherever possible +// TODO: SCANNER Waveform widget as FFT view ? +// TODO: Optimize (and group ?) CTCSS tone gen code /* Continuous (Fox-oring) -12s transmit, 48s space (Sprint 1/5th) -60s transmit, 240s space (Classic 1/5 min) -60s transmit, 360s space (Classic 1/7 min) +12s transmit, 48s space (Sprint 1/5th) +60s transmit, 240s space (Classic 1/5 min) +60s transmit, 360s space (Classic 1/7 min) */ -//TODO: FreqMan: Remove and rename categories -//TODO: Mousejack ? -//TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere) -//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback -//TODO: RDS multiple groups (sequence) -//TODO: Use ModalMessageView confirmation for TX ? -//TODO: Use msgpack for settings, lists... on sd card +// TODO: FreqMan: Remove and rename categories +// TODO: Mousejack ? +// TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere) +// TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback +// TODO: RDS multiple groups (sequence) +// TODO: Use ModalMessageView confirmation for TX ? +// TODO: Use msgpack for settings, lists... on sd card // Multimon-style stuff: -//TODO: DMR detector -//TODO: GSM channel detector -//TODO: Playdead amnesia and login -//TODO: Setup: Play dead by default ? Enable/disable ? +// TODO: DMR detector +// TODO: GSM channel detector +// TODO: Playdead amnesia and login +// TODO: Setup: Play dead by default ? Enable/disable ? // Old or low-priority stuff: -//TODO: Bodet :) -//TODO: Analog TV tx with camcorder font character generator -//TODO: Scan for OOK TX -//TODO: Check more OOK encoders -//BUG (fixed ?): No audio in about when shown second time -//TODO: Show MD5 mismatches for modules not found, etc... -//TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found ui_loadmodule -//BUG: Description doesn't show up first time going to system>module info (UI drawn on top) -//TODO: Two players tic-tac-toe -//TODO: Analog TV pong game +// TODO: Bodet :) +// TODO: Analog TV tx with camcorder font character generator +// TODO: Scan for OOK TX +// TODO: Check more OOK encoders +// BUG (fixed ?): No audio in about when shown second time +// TODO: Show MD5 mismatches for modules not found, etc... +// TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found ui_loadmodule +// BUG: Description doesn't show up first time going to system>module info (UI drawn on top) +// TODO: Two players tic-tac-toe +// TODO: Analog TV pong game #include "ch.h" @@ -140,50 +140,47 @@ Continuous (Fox-oring) #include "sd_card.hpp" #include - -#include "rffc507x.hpp" /* c/m, avoiding initial short ON Ant_DC_Bias pulse, from cold reset */ + +#include "rffc507x.hpp" /* c/m, avoiding initial short ON Ant_DC_Bias pulse, from cold reset */ rffc507x::RFFC507x first_if; static void event_loop() { - static ui::Context context; - static ui::SystemView system_view { - context, - portapack::display.screen_rect() - }; - - EventDispatcher event_dispatcher { &system_view, context }; - static MessageHandlerRegistration message_handler_display_sleep { - Message::ID::DisplaySleep, - [&event_dispatcher](const Message* const) { - event_dispatcher.set_display_sleep(true); - } - }; - - event_dispatcher.run(); + static ui::Context context; + static ui::SystemView system_view{ + context, + portapack::display.screen_rect()}; + + EventDispatcher event_dispatcher{&system_view, context}; + static MessageHandlerRegistration message_handler_display_sleep{ + Message::ID::DisplaySleep, + [&event_dispatcher](const Message* const) { + event_dispatcher.set_display_sleep(true); + }}; + + event_dispatcher.run(); } int main(void) { - first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */ - if( portapack::init() ) { - portapack::display.init(); - - // sdcStart(&SDCD1, nullptr); // Commented out as now happens in portapack.cpp + first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */ + if (portapack::init()) { + portapack::display.init(); - // controls_init(); // Commented out as now happens in portapack.cpp - lcd_frame_sync_configure(); - rtc_interrupt_enable(); + // sdcStart(&SDCD1, nullptr); // Commented out as now happens in portapack.cpp - event_loop(); + // controls_init(); // Commented out as now happens in portapack.cpp + lcd_frame_sync_configure(); + rtc_interrupt_enable(); + event_loop(); - sdcDisconnect(&SDCD1); - sdcStop(&SDCD1); + sdcDisconnect(&SDCD1); + sdcStop(&SDCD1); - portapack::shutdown(); - } + portapack::shutdown(); + } - m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true); - m0_halt(); + m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true); + m0_halt(); - return 0; + return 0; } diff --git a/firmware/application/mcuconf.h b/firmware/application/mcuconf.h index 1f9462be2..aef2f3417 100755 --- a/firmware/application/mcuconf.h +++ b/firmware/application/mcuconf.h @@ -36,34 +36,32 @@ * I2C driver system settings. */ - -#define LPC43XX_I2C_USE_I2C0 TRUE +#define LPC43XX_I2C_USE_I2C0 TRUE /* * SPI driver system settings. */ -#define LPC_SPI_USE_SSP1 TRUE - +#define LPC_SPI_USE_SSP1 TRUE /* * DMA driver system settings. */ -#define LPC_ADC0_IRQ_PRIORITY 1 +#define LPC_ADC0_IRQ_PRIORITY 1 //#define LPC_DMA_IRQ_PRIORITY 2 //#define LPC_ADC1_IRQ_PRIORITY 3 -#define LPC43XX_GPT_TIMER0_IRQ_PRIORITY 2 +#define LPC43XX_GPT_TIMER0_IRQ_PRIORITY 2 //#define LPC43XX_GPT_TIMER1_IRQ_PRIORITY 2 -#define LPC43XX_M0_I2C_I2C0_OR_I2C1_IRQ_PRIORITY 3 -#define LPC43XX_PIN_INT4_IRQ_PRIORITY 3 +#define LPC43XX_M0_I2C_I2C0_OR_I2C1_IRQ_PRIORITY 3 +#define LPC43XX_PIN_INT4_IRQ_PRIORITY 3 -#define LPC_SPI_SSP0_OR_SSP1_IRQ_PRIORITY 3 +#define LPC_SPI_SSP0_OR_SSP1_IRQ_PRIORITY 3 -#define LPC_SDC_SDIO_IRQ_PRIORITY 3 -#define LPC_RTC_IRQ_PRIORITY 3 +#define LPC_SDC_SDIO_IRQ_PRIORITY 3 +#define LPC_RTC_IRQ_PRIORITY 3 -#define LPC43XX_GPT_USE_TIMER0 TRUE +#define LPC43XX_GPT_USE_TIMER0 TRUE //#define LPC43XX_GPT_USE_TIMER1 TRUE -#define LPC43XX_M4TXEVENT_IRQ_PRIORITY 3 +#define LPC43XX_M4TXEVENT_IRQ_PRIORITY 3 diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 0f84574a4..ebd877987 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -29,7 +29,6 @@ #include "hackrf_gpio.hpp" using namespace hackrf::one; - #include "clock_manager.hpp" #include "event_m0.hpp" @@ -48,313 +47,309 @@ using asahi_kasei::ak4951::AK4951; #include "optional.hpp" #include "irq_controls.hpp" -#include "file.hpp" +#include "file.hpp" #include "sd_card.hpp" #include "string_format.hpp" namespace portapack { -portapack::IO io { - portapack::gpio_dir, - portapack::gpio_lcd_rdx, - portapack::gpio_lcd_wrx, - portapack::gpio_io_stbx, - portapack::gpio_addr, - portapack::gpio_lcd_te, - portapack::gpio_dfu, +portapack::IO io{ + portapack::gpio_dir, + portapack::gpio_lcd_rdx, + portapack::gpio_lcd_wrx, + portapack::gpio_io_stbx, + portapack::gpio_addr, + portapack::gpio_lcd_te, + portapack::gpio_dfu, }; portapack::BacklightCAT4004 backlight_cat4004; -portapack::BacklightOnOff backlight_on_off; +portapack::BacklightOnOff backlight_on_off; lcd::ILI9341 display; I2C i2c0(&I2CD0); SPI ssp1(&SPID2); -si5351::Si5351 clock_generator { - i2c0, hackrf::one::si5351_i2c_address -}; +si5351::Si5351 clock_generator{ + i2c0, hackrf::one::si5351_i2c_address}; -ClockManager clock_manager { - i2c0, clock_generator -}; +ClockManager clock_manager{ + i2c0, clock_generator}; -WM8731 audio_codec_wm8731 { i2c0, 0x1a }; -AK4951 audio_codec_ak4951 { i2c0, 0x12 }; +WM8731 audio_codec_wm8731{i2c0, 0x1a}; +AK4951 audio_codec_ak4951{i2c0, 0x12}; ReceiverModel receiver_model; TransmitterModel transmitter_model; TemperatureLogger temperature_logger; -bool antenna_bias { false }; -uint32_t bl_tick_counter { 0 }; +bool antenna_bias{false}; +uint32_t bl_tick_counter{0}; void set_antenna_bias(const bool v) { - antenna_bias = v; + antenna_bias = v; } bool get_antenna_bias() { - return antenna_bias; + return antenna_bias; } -bool speaker_mode { false }; - void set_speaker_mode(const bool v) { - speaker_mode = v; - if (speaker_mode) - audio::output::speaker_unmute(); - else - audio::output::speaker_mute(); - } +bool speaker_mode{false}; +void set_speaker_mode(const bool v) { + speaker_mode = v; + if (speaker_mode) + audio::output::speaker_unmute(); + else + audio::output::speaker_mute(); +} static constexpr uint32_t systick_count(const uint32_t clock_source_f) { - return clock_source_f / CH_FREQUENCY; + return clock_source_f / CH_FREQUENCY; } static constexpr uint32_t systick_load(const uint32_t clock_source_f) { - return systick_count(clock_source_f) - 1; + return systick_count(clock_source_f) - 1; } -constexpr uint32_t i2c0_bus_f = 400000; -constexpr uint32_t i2c0_high_period_ns = 900; +constexpr uint32_t i2c0_bus_f = 400000; +constexpr uint32_t i2c0_high_period_ns = 900; typedef struct { - uint32_t clock_f; - uint32_t systick_count; - uint32_t idivb; - uint32_t idivc; + uint32_t clock_f; + uint32_t systick_count; + uint32_t idivb; + uint32_t idivc; } clock_config_t; static constexpr uint32_t idiv_config(const cgu::CLK_SEL clk_sel, const uint32_t idiv) { - return cgu::IDIV_CTRL { 0, idiv-1, 1, clk_sel }; + return cgu::IDIV_CTRL{0, idiv - 1, 1, clk_sel}; } -constexpr clock_config_t clock_config_irc { - 12000000, systick_load(12000000), - idiv_config(cgu::CLK_SEL::IRC, 1), - idiv_config(cgu::CLK_SEL::IRC, 1), +constexpr clock_config_t clock_config_irc{ + 12000000, + systick_load(12000000), + idiv_config(cgu::CLK_SEL::IRC, 1), + idiv_config(cgu::CLK_SEL::IRC, 1), }; -constexpr clock_config_t clock_config_pll1_boot { - 96000000, systick_load(96000000), - idiv_config(cgu::CLK_SEL::PLL1, 9), - idiv_config(cgu::CLK_SEL::PLL1, 3), +constexpr clock_config_t clock_config_pll1_boot{ + 96000000, + systick_load(96000000), + idiv_config(cgu::CLK_SEL::PLL1, 9), + idiv_config(cgu::CLK_SEL::PLL1, 3), }; -constexpr clock_config_t clock_config_pll1_step { - 100000000, systick_load(100000000), - idiv_config(cgu::CLK_SEL::PLL1, 1), - idiv_config(cgu::CLK_SEL::PLL1, 1), +constexpr clock_config_t clock_config_pll1_step{ + 100000000, + systick_load(100000000), + idiv_config(cgu::CLK_SEL::PLL1, 1), + idiv_config(cgu::CLK_SEL::PLL1, 1), }; -constexpr clock_config_t clock_config_pll1 { - 200000000, systick_load(200000000), - idiv_config(cgu::CLK_SEL::PLL1, 2), - idiv_config(cgu::CLK_SEL::PLL1, 1), +constexpr clock_config_t clock_config_pll1{ + 200000000, + systick_load(200000000), + idiv_config(cgu::CLK_SEL::PLL1, 2), + idiv_config(cgu::CLK_SEL::PLL1, 1), }; -constexpr I2CClockConfig i2c_clock_config_400k_boot_clock { - .clock_source_f = clock_config_pll1_boot.clock_f, - .bus_f = i2c0_bus_f, - .high_period_ns = i2c0_high_period_ns, +constexpr I2CClockConfig i2c_clock_config_400k_boot_clock{ + .clock_source_f = clock_config_pll1_boot.clock_f, + .bus_f = i2c0_bus_f, + .high_period_ns = i2c0_high_period_ns, }; -constexpr I2CClockConfig i2c_clock_config_400k_fast_clock { - .clock_source_f = clock_config_pll1.clock_f, - .bus_f = i2c0_bus_f, - .high_period_ns = i2c0_high_period_ns, +constexpr I2CClockConfig i2c_clock_config_400k_fast_clock{ + .clock_source_f = clock_config_pll1.clock_f, + .bus_f = i2c0_bus_f, + .high_period_ns = i2c0_high_period_ns, }; -constexpr I2CConfig i2c_config_boot_clock { - .high_count = i2c_clock_config_400k_boot_clock.i2c_high_count(), - .low_count = i2c_clock_config_400k_boot_clock.i2c_low_count(), +constexpr I2CConfig i2c_config_boot_clock{ + .high_count = i2c_clock_config_400k_boot_clock.i2c_high_count(), + .low_count = i2c_clock_config_400k_boot_clock.i2c_low_count(), }; -constexpr I2CConfig i2c_config_fast_clock { - .high_count = i2c_clock_config_400k_fast_clock.i2c_high_count(), - .low_count = i2c_clock_config_400k_fast_clock.i2c_low_count(), +constexpr I2CConfig i2c_config_fast_clock{ + .high_count = i2c_clock_config_400k_fast_clock.i2c_high_count(), + .low_count = i2c_clock_config_400k_fast_clock.i2c_low_count(), }; enum class PortaPackModel { - R1_20150901, - R2_20170522, + R1_20150901, + R2_20170522, }; -static bool save_config(int8_t value){ - persistent_memory::set_config_cpld(value); - if(sd_card::status() == sd_card::Status::Mounted){ - make_new_directory("/hardware"); - File file; - auto sucess = file.create("/hardware/settings.txt"); - if(!sucess.is_valid()) { - file.write_line(to_string_dec_uint(value)); - } - } - return true; +static bool save_config(int8_t value) { + persistent_memory::set_config_cpld(value); + if (sd_card::status() == sd_card::Status::Mounted) { + make_new_directory("/hardware"); + File file; + auto sucess = file.create("/hardware/settings.txt"); + if (!sucess.is_valid()) { + file.write_line(to_string_dec_uint(value)); + } + } + return true; } int read_file(std::string name) { - std::string return_string = ""; - File file; - auto success = file.open(name); - - if(!success.is_valid()) { - char one_char[1]; - for(size_t pointer = 0; pointer < file.size() ; pointer++) { - file.seek(pointer); - file.read(one_char, 1); - return_string += one_char[0]; - } - return std::stoi(return_string); - } - return -1; + std::string return_string = ""; + File file; + auto success = file.open(name); + + if (!success.is_valid()) { + char one_char[1]; + for (size_t pointer = 0; pointer < file.size(); pointer++) { + file.seek(pointer); + file.read(one_char, 1); + return_string += one_char[0]; + } + return std::stoi(return_string); + } + return -1; } -static int load_config(){ - static Optional config_value; - if(!config_value.is_valid()){ - int8_t value = portapack::persistent_memory::config_cpld(); - if((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted){ - int data = read_file("/hardware/settings.txt"); - if(data != -1) { - config_value = data; - } - } else { - config_value = value; - } - } - return config_value.value(); +static int load_config() { + static Optional config_value; + if (!config_value.is_valid()) { + int8_t value = portapack::persistent_memory::config_cpld(); + if ((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted) { + int data = read_file("/hardware/settings.txt"); + if (data != -1) { + config_value = data; + } + } else { + config_value = value; + } + } + return config_value.value(); } - static PortaPackModel portapack_model() { - static Optional model; - - if( !model.is_valid() ) { - const auto switches_state = get_switches_state(); - if (switches_state[(size_t)ui::KeyEvent::Up]){ - save_config(1); - // model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway - } - else if (switches_state[(size_t)ui::KeyEvent::Down]){ - save_config(2); - // model = PortaPackModel::R1_20150901; - } - else if (switches_state[(size_t)ui::KeyEvent::Left]){ - save_config(3); - // model = PortaPackModel::R1_20150901; - } - else if (switches_state[(size_t)ui::KeyEvent::Right]){ - save_config(4); - // model = PortaPackModel::R2_20170522; - } - else if (switches_state[(size_t)ui::KeyEvent::Select]){ - save_config(0); - } - - - if (load_config() == 1) { - model = PortaPackModel::R2_20170522; - } else if (load_config() == 2) { - model = PortaPackModel::R1_20150901; - } else if (load_config() == 3) { - model = PortaPackModel::R1_20150901; - } else if (load_config() == 4) { - model = PortaPackModel::R2_20170522; - } else { - if( audio_codec_wm8731.detected() ) { - model = PortaPackModel::R1_20150901; // H1R1 - } else { - model = PortaPackModel::R2_20170522; // H1R2, H2, H2+ - } - } - } - - return model.value(); + static Optional model; + + if (!model.is_valid()) { + const auto switches_state = get_switches_state(); + if (switches_state[(size_t)ui::KeyEvent::Up]) { + save_config(1); + // model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway + } else if (switches_state[(size_t)ui::KeyEvent::Down]) { + save_config(2); + // model = PortaPackModel::R1_20150901; + } else if (switches_state[(size_t)ui::KeyEvent::Left]) { + save_config(3); + // model = PortaPackModel::R1_20150901; + } else if (switches_state[(size_t)ui::KeyEvent::Right]) { + save_config(4); + // model = PortaPackModel::R2_20170522; + } else if (switches_state[(size_t)ui::KeyEvent::Select]) { + save_config(0); + } + + if (load_config() == 1) { + model = PortaPackModel::R2_20170522; + } else if (load_config() == 2) { + model = PortaPackModel::R1_20150901; + } else if (load_config() == 3) { + model = PortaPackModel::R1_20150901; + } else if (load_config() == 4) { + model = PortaPackModel::R2_20170522; + } else { + if (audio_codec_wm8731.detected()) { + model = PortaPackModel::R1_20150901; // H1R1 + } else { + model = PortaPackModel::R2_20170522; // H1R2, H2, H2+ + } + } + } + + return model.value(); } -//audio_codec_wm8731 = H1R1 & H2+ -//audio_codec_ak4951 = H1R2 +// audio_codec_wm8731 = H1R1 & H2+ +// audio_codec_ak4951 = H1R2 static audio::Codec* portapack_audio_codec() { - /* I2C ready OK, Automatic recognition of audio chip */ - return (audio_codec_wm8731.detected()) - ? static_cast(&audio_codec_wm8731) - : static_cast(&audio_codec_ak4951) - ; + /* I2C ready OK, Automatic recognition of audio chip */ + return (audio_codec_wm8731.detected()) + ? static_cast(&audio_codec_wm8731) + : static_cast(&audio_codec_ak4951); } static const portapack::cpld::Config& portapack_cpld_config() { - return (portapack_model() == PortaPackModel::R2_20170522) - ? portapack::cpld::rev_20170522::config - : portapack::cpld::rev_20150901::config; + return (portapack_model() == PortaPackModel::R2_20170522) + ? portapack::cpld::rev_20170522::config + : portapack::cpld::rev_20150901::config; } Backlight* backlight() { - return (portapack_model() == PortaPackModel::R2_20170522) - ? static_cast(&backlight_cat4004) // R2_20170522 - : static_cast(&backlight_on_off); // R1_20150901 + return (portapack_model() == PortaPackModel::R2_20170522) + ? static_cast(&backlight_cat4004) // R2_20170522 + : static_cast(&backlight_on_off); // R1_20150901 } -#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) static LPC_CGU_BASE_CLK_Type* const base_clocks_idivc[] = { - &LPC_CGU->BASE_PERIPH_CLK, - &LPC_CGU->BASE_M4_CLK, - &LPC_CGU->BASE_APB1_CLK, - &LPC_CGU->BASE_APB3_CLK, - &LPC_CGU->BASE_SDIO_CLK, - &LPC_CGU->BASE_SSP1_CLK, + &LPC_CGU->BASE_PERIPH_CLK, + &LPC_CGU->BASE_M4_CLK, + &LPC_CGU->BASE_APB1_CLK, + &LPC_CGU->BASE_APB3_CLK, + &LPC_CGU->BASE_SDIO_CLK, + &LPC_CGU->BASE_SSP1_CLK, }; static void set_idivc_base_clocks(const cgu::CLK_SEL clock_source) { - for(uint32_t i=0; iAUTOBLOCK = 1; - base_clocks_idivc[i]->CLK_SEL = toUType(clock_source); - } + for (uint32_t i = 0; i < ARRAY_SIZE(base_clocks_idivc); i++) { + base_clocks_idivc[i]->AUTOBLOCK = 1; + base_clocks_idivc[i]->CLK_SEL = toUType(clock_source); + } } static void set_clock_config(const clock_config_t& config) { - LPC_CGU->IDIVB_CTRL.word = config.idivb; - LPC_CGU->IDIVC_CTRL.word = config.idivc; - systick_adjust_period(config.systick_count); - halLPCSetSystemClock(config.clock_f); + LPC_CGU->IDIVB_CTRL.word = config.idivb; + LPC_CGU->IDIVC_CTRL.word = config.idivc; + systick_adjust_period(config.systick_count); + halLPCSetSystemClock(config.clock_f); } static void shutdown_base() { - i2c0.stop(); + i2c0.stop(); - set_clock_config(clock_config_irc); + set_clock_config(clock_config_irc); - cgu::pll1::disable(); + cgu::pll1::disable(); - set_idivc_base_clocks(cgu::CLK_SEL::IRC); + set_idivc_base_clocks(cgu::CLK_SEL::IRC); - cgu::pll1::ctrl({ - .pd = 1, - .bypass = 0, - .fbsel = 0, - .direct = 1, - .psel = 0, - .autoblock = 1, - .nsel = 0, - .msel = 23, - .clk_sel = cgu::CLK_SEL::IRC, - }); + cgu::pll1::ctrl({ + .pd = 1, + .bypass = 0, + .fbsel = 0, + .direct = 1, + .psel = 0, + .autoblock = 1, + .nsel = 0, + .msel = 23, + .clk_sel = cgu::CLK_SEL::IRC, + }); - cgu::pll1::enable(); - while( !cgu::pll1::is_locked() ); + cgu::pll1::enable(); + while (!cgu::pll1::is_locked()) + ; - set_clock_config(clock_config_pll1_boot); + set_clock_config(clock_config_pll1_boot); - i2c0.start(i2c_config_boot_clock); + i2c0.start(i2c_config_boot_clock); - clock_manager.shutdown(); + clock_manager.shutdown(); } /* Clock scheme after exiting bootloader in SPIFI mode: - * + * * XTAL_OSC = powered down * * PLL0USB = powered down @@ -376,7 +371,7 @@ static void shutdown_base() { */ /* Clock scheme during PortaPack operation: - * + * * XTAL_OSC = powered down * * PLL0USB = powered down @@ -400,138 +395,138 @@ static void shutdown_base() { */ bool init() { - set_idivc_base_clocks(cgu::CLK_SEL::IDIVC); - - i2c0.start(i2c_config_boot_clock); - - // Keeping this here for now incase we need to revert - // if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) { - // shutdown_base(); - // return false; - // } - - // if( !hackrf::cpld::load_sram() ) { - // chSysHalt(); - // } - chThdSleepMilliseconds(100); + set_idivc_base_clocks(cgu::CLK_SEL::IDIVC); + + i2c0.start(i2c_config_boot_clock); + + // Keeping this here for now incase we need to revert + // if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) { + // shutdown_base(); + // return false; + // } + + // if( !hackrf::cpld::load_sram() ) { + // chSysHalt(); + // } + chThdSleepMilliseconds(100); + + configure_pins_portapack(); + + portapack::io.init(); + + clock_manager.init_clock_generator(); + + i2c0.stop(); + + chThdSleepMilliseconds(10); + + set_clock_config(clock_config_irc); + cgu::pll1::disable(); + + /* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4 + * core clock speed to the 110 - 204MHz range. + */ + + /* Step into the 90-110MHz M4 clock range */ + /* OG: + * Fclkin = 40M + * /N=2 = 20M = PFDin + * Fcco = PFDin * (M=10) = 200M + * r9: + * Fclkin = 10M + * /N=1 = 10M = PFDin + * Fcco = PFDin * (M=20) = 200M + * Fclk = Fcco / (2*(P=1)) = 100M + */ + cgu::pll1::ctrl({ + .pd = 1, + .bypass = 0, + .fbsel = 0, + .direct = 0, + .psel = 0, + .autoblock = 1, + .nsel = hackrf_r9 ? 0UL : 1UL, + .msel = hackrf_r9 ? 19UL : 9UL, + .clk_sel = cgu::CLK_SEL::GP_CLKIN, + }); + + cgu::pll1::enable(); + while (!cgu::pll1::is_locked()) + ; - configure_pins_portapack(); - - portapack::io.init(); - - clock_manager.init_clock_generator(); - - i2c0.stop(); - - chThdSleepMilliseconds(10); - - set_clock_config(clock_config_irc); - cgu::pll1::disable(); - - /* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4 - * core clock speed to the 110 - 204MHz range. - */ - - /* Step into the 90-110MHz M4 clock range */ - /* OG: - * Fclkin = 40M - * /N=2 = 20M = PFDin - * Fcco = PFDin * (M=10) = 200M - * r9: - * Fclkin = 10M - * /N=1 = 10M = PFDin - * Fcco = PFDin * (M=20) = 200M - * Fclk = Fcco / (2*(P=1)) = 100M - */ - cgu::pll1::ctrl({ - .pd = 1, - .bypass = 0, - .fbsel = 0, - .direct = 0, - .psel = 0, - .autoblock = 1, - .nsel = hackrf_r9 ? 0UL : 1UL, - .msel = hackrf_r9 ? 19UL : 9UL, - .clk_sel = cgu::CLK_SEL::GP_CLKIN, - }); - - cgu::pll1::enable(); - while( !cgu::pll1::is_locked() ); - - set_clock_config(clock_config_pll1_step); - - /* Delay >50us at 90-110MHz clock speed */ - volatile uint32_t delay = 1400; - while(delay--); - - set_clock_config(clock_config_pll1); - - /* Remove /2P divider from PLL1 output to achieve full speed */ - cgu::pll1::direct(); - - i2c0.start(i2c_config_fast_clock); - chThdSleepMilliseconds(10); - - /* Cache some configuration data from persistent memory. */ - persistent_memory::cache::init(); - - touch::adc::init(); - controls_init(); - chThdSleepMilliseconds(10); - - clock_manager.set_reference_ppb(persistent_memory::correction_ppb()); - clock_manager.enable_if_clocks(); - clock_manager.enable_codec_clocks(); - radio::init(); - - sdcStart(&SDCD1, nullptr); - sd_card::poll_inserted(); - - chThdSleepMilliseconds(10); - - portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config()); - if ( result == portapack::cpld::CpldUpdateStatus::Program_failed ) { - - chThdSleepMilliseconds(10); - // Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update - // Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update - if (load_config() != 3 /* left */ && load_config() != 4 /* right */){ - shutdown_base(); - return false; - } - } - - if( !hackrf::cpld::load_sram() ) { - chSysHalt(); - } - - chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues - - LPC_CREG->DMAMUX = portapack::gpdma_mux; - gpdma::controller.enable(); + set_clock_config(clock_config_pll1_step); - chThdSleepMilliseconds(10); - - audio::init(portapack_audio_codec()); - + /* Delay >50us at 90-110MHz clock speed */ + volatile uint32_t delay = 1400; + while (delay--) + ; - return true; + set_clock_config(clock_config_pll1); + + /* Remove /2P divider from PLL1 output to achieve full speed */ + cgu::pll1::direct(); + + i2c0.start(i2c_config_fast_clock); + chThdSleepMilliseconds(10); + + /* Cache some configuration data from persistent memory. */ + persistent_memory::cache::init(); + + touch::adc::init(); + controls_init(); + chThdSleepMilliseconds(10); + + clock_manager.set_reference_ppb(persistent_memory::correction_ppb()); + clock_manager.enable_if_clocks(); + clock_manager.enable_codec_clocks(); + radio::init(); + + sdcStart(&SDCD1, nullptr); + sd_card::poll_inserted(); + + chThdSleepMilliseconds(10); + + portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config()); + if (result == portapack::cpld::CpldUpdateStatus::Program_failed) { + chThdSleepMilliseconds(10); + // Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update + // Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update + if (load_config() != 3 /* left */ && load_config() != 4 /* right */) { + shutdown_base(); + return false; + } + } + + if (!hackrf::cpld::load_sram()) { + chSysHalt(); + } + + chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues + + LPC_CREG->DMAMUX = portapack::gpdma_mux; + gpdma::controller.enable(); + + chThdSleepMilliseconds(10); + + audio::init(portapack_audio_codec()); + + return true; } void shutdown(const bool leave_screen_on) { - gpdma::controller.disable(); + gpdma::controller.disable(); + + if (!leave_screen_on) { + backlight()->off(); + display.shutdown(); + } - if (!leave_screen_on) { - backlight()->off(); - display.shutdown(); - } - - radio::disable(); - audio::shutdown(); + radio::disable(); + audio::shutdown(); - hackrf::cpld::init_from_eeprom(); + hackrf::cpld::init_from_eeprom(); - shutdown_base(); + shutdown_base(); } } /* namespace portapack */ diff --git a/firmware/application/protocols/aprs.cpp b/firmware/application/protocols/aprs.cpp index 6dbbbb72e..027f34d3f 100644 --- a/firmware/application/protocols/aprs.cpp +++ b/firmware/application/protocols/aprs.cpp @@ -29,24 +29,20 @@ using namespace ax25; namespace aprs { -void make_aprs_frame(const char * src_address, const uint32_t src_ssid, - const char * dest_address, const uint32_t dest_ssid, - const std::string& payload) { - - AX25Frame frame; - - char address[14] = { 0 }; - - memcpy(&address[0], dest_address, 6); - memcpy(&address[7], src_address, 6); - //euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2 - // SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number) - // Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end) - address[6] = (dest_ssid | 48); - address[13] = (src_ssid | 48); - - - frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload); +void make_aprs_frame(const char* src_address, const uint32_t src_ssid, const char* dest_address, const uint32_t dest_ssid, const std::string& payload) { + AX25Frame frame; + + char address[14] = {0}; + + memcpy(&address[0], dest_address, 6); + memcpy(&address[7], src_address, 6); + // euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2 + // SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number) + // Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end) + address[6] = (dest_ssid | 48); + address[13] = (src_ssid | 48); + + frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload); } } /* namespace aprs */ diff --git a/firmware/application/protocols/aprs.hpp b/firmware/application/protocols/aprs.hpp index 4229b9e51..25d7a973a 100644 --- a/firmware/application/protocols/aprs.hpp +++ b/firmware/application/protocols/aprs.hpp @@ -28,11 +28,13 @@ namespace aprs { - void make_aprs_frame( - const char * src_address, const uint32_t src_ssid, - const char * dest_address, const uint32_t dest_ssid, - const std::string& payload); +void make_aprs_frame( + const char* src_address, + const uint32_t src_ssid, + const char* dest_address, + const uint32_t dest_ssid, + const std::string& payload); } /* namespace aprs */ -#endif/*__APRS_H__*/ +#endif /*__APRS_H__*/ diff --git a/firmware/application/protocols/ax25.cpp b/firmware/application/protocols/ax25.cpp index 492f6cfd5..a9bbfb07c 100644 --- a/firmware/application/protocols/ax25.cpp +++ b/firmware/application/protocols/ax25.cpp @@ -26,109 +26,101 @@ namespace ax25 { -void AX25Frame::make_extended_field(char * const data, size_t length) { - size_t i = 0; - - for (i = 0; i < length - 1; i++) - add_data(data[i] << 1); - - add_data((data[i] << 1) | 1); +void AX25Frame::make_extended_field(char* const data, size_t length) { + size_t i = 0; + + for (i = 0; i < length - 1; i++) + add_data(data[i] << 1); + + add_data((data[i] << 1) | 1); } void AX25Frame::NRZI_add_bit(const uint32_t bit) { - if (!bit) - current_bit ^= 1; // Zero: flip - - current_byte <<= 1; - current_byte |= current_bit; - - bit_counter++; - - if (bit_counter == 8) { - bit_counter = 0; - *bb_data_ptr = current_byte; - bb_data_ptr++; - } + if (!bit) + current_bit ^= 1; // Zero: flip + + current_byte <<= 1; + current_byte |= current_bit; + + bit_counter++; + + if (bit_counter == 8) { + bit_counter = 0; + *bb_data_ptr = current_byte; + bb_data_ptr++; + } } -void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data) -{ - bool bit; - - if (is_data) - crc_ccitt.process_byte(byte); - - for (uint32_t i = 0; i < 8; i++) - { - bit = (byte >> i) & 1; - - NRZI_add_bit(bit); - - if (bit) - { - ones_counter++; - if ((ones_counter == 5) && (!is_flag)) - { - NRZI_add_bit(0); - ones_counter = 0; - } - } - else - ones_counter = 0; - } +void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data) { + bool bit; + + if (is_data) + crc_ccitt.process_byte(byte); + + for (uint32_t i = 0; i < 8; i++) { + bit = (byte >> i) & 1; + + NRZI_add_bit(bit); + + if (bit) { + ones_counter++; + if ((ones_counter == 5) && (!is_flag)) { + NRZI_add_bit(0); + ones_counter = 0; + } + } else + ones_counter = 0; + } } -void AX25Frame::flush() -{ - if (bit_counter) - *bb_data_ptr = current_byte << (8 - bit_counter); //euquiq: This was 7 but there are 8 bits +void AX25Frame::flush() { + if (bit_counter) + *bb_data_ptr = current_byte << (8 - bit_counter); // euquiq: This was 7 but there are 8 bits }; void AX25Frame::add_flag() { - add_byte(AX25_FLAG, true, false); + add_byte(AX25_FLAG, true, false); }; void AX25Frame::add_data(uint8_t byte) { - add_byte(byte, false, true); + add_byte(byte, false, true); }; void AX25Frame::add_checksum() { - auto checksum = crc_ccitt.checksum(); - add_byte(checksum, false, false); - add_byte(checksum >> 8, false, false); + auto checksum = crc_ccitt.checksum(); + add_byte(checksum, false, false); + add_byte(checksum >> 8, false, false); } -void AX25Frame::make_ui_frame(char * const address, const uint8_t control, - const uint8_t protocol, const std::string& info) { - - size_t i; - - bb_data_ptr = (uint16_t*)shared_memory.bb_data.data; - memset(bb_data_ptr, 0, sizeof(shared_memory.bb_data.data)); - bit_counter = 0; - current_bit = 0; - current_byte = 0; - ones_counter = 0; - crc_ccitt.reset(); - - add_flag(); - add_flag(); - add_flag(); - add_flag(); - - make_extended_field(address, 14); - add_data(control); - add_data(protocol); - - for (i = 0; i < info.size(); i++) - add_data(info[i]); - - add_checksum(); - - add_flag(); - add_flag(); - - flush(); +void AX25Frame::make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info) { + size_t i; + + bb_data_ptr = (uint16_t*)shared_memory.bb_data.data; + memset(bb_data_ptr, 0, sizeof(shared_memory.bb_data.data)); + bit_counter = 0; + current_bit = 0; + current_byte = 0; + ones_counter = 0; + crc_ccitt.reset(); + + add_flag(); + add_flag(); + add_flag(); + add_flag(); + + make_extended_field(address, 14); + add_data(control); + add_data(protocol); + + for (i = 0; i < info.size(); i++) + add_data(info[i]); + + add_checksum(); + + add_flag(); + add_flag(); + + flush(); } } /* namespace ax25 */ diff --git a/firmware/application/protocols/ax25.hpp b/firmware/application/protocols/ax25.hpp index 8819cf5a7..4b02e5934 100644 --- a/firmware/application/protocols/ax25.hpp +++ b/firmware/application/protocols/ax25.hpp @@ -33,37 +33,36 @@ namespace ax25 { enum protocol_id_t { - X25_PLP = 0x01, - COMP_TCPIP = 0x06, - UNCOMP_TCPIP = 0x07, - SEG_FRAG = 0x08, - FLEXNET = 0xCE, - NO_LAYER3 = 0xF0 + X25_PLP = 0x01, + COMP_TCPIP = 0x06, + UNCOMP_TCPIP = 0x07, + SEG_FRAG = 0x08, + FLEXNET = 0xCE, + NO_LAYER3 = 0xF0 }; class AX25Frame { -public: - void make_ui_frame(char * const address, const uint8_t control, const uint8_t protocol, - const std::string& info); - -private: - void NRZI_add_bit(const uint32_t bit); - void make_extended_field(char * const data, size_t length); - void add_byte(uint8_t byte, bool is_flag, bool is_data); - void add_data(uint8_t byte); - void add_checksum(); - void add_flag(); - void flush(); - - uint16_t * bb_data_ptr { nullptr }; - uint8_t current_bit { 0 }; - uint8_t current_byte { 0 }; - size_t bit_counter { 0 }; - uint8_t ones_counter { 0 }; - - CRC<16, true, true> crc_ccitt { 0x1021, 0xFFFF, 0xFFFF }; + public: + void make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info); + + private: + void NRZI_add_bit(const uint32_t bit); + void make_extended_field(char* const data, size_t length); + void add_byte(uint8_t byte, bool is_flag, bool is_data); + void add_data(uint8_t byte); + void add_checksum(); + void add_flag(); + void flush(); + + uint16_t* bb_data_ptr{nullptr}; + uint8_t current_bit{0}; + uint8_t current_byte{0}; + size_t bit_counter{0}; + uint8_t ones_counter{0}; + + CRC<16, true, true> crc_ccitt{0x1021, 0xFFFF, 0xFFFF}; }; } /* namespace ax25 */ -#endif/*__AX25_H__*/ +#endif /*__AX25_H__*/ diff --git a/firmware/application/protocols/bht.cpp b/firmware/application/protocols/bht.cpp index 787ce2bec..6291caeea 100644 --- a/firmware/application/protocols/bht.cpp +++ b/firmware/application/protocols/bht.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -24,131 +24,129 @@ #include "portapack_persistent_memory.hpp" size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_number, uint32_t relay_state) { - size_t c; - const encoder_def_t * um3750_def; - uint8_t bits[12]; - std::string ep_fragments; - //char ep_message[13] = { 0 }; - - // Repeated 2x 26 times - // Whole frame + space = 128ms, data only = 64ms - - um3750_def = &encoder_defs[ENCODER_UM3750]; - - // City code is bit-reversed - for (c = 0; c < 8; c++) - bits[c] = (city_code >> c) & 1; - - bits[8] = (family_code_ep >> 1) & 1; - bits[9] = family_code_ep & 1; - bits[10] = relay_number & 1; - bits[11] = relay_state ? 1 : 0; - - // Text for display - //for (c = 0; c < 12; c++) - // ep_message[c] = bits[c] + '0'; - - c = 0; - for (auto ch : um3750_def->word_format) { - if (ch == 'S') - ep_fragments += um3750_def->sync; - else - ep_fragments += um3750_def->bit_format[bits[c++]]; - } - - // Return bitstream length - return make_bitstream(ep_fragments); + size_t c; + const encoder_def_t* um3750_def; + uint8_t bits[12]; + std::string ep_fragments; + // char ep_message[13] = { 0 }; + + // Repeated 2x 26 times + // Whole frame + space = 128ms, data only = 64ms + + um3750_def = &encoder_defs[ENCODER_UM3750]; + + // City code is bit-reversed + for (c = 0; c < 8; c++) + bits[c] = (city_code >> c) & 1; + + bits[8] = (family_code_ep >> 1) & 1; + bits[9] = family_code_ep & 1; + bits[10] = relay_number & 1; + bits[11] = relay_state ? 1 : 0; + + // Text for display + // for (c = 0; c < 12; c++) + // ep_message[c] = bits[c] + '0'; + + c = 0; + for (auto ch : um3750_def->word_format) { + if (ch == 'S') + ep_fragments += um3750_def->sync; + else + ep_fragments += um3750_def->bit_format[bits[c++]]; + } + + // Return bitstream length + return make_bitstream(ep_fragments); } std::string gen_message_xy(const std::string& ascii_code) { - std::string local_code = ascii_code; - uint8_t ccir_message[XY_TONE_COUNT]; - uint8_t translate; - uint32_t c; - - // Replace repeats with E code - for (c = 1; c < XY_TONE_COUNT; c++) - if (local_code[c] == local_code[c - 1]) local_code[c] = 'E'; - - for (c = 0; c < XY_TONE_COUNT; c++) { - if (local_code[c] <= '9') - translate = local_code[c] - '0'; - else - translate = local_code[c] - 'A' + 10; - ccir_message[c] = (translate < 16) ? translate : 0; // Sanitize - } - - // Copy for baseband - memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT); - - // Return as text for display - return local_code; + std::string local_code = ascii_code; + uint8_t ccir_message[XY_TONE_COUNT]; + uint8_t translate; + uint32_t c; + + // Replace repeats with E code + for (c = 1; c < XY_TONE_COUNT; c++) + if (local_code[c] == local_code[c - 1]) local_code[c] = 'E'; + + for (c = 0; c < XY_TONE_COUNT; c++) { + if (local_code[c] <= '9') + translate = local_code[c] - '0'; + else + translate = local_code[c] - 'A' + 10; + ccir_message[c] = (translate < 16) ? translate : 0; // Sanitize + } + + // Copy for baseband + memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT); + + // Return as text for display + return local_code; } -std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, - bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, - size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D) { - uint8_t ccir_message[XY_TONE_COUNT]; - size_t c; - - // Header - ccir_message[0] = (header_code_a / 10); - ccir_message[1] = (header_code_a % 10); - ccir_message[2] = (header_code_b / 10); - ccir_message[3] = (header_code_b % 10); - - // Addresses - ccir_message[4] = (city_code / 10); - ccir_message[5] = (city_code % 10); - ccir_message[6] = family_code; - - if (subfamily_wc) - ccir_message[7] = 0xA; // Wildcard - else - ccir_message[7] = subfamily_code; - - if (id_wc) { - ccir_message[8] = 0xA; // Wildcard - ccir_message[9] = 0xA; // Wildcard - } else { - ccir_message[8] = (receiver_code / 10); - ccir_message[9] = (receiver_code % 10); - } - - ccir_message[10] = 0xB; - - // Relay states - ccir_message[11] = relay_state_A; - ccir_message[12] = relay_state_B; - ccir_message[13] = relay_state_C; - ccir_message[14] = relay_state_D; - - ccir_message[15] = 0xB; - - // End - for (c = 16; c < XY_TONE_COUNT; c++) - ccir_message[c] = 0; - - // Replace repeats with E code - for (c = 1; c < XY_TONE_COUNT; c++) - if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 0xE; - - // Copy for baseband - memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT); - - // Return as text for display - return ccir_to_ascii(ccir_message); +std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D) { + uint8_t ccir_message[XY_TONE_COUNT]; + size_t c; + + // Header + ccir_message[0] = (header_code_a / 10); + ccir_message[1] = (header_code_a % 10); + ccir_message[2] = (header_code_b / 10); + ccir_message[3] = (header_code_b % 10); + + // Addresses + ccir_message[4] = (city_code / 10); + ccir_message[5] = (city_code % 10); + ccir_message[6] = family_code; + + if (subfamily_wc) + ccir_message[7] = 0xA; // Wildcard + else + ccir_message[7] = subfamily_code; + + if (id_wc) { + ccir_message[8] = 0xA; // Wildcard + ccir_message[9] = 0xA; // Wildcard + } else { + ccir_message[8] = (receiver_code / 10); + ccir_message[9] = (receiver_code % 10); + } + + ccir_message[10] = 0xB; + + // Relay states + ccir_message[11] = relay_state_A; + ccir_message[12] = relay_state_B; + ccir_message[13] = relay_state_C; + ccir_message[14] = relay_state_D; + + ccir_message[15] = 0xB; + + // End + for (c = 16; c < XY_TONE_COUNT; c++) + ccir_message[c] = 0; + + // Replace repeats with E code + for (c = 1; c < XY_TONE_COUNT; c++) + if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 0xE; + + // Copy for baseband + memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT); + + // Return as text for display + return ccir_to_ascii(ccir_message); } -std::string ccir_to_ascii(uint8_t * ccir) { - std::string ascii; - - for (size_t c = 0; c < XY_TONE_COUNT; c++) { - if (ccir[c] > 9) - ascii += (char)(ccir[c] - 10 + 'A'); - else - ascii += (char)(ccir[c] + '0'); - } - - return ascii; +std::string ccir_to_ascii(uint8_t* ccir) { + std::string ascii; + + for (size_t c = 0; c < XY_TONE_COUNT; c++) { + if (ccir[c] > 9) + ascii += (char)(ccir[c] - 10 + 'A'); + else + ascii += (char)(ccir[c] + '0'); + } + + return ascii; } diff --git a/firmware/application/protocols/bht.hpp b/firmware/application/protocols/bht.hpp index af0f297d9..7f4f6fae5 100644 --- a/firmware/application/protocols/bht.hpp +++ b/firmware/application/protocols/bht.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,24 +28,22 @@ using namespace encoders; -#define XY_TONE_DURATION ((TONES_SAMPLERATE * 0.1) - 1) // 100ms -#define XY_SILENCE (TONES_SAMPLERATE * 0.4) // 400ms -#define XY_TONE_COUNT 20 -#define XY_MAX_CITY 99 +#define XY_TONE_DURATION ((TONES_SAMPLERATE * 0.1) - 1) // 100ms +#define XY_SILENCE (TONES_SAMPLERATE * 0.4) // 400ms +#define XY_TONE_COUNT 20 +#define XY_MAX_CITY 99 + +#define EPAR_BIT_DURATION (OOK_SAMPLERATE / 580) +#define EPAR_REPEAT_COUNT 26 +#define EPAR_MAX_CITY 255 -#define EPAR_BIT_DURATION (OOK_SAMPLERATE / 580) -#define EPAR_REPEAT_COUNT 26 -#define EPAR_MAX_CITY 255 - struct bht_city { - std::string name; - uint8_t freq_index; - bool recent; + std::string name; + uint8_t freq_index; + bool recent; }; size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_state_A, uint32_t relay_state_B); std::string gen_message_xy(const std::string& code); -std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, - bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, - size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D); -std::string ccir_to_ascii(uint8_t * ccir); +std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D); +std::string ccir_to_ascii(uint8_t* ccir); diff --git a/firmware/application/protocols/dcs.cpp b/firmware/application/protocols/dcs.cpp index 95bfaf1ff..829317907 100644 --- a/firmware/application/protocols/dcs.cpp +++ b/firmware/application/protocols/dcs.cpp @@ -25,523 +25,523 @@ namespace dcs { const uint16_t dcs_parity[DCS_CODES_NB] = { - 0b11000111010, // 0 - 0b01001001111, // 1 - 0b01010100101, // 2 - 0b11011010000, // 3 - 0b01101110001, // 4 - 0b11100000100, // 5 - 0b11111101110, // 6 - 0b01110011011, // 7 - 0b00011011001, // 8 - 0b10010101100, // 9 - 0b10001000110, // 10 - 0b00000110011, // 11 - 0b10110010010, // 12 - 0b00111100111, // 13 - 0b00100001101, // 14 - 0b10101111000, // 15 - 0b11110001001, // 16 - 0b01111111100, // 17 - 0b01100010110, // 18 - 0b11101100011, // 19 - 0b01011000010, // 20 - 0b11010110111, // 21 - 0b11001011101, // 22 - 0b01000101000, // 23 - 0b00101101010, // 24 - 0b10100011111, // 25 - 0b10111110101, // 26 - 0b00110000000, // 27 - 0b10000100001, // 28 - 0b00001010100, // 29 - 0b00010111110, // 30 - 0b10011001011, // 31 - 0b10101011100, // 32 - 0b00100101001, // 33 - 0b00111000011, // 34 - 0b10110110110, // 35 - 0b00000010111, // 36 - 0b10001100010, // 37 - 0b10010001000, // 38 - 0b00011111101, // 39 - 0b01110111111, // 40 - 0b11111001010, // 41 - 0b11100100000, // 42 - 0b01101010101, // 43 - 0b11011110100, // 44 - 0b01010000001, // 45 - 0b01001101011, // 46 - 0b11000011110, // 47 - 0b10011101111, // 48 - 0b00010011010, // 49 - 0b00001110000, // 50 - 0b10000000101, // 51 - 0b00110100100, // 52 - 0b10111010001, // 53 - 0b10100111011, // 54 - 0b00101001110, // 55 - 0b01000001100, // 56 - 0b11001111001, // 57 - 0b11010010011, // 58 - 0b01011100110, // 59 - 0b11101000111, // 60 - 0b01100110010, // 61 - 0b01111011000, // 62 - 0b11110101101, // 63 - 0b00011110110, // 64 - 0b10010000011, // 65 - 0b10001101001, // 66 - 0b00000011100, // 67 - 0b10110111101, // 68 - 0b00111001000, // 69 - 0b00100100010, // 70 - 0b10101010111, // 71 - 0b11000010101, // 72 - 0b01001100000, // 73 - 0b01010001010, // 74 - 0b11011111111, // 75 - 0b01101011110, // 76 - 0b11100101011, // 77 - 0b11111000001, // 78 - 0b01110110100, // 79 - 0b00101000101, // 80 - 0b10100110000, // 81 - 0b10111011010, // 82 - 0b00110101111, // 83 - 0b10000001110, // 84 - 0b00001111011, // 85 - 0b00010010001, // 86 - 0b10011100100, // 87 - 0b11110100110, // 88 - 0b01111010011, // 89 - 0b01100111001, // 90 - 0b11101001100, // 91 - 0b01011101101, // 92 - 0b11010011000, // 93 - 0b11001110010, // 94 - 0b01000000111, // 95 - 0b01110010000, // 96 - 0b11111100101, // 97 - 0b11100001111, // 98 - 0b01101111010, // 99 - 0b11011011011, // 100 - 0b01010101110, // 101 - 0b01001000100, // 102 - 0b11000110001, // 103 - 0b10101110011, // 104 - 0b00100000110, // 105 - 0b00111101100, // 106 - 0b10110011001, // 107 - 0b00000111000, // 108 - 0b10001001101, // 109 - 0b10010100111, // 110 - 0b00011010010, // 111 - 0b01000100011, // 112 - 0b11001010110, // 113 - 0b11010111100, // 114 - 0b01011001001, // 115 - 0b11101101000, // 116 - 0b01100011101, // 117 - 0b01111110111, // 118 - 0b11110000010, // 119 - 0b10011000000, // 120 - 0b00010110101, // 121 - 0b00001011111, // 122 - 0b10000101010, // 123 - 0b00110001011, // 124 - 0b10111111110, // 125 - 0b10100010100, // 126 - 0b00101100001, // 127 - 0b11111010111, // 128 - 0b01110100010, // 129 - 0b01101001000, // 130 - 0b11100111101, // 131 - 0b01010011100, // 132 - 0b11011101001, // 133 - 0b11000000011, // 134 - 0b01001110110, // 135 - 0b00100110100, // 136 - 0b10101000001, // 137 - 0b10110101011, // 138 - 0b00111011110, // 139 - 0b10001111111, // 140 - 0b00000001010, // 141 - 0b00011100000, // 142 - 0b10010010101, // 143 - 0b11001100100, // 144 - 0b01000010001, // 145 - 0b01011111011, // 146 - 0b11010001110, // 147 - 0b01100101111, // 148 - 0b11101011010, // 149 - 0b11110110000, // 150 - 0b01111000101, // 151 - 0b00010000111, // 152 - 0b10011110010, // 153 - 0b10000011000, // 154 - 0b00001101101, // 155 - 0b10111001100, // 156 - 0b00110111001, // 157 - 0b00101010011, // 158 - 0b10100100110, // 159 - 0b10010110001, // 160 - 0b00011000100, // 161 - 0b00000101110, // 162 - 0b10001011011, // 163 - 0b00111111010, // 164 - 0b10110001111, // 165 - 0b10101100101, // 166 - 0b00100010000, // 167 - 0b01001010010, // 168 - 0b11000100111, // 169 - 0b11011001101, // 170 - 0b01010111000, // 171 - 0b11100011001, // 172 - 0b01101101100, // 173 - 0b01110000110, // 174 - 0b11111110011, // 175 - 0b10100000010, // 176 - 0b00101110111, // 177 - 0b00110011101, // 178 - 0b10111101000, // 179 - 0b00001001001, // 180 - 0b10000111100, // 181 - 0b10011010110, // 182 - 0b00010100011, // 183 - 0b01111100001, // 184 - 0b11110010100, // 185 - 0b11101111110, // 186 - 0b01100001011, // 187 - 0b11010101010, // 188 - 0b01011011111, // 189 - 0b01000110101, // 190 - 0b11001000000, // 191 - 0b00100011011, // 192 - 0b10101101110, // 193 - 0b10110000100, // 194 - 0b00111110001, // 195 - 0b10001010000, // 196 - 0b00000100101, // 197 - 0b00011001111, // 198 - 0b10010111010, // 199 - 0b11111111000, // 200 - 0b01110001101, // 201 - 0b01101100111, // 202 - 0b11100010010, // 203 - 0b01010110011, // 204 - 0b11011000110, // 205 - 0b11000101100, // 206 - 0b01001011001, // 207 - 0b00010101000, // 208 - 0b10011011101, // 209 - 0b10000110111, // 210 - 0b00001000010, // 211 - 0b10111100011, // 212 - 0b00110010110, // 213 - 0b00101111100, // 214 - 0b10100001001, // 215 - 0b11001001011, // 216 - 0b01000111110, // 217 - 0b01011010100, // 218 - 0b11010100001, // 219 - 0b01100000000, // 220 - 0b11101110101, // 221 - 0b11110011111, // 222 - 0b01111101010, // 223 - 0b01001111101, // 224 - 0b11000001000, // 225 - 0b11011100010, // 226 - 0b01010010111, // 227 - 0b11100110110, // 228 - 0b01101000011, // 229 - 0b01110101001, // 230 - 0b11111011100, // 231 - 0b10010011110, // 232 - 0b00011101011, // 233 - 0b00000000001, // 234 - 0b10001110100, // 235 - 0b00111010101, // 236 - 0b10110100000, // 237 - 0b10101001010, // 238 - 0b00100111111, // 239 - 0b01111001110, // 240 - 0b11110111011, // 241 - 0b11101010001, // 242 - 0b01100100100, // 243 - 0b11010000101, // 244 - 0b01011110000, // 245 - 0b01000011010, // 246 - 0b11001101111, // 247 - 0b10100101101, // 248 - 0b00101011000, // 249 - 0b00110110010, // 250 - 0b10111000111, // 251 - 0b00001100110, // 252 - 0b10000010011, // 253 - 0b10011111001, // 254 - 0b00010001100, // 255 - 0b10111100000, // 256 - 0b00110010101, // 257 - 0b00101111111, // 258 - 0b10100001010, // 259 - 0b00010101011, // 260 - 0b10011011110, // 261 - 0b10000110100, // 262 - 0b00001000001, // 263 - 0b01100000011, // 264 - 0b11101110110, // 265 - 0b11110011100, // 266 - 0b01111101001, // 267 - 0b11001001000, // 268 - 0b01000111101, // 269 - 0b01011010111, // 270 - 0b11010100010, // 271 - 0b10001010011, // 272 - 0b00000100110, // 273 - 0b00011001100, // 274 - 0b10010111001, // 275 - 0b00100011000, // 276 - 0b10101101101, // 277 - 0b10110000111, // 278 - 0b00111110010, // 279 - 0b01010110000, // 280 - 0b11011000101, // 281 - 0b11000101111, // 282 - 0b01001011010, // 283 - 0b11111111011, // 284 - 0b01110001110, // 285 - 0b01101100100, // 286 - 0b11100010001, // 287 - 0b11010000110, // 288 - 0b01011110011, // 289 - 0b01000011001, // 290 - 0b11001101100, // 291 - 0b01111001101, // 292 - 0b11110111000, // 293 - 0b11101010010, // 294 - 0b01100100111, // 295 - 0b00001100101, // 296 - 0b10000010000, // 297 - 0b10011111010, // 298 - 0b00010001111, // 299 - 0b10100101110, // 300 - 0b00101011011, // 301 - 0b00110110001, // 302 - 0b10111000100, // 303 - 0b11100110101, // 304 - 0b01101000000, // 305 - 0b01110101010, // 306 - 0b11111011111, // 307 - 0b01001111110, // 308 - 0b11000001011, // 309 - 0b11011100001, // 310 - 0b01010010100, // 311 - 0b00111010110, // 312 - 0b10110100011, // 313 - 0b10101001001, // 314 - 0b00100111100, // 315 - 0b10010011101, // 316 - 0b00011101000, // 317 - 0b00000000010, // 318 - 0b10001110111, // 319 - 0b01100101100, // 320 - 0b11101011001, // 321 - 0b11110110011, // 322 - 0b01111000110, // 323 - 0b11001100111, // 324 - 0b01000010010, // 325 - 0b01011111000, // 326 - 0b11010001101, // 327 - 0b10111001111, // 328 - 0b00110111010, // 329 - 0b00101010000, // 330 - 0b10100100101, // 331 - 0b00010000100, // 332 - 0b10011110001, // 333 - 0b10000011011, // 334 - 0b00001101110, // 335 - 0b01010011111, // 336 - 0b11011101010, // 337 - 0b11000000000, // 338 - 0b01001110101, // 339 - 0b11111010100, // 340 - 0b01110100001, // 341 - 0b01101001011, // 342 - 0b11100111110, // 343 - 0b10001111100, // 344 - 0b00000001001, // 345 - 0b00011100011, // 346 - 0b10010010110, // 347 - 0b00100110111, // 348 - 0b10101000010, // 349 - 0b10110101000, // 350 - 0b00111011101, // 351 - 0b00001001010, // 352 - 0b10000111111, // 353 - 0b10011010101, // 354 - 0b00010100000, // 355 - 0b10100000001, // 356 - 0b00101110100, // 357 - 0b00110011110, // 358 - 0b10111101011, // 359 - 0b11010101001, // 360 - 0b01011011100, // 361 - 0b01000110110, // 362 - 0b11001000011, // 363 - 0b01111100010, // 364 - 0b11110010111, // 365 - 0b11101111101, // 366 - 0b01100001000, // 367 - 0b00111111001, // 368 - 0b10110001100, // 369 - 0b10101100110, // 370 - 0b00100010011, // 371 - 0b10010110010, // 372 - 0b00011000111, // 373 - 0b00000101101, // 374 - 0b10001011000, // 375 - 0b11100011010, // 376 - 0b01101101111, // 377 - 0b01110000101, // 378 - 0b11111110000, // 379 - 0b01001010001, // 380 - 0b11000100100, // 381 - 0b11011001110, // 382 - 0b01010111011, // 383 - 0b10000001101, // 384 - 0b00001111000, // 385 - 0b00010010010, // 386 - 0b10011100111, // 387 - 0b00101000110, // 388 - 0b10100110011, // 389 - 0b10111011001, // 390 - 0b00110101100, // 391 - 0b01011101110, // 392 - 0b11010011011, // 393 - 0b11001110001, // 394 - 0b01000000100, // 395 - 0b11110100101, // 396 - 0b01111010000, // 397 - 0b01100111010, // 398 - 0b11101001111, // 399 - 0b10110111110, // 400 - 0b00111001011, // 401 - 0b00100100001, // 402 - 0b10101010100, // 403 - 0b00011110101, // 404 - 0b10010000000, // 405 - 0b10001101010, // 406 - 0b00000011111, // 407 - 0b01101011101, // 408 - 0b11100101000, // 409 - 0b11111000010, // 410 - 0b01110110111, // 411 - 0b11000010110, // 412 - 0b01001100011, // 413 - 0b01010001001, // 414 - 0b11011111100, // 415 - 0b11101101011, // 416 - 0b01100011110, // 417 - 0b01111110100, // 418 - 0b11110000001, // 419 - 0b01000100000, // 420 - 0b11001010101, // 421 - 0b11010111111, // 422 - 0b01011001010, // 423 - 0b00110001000, // 424 - 0b10111111101, // 425 - 0b10100010111, // 426 - 0b00101100010, // 427 - 0b10011000011, // 428 - 0b00010110110, // 429 - 0b00001011100, // 430 - 0b10000101001, // 431 - 0b11011011000, // 432 - 0b01010101101, // 433 - 0b01001000111, // 434 - 0b11000110010, // 435 - 0b01110010011, // 436 - 0b11111100110, // 437 - 0b11100001100, // 438 - 0b01101111001, // 439 - 0b00000111011, // 440 - 0b10001001110, // 441 - 0b10010100100, // 442 - 0b00011010001, // 443 - 0b10101110000, // 444 - 0b00100000101, // 445 - 0b00111101111, // 446 - 0b10110011010, // 447 - 0b01011000001, // 448 - 0b11010110100, // 449 - 0b11001011110, // 450 - 0b01000101011, // 451 - 0b11110001010, // 452 - 0b01111111111, // 453 - 0b01100010101, // 454 - 0b11101100000, // 455 - 0b10000100010, // 456 - 0b00001010111, // 457 - 0b00010111101, // 458 - 0b10011001000, // 459 - 0b00101101001, // 460 - 0b10100011100, // 461 - 0b10111110110, // 462 - 0b00110000011, // 463 - 0b01101110010, // 464 - 0b11100000111, // 465 - 0b11111101101, // 466 - 0b01110011000, // 467 - 0b11000111001, // 468 - 0b01001001100, // 469 - 0b01010100110, // 470 - 0b11011010011, // 471 - 0b10110010001, // 472 - 0b00111100100, // 473 - 0b00100001110, // 474 - 0b10101111011, // 475 - 0b00011011010, // 476 - 0b10010101111, // 477 - 0b10001000101, // 478 - 0b00000110000, // 479 - 0b00110100111, // 480 - 0b10111010010, // 481 - 0b10100111000, // 482 - 0b00101001101, // 483 - 0b10011101100, // 484 - 0b00010011001, // 485 - 0b00001110011, // 486 - 0b10000000110, // 487 - 0b11101000100, // 488 - 0b01100110001, // 489 - 0b01111011011, // 490 - 0b11110101110, // 491 - 0b01000001111, // 492 - 0b11001111010, // 493 - 0b11010010000, // 494 - 0b01011100101, // 495 - 0b00000010100, // 496 - 0b10001100001, // 497 - 0b10010001011, // 498 - 0b00011111110, // 499 - 0b10101011111, // 500 - 0b00100101010, // 501 - 0b00111000000, // 502 - 0b10110110101, // 503 - 0b11011110111, // 504 - 0b01010000010, // 505 - 0b01001101000, // 506 - 0b11000011101, // 507 - 0b01110111100, // 508 - 0b11111001001, // 509 - 0b11100100011, // 510 - 0b01101010110 // 511 + 0b11000111010, // 0 + 0b01001001111, // 1 + 0b01010100101, // 2 + 0b11011010000, // 3 + 0b01101110001, // 4 + 0b11100000100, // 5 + 0b11111101110, // 6 + 0b01110011011, // 7 + 0b00011011001, // 8 + 0b10010101100, // 9 + 0b10001000110, // 10 + 0b00000110011, // 11 + 0b10110010010, // 12 + 0b00111100111, // 13 + 0b00100001101, // 14 + 0b10101111000, // 15 + 0b11110001001, // 16 + 0b01111111100, // 17 + 0b01100010110, // 18 + 0b11101100011, // 19 + 0b01011000010, // 20 + 0b11010110111, // 21 + 0b11001011101, // 22 + 0b01000101000, // 23 + 0b00101101010, // 24 + 0b10100011111, // 25 + 0b10111110101, // 26 + 0b00110000000, // 27 + 0b10000100001, // 28 + 0b00001010100, // 29 + 0b00010111110, // 30 + 0b10011001011, // 31 + 0b10101011100, // 32 + 0b00100101001, // 33 + 0b00111000011, // 34 + 0b10110110110, // 35 + 0b00000010111, // 36 + 0b10001100010, // 37 + 0b10010001000, // 38 + 0b00011111101, // 39 + 0b01110111111, // 40 + 0b11111001010, // 41 + 0b11100100000, // 42 + 0b01101010101, // 43 + 0b11011110100, // 44 + 0b01010000001, // 45 + 0b01001101011, // 46 + 0b11000011110, // 47 + 0b10011101111, // 48 + 0b00010011010, // 49 + 0b00001110000, // 50 + 0b10000000101, // 51 + 0b00110100100, // 52 + 0b10111010001, // 53 + 0b10100111011, // 54 + 0b00101001110, // 55 + 0b01000001100, // 56 + 0b11001111001, // 57 + 0b11010010011, // 58 + 0b01011100110, // 59 + 0b11101000111, // 60 + 0b01100110010, // 61 + 0b01111011000, // 62 + 0b11110101101, // 63 + 0b00011110110, // 64 + 0b10010000011, // 65 + 0b10001101001, // 66 + 0b00000011100, // 67 + 0b10110111101, // 68 + 0b00111001000, // 69 + 0b00100100010, // 70 + 0b10101010111, // 71 + 0b11000010101, // 72 + 0b01001100000, // 73 + 0b01010001010, // 74 + 0b11011111111, // 75 + 0b01101011110, // 76 + 0b11100101011, // 77 + 0b11111000001, // 78 + 0b01110110100, // 79 + 0b00101000101, // 80 + 0b10100110000, // 81 + 0b10111011010, // 82 + 0b00110101111, // 83 + 0b10000001110, // 84 + 0b00001111011, // 85 + 0b00010010001, // 86 + 0b10011100100, // 87 + 0b11110100110, // 88 + 0b01111010011, // 89 + 0b01100111001, // 90 + 0b11101001100, // 91 + 0b01011101101, // 92 + 0b11010011000, // 93 + 0b11001110010, // 94 + 0b01000000111, // 95 + 0b01110010000, // 96 + 0b11111100101, // 97 + 0b11100001111, // 98 + 0b01101111010, // 99 + 0b11011011011, // 100 + 0b01010101110, // 101 + 0b01001000100, // 102 + 0b11000110001, // 103 + 0b10101110011, // 104 + 0b00100000110, // 105 + 0b00111101100, // 106 + 0b10110011001, // 107 + 0b00000111000, // 108 + 0b10001001101, // 109 + 0b10010100111, // 110 + 0b00011010010, // 111 + 0b01000100011, // 112 + 0b11001010110, // 113 + 0b11010111100, // 114 + 0b01011001001, // 115 + 0b11101101000, // 116 + 0b01100011101, // 117 + 0b01111110111, // 118 + 0b11110000010, // 119 + 0b10011000000, // 120 + 0b00010110101, // 121 + 0b00001011111, // 122 + 0b10000101010, // 123 + 0b00110001011, // 124 + 0b10111111110, // 125 + 0b10100010100, // 126 + 0b00101100001, // 127 + 0b11111010111, // 128 + 0b01110100010, // 129 + 0b01101001000, // 130 + 0b11100111101, // 131 + 0b01010011100, // 132 + 0b11011101001, // 133 + 0b11000000011, // 134 + 0b01001110110, // 135 + 0b00100110100, // 136 + 0b10101000001, // 137 + 0b10110101011, // 138 + 0b00111011110, // 139 + 0b10001111111, // 140 + 0b00000001010, // 141 + 0b00011100000, // 142 + 0b10010010101, // 143 + 0b11001100100, // 144 + 0b01000010001, // 145 + 0b01011111011, // 146 + 0b11010001110, // 147 + 0b01100101111, // 148 + 0b11101011010, // 149 + 0b11110110000, // 150 + 0b01111000101, // 151 + 0b00010000111, // 152 + 0b10011110010, // 153 + 0b10000011000, // 154 + 0b00001101101, // 155 + 0b10111001100, // 156 + 0b00110111001, // 157 + 0b00101010011, // 158 + 0b10100100110, // 159 + 0b10010110001, // 160 + 0b00011000100, // 161 + 0b00000101110, // 162 + 0b10001011011, // 163 + 0b00111111010, // 164 + 0b10110001111, // 165 + 0b10101100101, // 166 + 0b00100010000, // 167 + 0b01001010010, // 168 + 0b11000100111, // 169 + 0b11011001101, // 170 + 0b01010111000, // 171 + 0b11100011001, // 172 + 0b01101101100, // 173 + 0b01110000110, // 174 + 0b11111110011, // 175 + 0b10100000010, // 176 + 0b00101110111, // 177 + 0b00110011101, // 178 + 0b10111101000, // 179 + 0b00001001001, // 180 + 0b10000111100, // 181 + 0b10011010110, // 182 + 0b00010100011, // 183 + 0b01111100001, // 184 + 0b11110010100, // 185 + 0b11101111110, // 186 + 0b01100001011, // 187 + 0b11010101010, // 188 + 0b01011011111, // 189 + 0b01000110101, // 190 + 0b11001000000, // 191 + 0b00100011011, // 192 + 0b10101101110, // 193 + 0b10110000100, // 194 + 0b00111110001, // 195 + 0b10001010000, // 196 + 0b00000100101, // 197 + 0b00011001111, // 198 + 0b10010111010, // 199 + 0b11111111000, // 200 + 0b01110001101, // 201 + 0b01101100111, // 202 + 0b11100010010, // 203 + 0b01010110011, // 204 + 0b11011000110, // 205 + 0b11000101100, // 206 + 0b01001011001, // 207 + 0b00010101000, // 208 + 0b10011011101, // 209 + 0b10000110111, // 210 + 0b00001000010, // 211 + 0b10111100011, // 212 + 0b00110010110, // 213 + 0b00101111100, // 214 + 0b10100001001, // 215 + 0b11001001011, // 216 + 0b01000111110, // 217 + 0b01011010100, // 218 + 0b11010100001, // 219 + 0b01100000000, // 220 + 0b11101110101, // 221 + 0b11110011111, // 222 + 0b01111101010, // 223 + 0b01001111101, // 224 + 0b11000001000, // 225 + 0b11011100010, // 226 + 0b01010010111, // 227 + 0b11100110110, // 228 + 0b01101000011, // 229 + 0b01110101001, // 230 + 0b11111011100, // 231 + 0b10010011110, // 232 + 0b00011101011, // 233 + 0b00000000001, // 234 + 0b10001110100, // 235 + 0b00111010101, // 236 + 0b10110100000, // 237 + 0b10101001010, // 238 + 0b00100111111, // 239 + 0b01111001110, // 240 + 0b11110111011, // 241 + 0b11101010001, // 242 + 0b01100100100, // 243 + 0b11010000101, // 244 + 0b01011110000, // 245 + 0b01000011010, // 246 + 0b11001101111, // 247 + 0b10100101101, // 248 + 0b00101011000, // 249 + 0b00110110010, // 250 + 0b10111000111, // 251 + 0b00001100110, // 252 + 0b10000010011, // 253 + 0b10011111001, // 254 + 0b00010001100, // 255 + 0b10111100000, // 256 + 0b00110010101, // 257 + 0b00101111111, // 258 + 0b10100001010, // 259 + 0b00010101011, // 260 + 0b10011011110, // 261 + 0b10000110100, // 262 + 0b00001000001, // 263 + 0b01100000011, // 264 + 0b11101110110, // 265 + 0b11110011100, // 266 + 0b01111101001, // 267 + 0b11001001000, // 268 + 0b01000111101, // 269 + 0b01011010111, // 270 + 0b11010100010, // 271 + 0b10001010011, // 272 + 0b00000100110, // 273 + 0b00011001100, // 274 + 0b10010111001, // 275 + 0b00100011000, // 276 + 0b10101101101, // 277 + 0b10110000111, // 278 + 0b00111110010, // 279 + 0b01010110000, // 280 + 0b11011000101, // 281 + 0b11000101111, // 282 + 0b01001011010, // 283 + 0b11111111011, // 284 + 0b01110001110, // 285 + 0b01101100100, // 286 + 0b11100010001, // 287 + 0b11010000110, // 288 + 0b01011110011, // 289 + 0b01000011001, // 290 + 0b11001101100, // 291 + 0b01111001101, // 292 + 0b11110111000, // 293 + 0b11101010010, // 294 + 0b01100100111, // 295 + 0b00001100101, // 296 + 0b10000010000, // 297 + 0b10011111010, // 298 + 0b00010001111, // 299 + 0b10100101110, // 300 + 0b00101011011, // 301 + 0b00110110001, // 302 + 0b10111000100, // 303 + 0b11100110101, // 304 + 0b01101000000, // 305 + 0b01110101010, // 306 + 0b11111011111, // 307 + 0b01001111110, // 308 + 0b11000001011, // 309 + 0b11011100001, // 310 + 0b01010010100, // 311 + 0b00111010110, // 312 + 0b10110100011, // 313 + 0b10101001001, // 314 + 0b00100111100, // 315 + 0b10010011101, // 316 + 0b00011101000, // 317 + 0b00000000010, // 318 + 0b10001110111, // 319 + 0b01100101100, // 320 + 0b11101011001, // 321 + 0b11110110011, // 322 + 0b01111000110, // 323 + 0b11001100111, // 324 + 0b01000010010, // 325 + 0b01011111000, // 326 + 0b11010001101, // 327 + 0b10111001111, // 328 + 0b00110111010, // 329 + 0b00101010000, // 330 + 0b10100100101, // 331 + 0b00010000100, // 332 + 0b10011110001, // 333 + 0b10000011011, // 334 + 0b00001101110, // 335 + 0b01010011111, // 336 + 0b11011101010, // 337 + 0b11000000000, // 338 + 0b01001110101, // 339 + 0b11111010100, // 340 + 0b01110100001, // 341 + 0b01101001011, // 342 + 0b11100111110, // 343 + 0b10001111100, // 344 + 0b00000001001, // 345 + 0b00011100011, // 346 + 0b10010010110, // 347 + 0b00100110111, // 348 + 0b10101000010, // 349 + 0b10110101000, // 350 + 0b00111011101, // 351 + 0b00001001010, // 352 + 0b10000111111, // 353 + 0b10011010101, // 354 + 0b00010100000, // 355 + 0b10100000001, // 356 + 0b00101110100, // 357 + 0b00110011110, // 358 + 0b10111101011, // 359 + 0b11010101001, // 360 + 0b01011011100, // 361 + 0b01000110110, // 362 + 0b11001000011, // 363 + 0b01111100010, // 364 + 0b11110010111, // 365 + 0b11101111101, // 366 + 0b01100001000, // 367 + 0b00111111001, // 368 + 0b10110001100, // 369 + 0b10101100110, // 370 + 0b00100010011, // 371 + 0b10010110010, // 372 + 0b00011000111, // 373 + 0b00000101101, // 374 + 0b10001011000, // 375 + 0b11100011010, // 376 + 0b01101101111, // 377 + 0b01110000101, // 378 + 0b11111110000, // 379 + 0b01001010001, // 380 + 0b11000100100, // 381 + 0b11011001110, // 382 + 0b01010111011, // 383 + 0b10000001101, // 384 + 0b00001111000, // 385 + 0b00010010010, // 386 + 0b10011100111, // 387 + 0b00101000110, // 388 + 0b10100110011, // 389 + 0b10111011001, // 390 + 0b00110101100, // 391 + 0b01011101110, // 392 + 0b11010011011, // 393 + 0b11001110001, // 394 + 0b01000000100, // 395 + 0b11110100101, // 396 + 0b01111010000, // 397 + 0b01100111010, // 398 + 0b11101001111, // 399 + 0b10110111110, // 400 + 0b00111001011, // 401 + 0b00100100001, // 402 + 0b10101010100, // 403 + 0b00011110101, // 404 + 0b10010000000, // 405 + 0b10001101010, // 406 + 0b00000011111, // 407 + 0b01101011101, // 408 + 0b11100101000, // 409 + 0b11111000010, // 410 + 0b01110110111, // 411 + 0b11000010110, // 412 + 0b01001100011, // 413 + 0b01010001001, // 414 + 0b11011111100, // 415 + 0b11101101011, // 416 + 0b01100011110, // 417 + 0b01111110100, // 418 + 0b11110000001, // 419 + 0b01000100000, // 420 + 0b11001010101, // 421 + 0b11010111111, // 422 + 0b01011001010, // 423 + 0b00110001000, // 424 + 0b10111111101, // 425 + 0b10100010111, // 426 + 0b00101100010, // 427 + 0b10011000011, // 428 + 0b00010110110, // 429 + 0b00001011100, // 430 + 0b10000101001, // 431 + 0b11011011000, // 432 + 0b01010101101, // 433 + 0b01001000111, // 434 + 0b11000110010, // 435 + 0b01110010011, // 436 + 0b11111100110, // 437 + 0b11100001100, // 438 + 0b01101111001, // 439 + 0b00000111011, // 440 + 0b10001001110, // 441 + 0b10010100100, // 442 + 0b00011010001, // 443 + 0b10101110000, // 444 + 0b00100000101, // 445 + 0b00111101111, // 446 + 0b10110011010, // 447 + 0b01011000001, // 448 + 0b11010110100, // 449 + 0b11001011110, // 450 + 0b01000101011, // 451 + 0b11110001010, // 452 + 0b01111111111, // 453 + 0b01100010101, // 454 + 0b11101100000, // 455 + 0b10000100010, // 456 + 0b00001010111, // 457 + 0b00010111101, // 458 + 0b10011001000, // 459 + 0b00101101001, // 460 + 0b10100011100, // 461 + 0b10111110110, // 462 + 0b00110000011, // 463 + 0b01101110010, // 464 + 0b11100000111, // 465 + 0b11111101101, // 466 + 0b01110011000, // 467 + 0b11000111001, // 468 + 0b01001001100, // 469 + 0b01010100110, // 470 + 0b11011010011, // 471 + 0b10110010001, // 472 + 0b00111100100, // 473 + 0b00100001110, // 474 + 0b10101111011, // 475 + 0b00011011010, // 476 + 0b10010101111, // 477 + 0b10001000101, // 478 + 0b00000110000, // 479 + 0b00110100111, // 480 + 0b10111010010, // 481 + 0b10100111000, // 482 + 0b00101001101, // 483 + 0b10011101100, // 484 + 0b00010011001, // 485 + 0b00001110011, // 486 + 0b10000000110, // 487 + 0b11101000100, // 488 + 0b01100110001, // 489 + 0b01111011011, // 490 + 0b11110101110, // 491 + 0b01000001111, // 492 + 0b11001111010, // 493 + 0b11010010000, // 494 + 0b01011100101, // 495 + 0b00000010100, // 496 + 0b10001100001, // 497 + 0b10010001011, // 498 + 0b00011111110, // 499 + 0b10101011111, // 500 + 0b00100101010, // 501 + 0b00111000000, // 502 + 0b10110110101, // 503 + 0b11011110111, // 504 + 0b01010000010, // 505 + 0b01001101000, // 506 + 0b11000011101, // 507 + 0b01110111100, // 508 + 0b11111001001, // 509 + 0b11100100011, // 510 + 0b01101010110 // 511 }; uint32_t dcs_word(uint32_t code) { - code &= 511; - return (dcs_parity[code] << 12) | (0b100 << 9) | code; + code &= 511; + return (dcs_parity[code] << 12) | (0b100 << 9) | code; } -} +} // namespace dcs diff --git a/firmware/application/protocols/dcs.hpp b/firmware/application/protocols/dcs.hpp index c6556c438..3b9be4eed 100644 --- a/firmware/application/protocols/dcs.hpp +++ b/firmware/application/protocols/dcs.hpp @@ -33,4 +33,4 @@ uint32_t dcs_word(uint32_t code); } -#endif/*__DCS_H_*/ +#endif /*__DCS_H_*/ diff --git a/firmware/application/protocols/encoders.cpp b/firmware/application/protocols/encoders.cpp index ad025f1d7..9aa951942 100644 --- a/firmware/application/protocols/encoders.cpp +++ b/firmware/application/protocols/encoders.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -29,59 +29,59 @@ using namespace portapack; namespace encoders { size_t make_bitstream(std::string& fragments) { - uint8_t byte = 0; - size_t bitstream_length = 0; - uint8_t * bitstream = shared_memory.bb_data.data; - - for (auto c : fragments) { - byte <<= 1; - if (c != '0') - byte |= 1; - - if ((bitstream_length & 7) == 7) - bitstream[bitstream_length >> 3] = byte; - - bitstream_length++; - } - - // Finish last byte if needed - size_t padding = 8 - (bitstream_length & 7); - if (padding != 8) { - byte <<= padding; - bitstream[(bitstream_length + padding - 1) >> 3] = byte; - padding++; - } - - return bitstream_length; + uint8_t byte = 0; + size_t bitstream_length = 0; + uint8_t* bitstream = shared_memory.bb_data.data; + + for (auto c : fragments) { + byte <<= 1; + if (c != '0') + byte |= 1; + + if ((bitstream_length & 7) == 7) + bitstream[bitstream_length >> 3] = byte; + + bitstream_length++; + } + + // Finish last byte if needed + size_t padding = 8 - (bitstream_length & 7); + if (padding != 8) { + byte <<= padding; + bitstream[(bitstream_length + padding - 1) >> 3] = byte; + padding++; + } + + return bitstream_length; } void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits) { - uint8_t * bitstream = shared_memory.bb_data.data; - uint32_t bit_mask = 1 << (bit_count - 1); - uint32_t bit_index; - uint8_t byte = 0; - - if (bitstream_length & 7) - byte = bitstream[bitstream_length >> 3]; - - bit_index = 7 - (bitstream_length & 7); - - for (size_t i = 0; i < bit_count; i++) { - if (bits & bit_mask) - byte |= (1 << bit_index); - - if (!bit_index) { - bitstream[bitstream_length >> 3] = byte; - byte = 0; - } - - bit_index = (bit_index - 1) & 7; - bits <<= 1; - - bitstream_length++; - } - - bitstream[bitstream_length >> 3] = byte; + uint8_t* bitstream = shared_memory.bb_data.data; + uint32_t bit_mask = 1 << (bit_count - 1); + uint32_t bit_index; + uint8_t byte = 0; + + if (bitstream_length & 7) + byte = bitstream[bitstream_length >> 3]; + + bit_index = 7 - (bitstream_length & 7); + + for (size_t i = 0; i < bit_count; i++) { + if (bits & bit_mask) + byte |= (1 << bit_index); + + if (!bit_index) { + bitstream[bitstream_length >> 3] = byte; + byte = 0; + } + + bit_index = (bit_index - 1) & 7; + bits <<= 1; + + bitstream_length++; + } + + bitstream[bitstream_length >> 3] = byte; } - + } /* namespace encoders */ diff --git a/firmware/application/protocols/encoders.hpp b/firmware/application/protocols/encoders.hpp index c8f11e4f6..130c2517d 100644 --- a/firmware/application/protocols/encoders.hpp +++ b/firmware/application/protocols/encoders.hpp @@ -29,200 +29,253 @@ namespace encoders { - #define ENC_TYPES_COUNT 14 - #define OOK_SAMPLERATE 2280000U - - #define ENCODER_UM3750 8 - - size_t make_bitstream(std::string& fragments); - void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits); - - struct encoder_def_t { - char name[16]; // Encoder chip ref/name - char address_symbols[8]; // List of possible symbols like "01", "01F"... - char data_symbols[8]; // Same - uint16_t clk_per_symbol; // Oscillator periods per symbol - uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state) - char bit_format[4][20]; // List of fragments for each symbol in previous *_symbols list order - uint8_t word_length; // Total # of symbols (not counting sync) - char word_format[32]; // A for Address, D for Data, S for sync - char sync[64]; // Like bit_format - uint32_t default_speed; // Default encoder clk frequency (often set by shitty resistor) - uint8_t repeat_min; // Minimum repeat count - uint16_t pause_symbols; // Length of pause between repeats in symbols - }; - - // Warning ! If this is changed, make sure that ENCODER_UM3750 is still valid ! - constexpr encoder_def_t encoder_defs[ENC_TYPES_COUNT] = { - // PT2260-R2 - { - "2260-R2", - "01F", "01", - 1024, 128, - { "10001000", "11101110", "10001110" }, - 12, "AAAAAAAAAADDS", - "10000000000000000000000000000000", - 150000, 2, - 0 - }, - - // PT2260-R4 - { - "2260-R4", - "01F", "01", - 1024, 128, - { "10001000", "11101110", "10001110" }, - 12, "AAAAAAAADDDDS", - "10000000000000000000000000000000", - 150000, 2, - 0 - }, - - // PT2262 - { - "2262 ", - "01F", "01F", - 32, 4, - { "10001000", "11101110", "10001110" }, - 12, "AAAAAAAAAAAAS", - "10000000000000000000000000000000", - 20000, 4, - 0 - }, - - // 16-bit ? - { - "16-bit ", - "01", "01", - 32, 8, - { "1110", "1000" }, // Opposite ? - 16, "AAAAAAAAAAAAAAAAS", - "100000000000000000000", - 25000, 50, - 0 // ? - }, - - // RT1527 - { - "1527 ", - "01", "01", - 128, 32, - { "1000", "1110" }, - 24, "SAAAAAAAAAAAAAAAAAAAADDDD", - "10000000000000000000000000000000", - 100000, 4, - 0 - }, - - // HK526E - { - "526E ", - "01", "01", - 24, 8, - { "110", "100" }, - 12, "AAAAAAAAAAAA", - "", - 20000, 4, - 10 // ? - }, - - // HT12E - { - "12E ", - "01", "01", - 3, 1, - { "011", "001" }, - 12, "SAAAAAAAADDDD", - "0000000000000000000000000000000000001", - 3000, 4, - 10 // ? - }, - - // VD5026 13 bits ? - { - "5026 ", - "0123", "0123", - 128, 8, - { "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" }, - 12, "SAAAAAAAAAAAA", - "000000000000000000000000000000000000000000000001", // ? - 100000, 4, - 10 // ? - }, - - // UM3750 - { - "UM3750 ", - "01", "01", - 96, 32, - { "011", "001" }, - 12, "SAAAAAAAAAAAA", - "001", - 100000, 4, - (3 * 12) - 6 // Compensates for pause delay bug in proc_ook - }, - - // UM3758 - { - "UM3758 ", - "01F", "01", - 96, 16, - { "011011", "001001", "011001" }, - 18, "SAAAAAAAAAADDDDDDDD", - "1", - 160000, 4, - 10 // ? - }, - - // BA5104 - { - "BA5104 ", - "01", "01", - 3072, 768, - { "1000", "1110" }, - 9, "SDDAAAAAAA", - "", - 455000, 4, - 10 // ? - }, - - // MC145026 - { - "145026 ", - "01F", "01", - 16, 1, - { "0111111101111111", "0100000001000000", "0111111101000000" }, - 9, "SAAAAADDDD", - "000000000000000000", - 455000, 2, - 2 - }, - - // HT6*** TODO: Add individual variations - { - "HT6*** ", - "01F", "01", - 198, 33, - { "011011", "001001", "001011" }, - 18, "SAAAAAAAAAAAADDDDDD", - "0000000000000000000000000000000000001011001011001", - 80000, 3, - 10 // ? - }, - - // TC9148 - { - "TC9148 ", - "01", "01", - 48, 12, - { "1000", "1110", }, - 12, "AAAAAAAAAAAA", - "", - 455000, 3, - 10 // ? - } - }; +#define ENC_TYPES_COUNT 14 +#define OOK_SAMPLERATE 2280000U + +#define ENCODER_UM3750 8 + +size_t make_bitstream(std::string& fragments); +void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits); + +struct encoder_def_t { + char name[16]; // Encoder chip ref/name + char address_symbols[8]; // List of possible symbols like "01", "01F"... + char data_symbols[8]; // Same + uint16_t clk_per_symbol; // Oscillator periods per symbol + uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state) + char bit_format[4][20]; // List of fragments for each symbol in previous *_symbols list order + uint8_t word_length; // Total # of symbols (not counting sync) + char word_format[32]; // A for Address, D for Data, S for sync + char sync[64]; // Like bit_format + uint32_t default_speed; // Default encoder clk frequency (often set by shitty resistor) + uint8_t repeat_min; // Minimum repeat count + uint16_t pause_symbols; // Length of pause between repeats in symbols +}; + +// Warning ! If this is changed, make sure that ENCODER_UM3750 is still valid ! +constexpr encoder_def_t encoder_defs[ENC_TYPES_COUNT] = { + // PT2260-R2 + { + "2260-R2", + "01F", + "01", + 1024, + 128, + {"10001000", "11101110", "10001110"}, + 12, + "AAAAAAAAAADDS", + "10000000000000000000000000000000", + 150000, + 2, + 0}, + + // PT2260-R4 + { + "2260-R4", + "01F", + "01", + 1024, + 128, + {"10001000", "11101110", "10001110"}, + 12, + "AAAAAAAADDDDS", + "10000000000000000000000000000000", + 150000, + 2, + 0}, + + // PT2262 + { + "2262 ", + "01F", + "01F", + 32, + 4, + {"10001000", "11101110", "10001110"}, + 12, + "AAAAAAAAAAAAS", + "10000000000000000000000000000000", + 20000, + 4, + 0}, + + // 16-bit ? + { + "16-bit ", + "01", + "01", + 32, + 8, + {"1110", "1000"}, // Opposite ? + 16, + "AAAAAAAAAAAAAAAAS", + "100000000000000000000", + 25000, + 50, + 0 // ? + }, + + // RT1527 + { + "1527 ", + "01", + "01", + 128, + 32, + {"1000", "1110"}, + 24, + "SAAAAAAAAAAAAAAAAAAAADDDD", + "10000000000000000000000000000000", + 100000, + 4, + 0}, + + // HK526E + { + "526E ", + "01", + "01", + 24, + 8, + {"110", "100"}, + 12, + "AAAAAAAAAAAA", + "", + 20000, + 4, + 10 // ? + }, + + // HT12E + { + "12E ", + "01", + "01", + 3, + 1, + {"011", "001"}, + 12, + "SAAAAAAAADDDD", + "0000000000000000000000000000000000001", + 3000, + 4, + 10 // ? + }, + + // VD5026 13 bits ? + { + "5026 ", + "0123", + "0123", + 128, + 8, + {"1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110"}, + 12, + "SAAAAAAAAAAAA", + "000000000000000000000000000000000000000000000001", // ? + 100000, + 4, + 10 // ? + }, + + // UM3750 + { + "UM3750 ", + "01", + "01", + 96, + 32, + {"011", "001"}, + 12, + "SAAAAAAAAAAAA", + "001", + 100000, + 4, + (3 * 12) - 6 // Compensates for pause delay bug in proc_ook + }, + + // UM3758 + { + "UM3758 ", + "01F", + "01", + 96, + 16, + {"011011", "001001", "011001"}, + 18, + "SAAAAAAAAAADDDDDDDD", + "1", + 160000, + 4, + 10 // ? + }, + + // BA5104 + { + "BA5104 ", + "01", + "01", + 3072, + 768, + {"1000", "1110"}, + 9, + "SDDAAAAAAA", + "", + 455000, + 4, + 10 // ? + }, + + // MC145026 + { + "145026 ", + "01F", + "01", + 16, + 1, + {"0111111101111111", "0100000001000000", "0111111101000000"}, + 9, + "SAAAAADDDD", + "000000000000000000", + 455000, + 2, + 2}, + + // HT6*** TODO: Add individual variations + { + "HT6*** ", + "01F", + "01", + 198, + 33, + {"011011", "001001", "001011"}, + 18, + "SAAAAAAAAAAAADDDDDD", + "0000000000000000000000000000000000001011001011001", + 80000, + 3, + 10 // ? + }, + + // TC9148 + { + "TC9148 ", + "01", + "01", + 48, + 12, + { + "1000", + "1110", + }, + 12, + "AAAAAAAAAAAA", + "", + 455000, + 3, + 10 // ? + }}; } /* namespace encoders */ -#endif/*__ENCODERS_H__*/ +#endif /*__ENCODERS_H__*/ diff --git a/firmware/application/protocols/lcr.cpp b/firmware/application/protocols/lcr.cpp index 4fc886437..f40291e5b 100644 --- a/firmware/application/protocols/lcr.cpp +++ b/firmware/application/protocols/lcr.cpp @@ -26,46 +26,46 @@ namespace lcr { std::string generate_message(std::string rgsb, std::vector litterals, size_t option_ec) { - const std::string ec_lut[4] = { "A", "J", "N", "S" }; // Eclairage (Auto, Jour, Nuit) - char eom[3] = { 3, 0, 0 }; // EOM and space for checksum - uint8_t i; - std::string lcr_message { 127, 127, 127, 127, 127, 127, 127, 5 }; // 5/15 ? Modem sync and SOM - char checksum = 0; - - // Pad litterals to 7 chars (not required ?) - for (auto & litteral : litterals) - while (litteral.length() < 7) - litteral += ' '; - - // Compose LCR message - lcr_message += rgsb; - lcr_message += "PA "; - - i = 1; - for (auto & litteral : litterals) { - lcr_message += "AM="; - lcr_message += to_string_dec_uint(i, 1); - lcr_message += " AF=\""; - lcr_message += litteral; - lcr_message += "\" CL=0 "; - i++; - } - - lcr_message += "EC="; - lcr_message += ec_lut[option_ec]; - lcr_message += " SAB=0"; - - // Checksum - i = 7; // Skip modem sync - while (lcr_message[i]) - checksum ^= lcr_message[i++]; - checksum ^= eom[0]; // EOM char - checksum &= 0x7F; // Trim - eom[1] = checksum; - - lcr_message += eom; - - return lcr_message; + const std::string ec_lut[4] = {"A", "J", "N", "S"}; // Eclairage (Auto, Jour, Nuit) + char eom[3] = {3, 0, 0}; // EOM and space for checksum + uint8_t i; + std::string lcr_message{127, 127, 127, 127, 127, 127, 127, 5}; // 5/15 ? Modem sync and SOM + char checksum = 0; + + // Pad litterals to 7 chars (not required ?) + for (auto& litteral : litterals) + while (litteral.length() < 7) + litteral += ' '; + + // Compose LCR message + lcr_message += rgsb; + lcr_message += "PA "; + + i = 1; + for (auto& litteral : litterals) { + lcr_message += "AM="; + lcr_message += to_string_dec_uint(i, 1); + lcr_message += " AF=\""; + lcr_message += litteral; + lcr_message += "\" CL=0 "; + i++; + } + + lcr_message += "EC="; + lcr_message += ec_lut[option_ec]; + lcr_message += " SAB=0"; + + // Checksum + i = 7; // Skip modem sync + while (lcr_message[i]) + checksum ^= lcr_message[i++]; + checksum ^= eom[0]; // EOM char + checksum &= 0x7F; // Trim + eom[1] = checksum; + + lcr_message += eom; + + return lcr_message; } } /* namespace lcr */ diff --git a/firmware/application/protocols/lcr.hpp b/firmware/application/protocols/lcr.hpp index e8aaa6cb8..82beee2b2 100644 --- a/firmware/application/protocols/lcr.hpp +++ b/firmware/application/protocols/lcr.hpp @@ -34,4 +34,4 @@ std::string generate_message(std::string rgsb, std::vector litteral } /* namespace lcr */ -#endif/*__LCR_H__*/ +#endif /*__LCR_H__*/ diff --git a/firmware/application/protocols/modems.cpp b/firmware/application/protocols/modems.cpp index bc8f815f3..c7b88d10d 100644 --- a/firmware/application/protocols/modems.cpp +++ b/firmware/application/protocols/modems.cpp @@ -29,91 +29,91 @@ using namespace portapack; namespace modems { -void generate_data(const std::string& in_message, uint16_t * out_data) { - uint8_t parity_init, parity, bits, bit, cur_byte; - uint16_t ordered_word; - size_t bytes; - - serial_format_t serial_format = persistent_memory::serial_format(); - size_t message_length = in_message.length(); - - if (serial_format.parity == ODD) - parity_init = 1; - else - parity_init = 0; - - uint8_t data_bits = serial_format.data_bits; - - for (bytes = 0; bytes < message_length; bytes++) { - parity = parity_init; - cur_byte = in_message[bytes]; - bit = 0; - - if (serial_format.bit_order == MSB_FIRST) { - ordered_word = cur_byte; - for (bits = 0; bits < data_bits; bits++) { - bit = (cur_byte >> bits) & 1; // Get LSB - parity += bit; - } - } else { - ordered_word = 0; - for (bits = 0; bits < data_bits; bits++) { - bit = (cur_byte >> bits) & 1; // Get LSB - parity += bit; - ordered_word |= (bit << ((data_bits - 1) - bits)); // Set MSB - } - } - - if (serial_format.parity) { - ordered_word <<= 1; - ordered_word |= (parity & 1); - } - - for (bits = 0; bits < serial_format.stop_bits; bits++) { - ordered_word <<= 1; - ordered_word |= 1; - } - - out_data[bytes] = ordered_word; - } - - out_data[bytes] = 0; // End marker +void generate_data(const std::string& in_message, uint16_t* out_data) { + uint8_t parity_init, parity, bits, bit, cur_byte; + uint16_t ordered_word; + size_t bytes; + + serial_format_t serial_format = persistent_memory::serial_format(); + size_t message_length = in_message.length(); + + if (serial_format.parity == ODD) + parity_init = 1; + else + parity_init = 0; + + uint8_t data_bits = serial_format.data_bits; + + for (bytes = 0; bytes < message_length; bytes++) { + parity = parity_init; + cur_byte = in_message[bytes]; + bit = 0; + + if (serial_format.bit_order == MSB_FIRST) { + ordered_word = cur_byte; + for (bits = 0; bits < data_bits; bits++) { + bit = (cur_byte >> bits) & 1; // Get LSB + parity += bit; + } + } else { + ordered_word = 0; + for (bits = 0; bits < data_bits; bits++) { + bit = (cur_byte >> bits) & 1; // Get LSB + parity += bit; + ordered_word |= (bit << ((data_bits - 1) - bits)); // Set MSB + } + } + + if (serial_format.parity) { + ordered_word <<= 1; + ordered_word |= (parity & 1); + } + + for (bits = 0; bits < serial_format.stop_bits; bits++) { + ordered_word <<= 1; + ordered_word |= 1; + } + + out_data[bytes] = ordered_word; + } + + out_data[bytes] = 0; // End marker } // This accepts a word with start and stop bits removed ! uint32_t deframe_word(uint32_t raw_word) { - uint32_t cur_bit, deframed_word { 0 }; - size_t bit; - - serial_format_t serial_format = persistent_memory::serial_format(); - - /*if (serial_format.parity == ODD) - parity = 1; - else - parity = 0;*/ - - size_t data_bits = serial_format.data_bits; - - // Ignore parity for now - if (serial_format.parity) - raw_word >>= 1; - - if (serial_format.bit_order == LSB_FIRST) { - // Reverse data bits - for (bit = 0; bit < data_bits; bit++) { - cur_bit = raw_word & 1; - - deframed_word <<= 1; - deframed_word |= cur_bit; - - //parity += cur_bit; - - raw_word >>= 1; - } - - return deframed_word; - } else - return raw_word; + uint32_t cur_bit, deframed_word{0}; + size_t bit; + + serial_format_t serial_format = persistent_memory::serial_format(); + + /*if (serial_format.parity == ODD) + parity = 1; + else + parity = 0;*/ + + size_t data_bits = serial_format.data_bits; + + // Ignore parity for now + if (serial_format.parity) + raw_word >>= 1; + + if (serial_format.bit_order == LSB_FIRST) { + // Reverse data bits + for (bit = 0; bit < data_bits; bit++) { + cur_bit = raw_word & 1; + + deframed_word <<= 1; + deframed_word |= cur_bit; + + // parity += cur_bit; + + raw_word >>= 1; + } + + return deframed_word; + } else + return raw_word; } } /* namespace modems */ diff --git a/firmware/application/protocols/modems.hpp b/firmware/application/protocols/modems.hpp index 48dea3ee8..5a0ea3ab4 100644 --- a/firmware/application/protocols/modems.hpp +++ b/firmware/application/protocols/modems.hpp @@ -28,38 +28,37 @@ #define __MODEMS_H__ namespace modems { - + #define MODEM_DEF_COUNT 7 #define AFSK_TX_SAMPLERATE 1536000U enum ModemModulation { - AFSK = 0, - FSK, - PSK, - AM // SSB + AFSK = 0, + FSK, + PSK, + AM // SSB }; struct modem_def_t { - char name[16]; - ModemModulation modulation; - uint16_t mark_freq; - uint16_t space_freq; - uint16_t baudrate; + char name[16]; + ModemModulation modulation; + uint16_t mark_freq; + uint16_t space_freq; + uint16_t baudrate; }; constexpr modem_def_t modem_defs[MODEM_DEF_COUNT] = { - { "Bell202", AFSK, 1200, 2200, 1200 }, - { "Bell103", AFSK, 1270, 1070, 300 }, - { "V21", AFSK, 980, 1180, 300 }, - { "V23 M1", AFSK, 1300, 1700, 600 }, - { "V23 M2", AFSK, 1300, 2100, 1200 }, - { "RTTY US", AM, 2295, 2125, 45 }, - { "RTTY EU", AM, 2125, 1955, 45 } -}; + {"Bell202", AFSK, 1200, 2200, 1200}, + {"Bell103", AFSK, 1270, 1070, 300}, + {"V21", AFSK, 980, 1180, 300}, + {"V23 M1", AFSK, 1300, 1700, 600}, + {"V23 M2", AFSK, 1300, 2100, 1200}, + {"RTTY US", AM, 2295, 2125, 45}, + {"RTTY EU", AM, 2125, 1955, 45}}; -void generate_data(const std::string& in_message, uint16_t * out_data); +void generate_data(const std::string& in_message, uint16_t* out_data); uint32_t deframe_word(uint32_t raw_word); } /* namespace modems */ -#endif/*__MODEMS_H__*/ +#endif /*__MODEMS_H__*/ diff --git a/firmware/application/protocols/rds.cpp b/firmware/application/protocols/rds.cpp index 342f82e5b..e6975cc85 100644 --- a/firmware/application/protocols/rds.cpp +++ b/firmware/application/protocols/rds.cpp @@ -35,129 +35,123 @@ namespace rds { uint32_t make_block(uint32_t data, uint16_t offset) { uint16_t CRC = 0; uint8_t bit; - + for (uint8_t i = 0; i < 16; i++) { bit = (((data << i) & 0x8000) >> 15) ^ (CRC >> 9); if (bit) CRC ^= 0b0011011100; CRC = ((CRC << 1) | bit) & 0x3FF; } - + return (data << 10) | (CRC ^ offset); } // Boolean to binary uint8_t b2b(const bool in) { - if (in) - return 1; - else - return 0; + if (in) + return 1; + else + return 0; } // Type 0B groups are like 0A groups but without alternative frequency data -RDSGroup make_0B_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, - const bool MS, const bool DI, const uint8_t C, const std::string chars) { - RDSGroup group; - - group.block[0] = PI_code; - group.block[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3); - group.block[2] = PI_code; - group.block[3] = (chars[0] << 8) | chars[1]; - - return group; +RDSGroup make_0B_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, const bool MS, const bool DI, const uint8_t C, const std::string chars) { + RDSGroup group; + + group.block[0] = PI_code; + group.block[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3); + group.block[2] = PI_code; + group.block[3] = (chars[0] << 8) | chars[1]; + + return group; } // For RadioText, up to 64 chars with 2A, 32 chars with 2B -RDSGroup make_2A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, - const uint8_t segment, const std::string chars) { - RDSGroup group; - - group.block[0] = PI_code; - group.block[1] = (0x2 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(AB) << 4) | (segment & 15); - group.block[2] = (chars[0] << 8) | chars[1]; - group.block[3] = (chars[2] << 8) | chars[3]; - - return group; +RDSGroup make_2A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, const uint8_t segment, const std::string chars) { + RDSGroup group; + + group.block[0] = PI_code; + group.block[1] = (0x2 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(AB) << 4) | (segment & 15); + group.block[2] = (chars[0] << 8) | chars[1]; + group.block[3] = (chars[2] << 8) | chars[3]; + + return group; } // Time and date - usually one message per minute - Month: 1~12 - Day: 1~31 - Hour/Minute: 0~59 - Local offset: -12/+12 from UTC -RDSGroup make_4A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, - const uint16_t year, const uint8_t month, const uint8_t day, - const uint8_t hour, const uint8_t minute, const int8_t local_offset) { - RDSGroup group; - uint32_t L = 0; - uint32_t day_code; - - if ((month == 1) || (month == 2)) L = 1; - - day_code = 14956 + day + (uint32_t)((float)(year - 1900 - L) * 365.25) + uint16_t((float)((month + 1) + L * 12) * 30.6001); - - group.block[0] = PI_code; - group.block[1] = (0x4 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | ((day_code & 0x18000) >> 15); - group.block[2] = ((day_code & 0x7FFF) << 1) | (hour >> 4); - group.block[3] = ((hour & 15) << 12) | ((minute & 0x3F) << 6) | (local_offset & 0x3F); - - return group; +RDSGroup make_4A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const uint16_t year, const uint8_t month, const uint8_t day, const uint8_t hour, const uint8_t minute, const int8_t local_offset) { + RDSGroup group; + uint32_t L = 0; + uint32_t day_code; + + if ((month == 1) || (month == 2)) L = 1; + + day_code = 14956 + day + (uint32_t)((float)(year - 1900 - L) * 365.25) + uint16_t((float)((month + 1) + L * 12) * 30.6001); + + group.block[0] = PI_code; + group.block[1] = (0x4 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | ((day_code & 0x18000) >> 15); + group.block[2] = ((day_code & 0x7FFF) << 1) | (hour >> 4); + group.block[3] = ((hour & 15) << 12) | ((minute & 0x3F) << 6) | (local_offset & 0x3F); + + return group; } -void gen_PSN(std::vector& frame, const std::string& psname, const RDS_flags * rds_flags) { - uint8_t c; - RDSGroup group; - - frame.clear(); - - // 4 groups with 2 PSN characters in each - for (c = 0; c < 4; c++) { - group = make_0B_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, psname.substr(c * 2, 2)); - group.block[0] = make_block(group.block[0], RDS_OFFSET_A); - group.block[1] = make_block(group.block[1], RDS_OFFSET_B); - group.block[2] = make_block(group.block[2], RDS_OFFSET_Cp); // C' ! - group.block[3] = make_block(group.block[3], RDS_OFFSET_D); - frame.emplace_back(group); - } +void gen_PSN(std::vector& frame, const std::string& psname, const RDS_flags* rds_flags) { + uint8_t c; + RDSGroup group; + + frame.clear(); + + // 4 groups with 2 PSN characters in each + for (c = 0; c < 4; c++) { + group = make_0B_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, psname.substr(c * 2, 2)); + group.block[0] = make_block(group.block[0], RDS_OFFSET_A); + group.block[1] = make_block(group.block[1], RDS_OFFSET_B); + group.block[2] = make_block(group.block[2], RDS_OFFSET_Cp); // C' ! + group.block[3] = make_block(group.block[3], RDS_OFFSET_D); + frame.emplace_back(group); + } } -void gen_RadioText(std::vector& frame, const std::string& text, const bool AB, const RDS_flags * rds_flags) { - size_t c; - //RDSGroup * groups_ptr; - std::string radiotext_buffer = text; - size_t rt_length, group_count; - RDSGroup group; - - radiotext_buffer += 0x0D; - rt_length = radiotext_buffer.length(); - rt_length = (rt_length + 3) & 0xFC; - - group_count = rt_length >> 2; // 4 characters per group - - //groups_ptr = (RDSGroup*)chHeapAlloc(0, group_count * sizeof(RDSGroup)); - - frame.clear(); - - for (c = 0; c < group_count; c++) { - group = make_2A_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, AB, c, radiotext_buffer.substr(c * 4, 4)); - group.block[0] = make_block(group.block[0], RDS_OFFSET_A); - group.block[1] = make_block(group.block[1], RDS_OFFSET_B); - group.block[2] = make_block(group.block[2], RDS_OFFSET_C); - group.block[3] = make_block(group.block[3], RDS_OFFSET_D); - frame.emplace_back(group); - } +void gen_RadioText(std::vector& frame, const std::string& text, const bool AB, const RDS_flags* rds_flags) { + size_t c; + // RDSGroup * groups_ptr; + std::string radiotext_buffer = text; + size_t rt_length, group_count; + RDSGroup group; + + radiotext_buffer += 0x0D; + rt_length = radiotext_buffer.length(); + rt_length = (rt_length + 3) & 0xFC; + + group_count = rt_length >> 2; // 4 characters per group + + // groups_ptr = (RDSGroup*)chHeapAlloc(0, group_count * sizeof(RDSGroup)); + + frame.clear(); + + for (c = 0; c < group_count; c++) { + group = make_2A_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, AB, c, radiotext_buffer.substr(c * 4, 4)); + group.block[0] = make_block(group.block[0], RDS_OFFSET_A); + group.block[1] = make_block(group.block[1], RDS_OFFSET_B); + group.block[2] = make_block(group.block[2], RDS_OFFSET_C); + group.block[3] = make_block(group.block[3], RDS_OFFSET_D); + frame.emplace_back(group); + } } -void gen_ClockTime(std::vector& frame, const RDS_flags * rds_flags, - const uint16_t year, const uint8_t month, const uint8_t day, - const uint8_t hour, const uint8_t minute, const int8_t local_offset) { - RDSGroup group; - - group = make_4A_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, year, month, day, hour, minute, local_offset); - - // Generate checkbits for each block - group.block[0] = make_block(group.block[0], RDS_OFFSET_A); - group.block[1] = make_block(group.block[1], RDS_OFFSET_B); - group.block[2] = make_block(group.block[2], RDS_OFFSET_C); - group.block[3] = make_block(group.block[3], RDS_OFFSET_D); - - frame.clear(); - frame.emplace_back(group); +void gen_ClockTime(std::vector& frame, const RDS_flags* rds_flags, const uint16_t year, const uint8_t month, const uint8_t day, const uint8_t hour, const uint8_t minute, const int8_t local_offset) { + RDSGroup group; + + group = make_4A_group(rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, year, month, day, hour, minute, local_offset); + + // Generate checkbits for each block + group.block[0] = make_block(group.block[0], RDS_OFFSET_A); + group.block[1] = make_block(group.block[1], RDS_OFFSET_B); + group.block[2] = make_block(group.block[2], RDS_OFFSET_C); + group.block[3] = make_block(group.block[3], RDS_OFFSET_D); + + frame.clear(); + frame.emplace_back(group); } } /* namespace rds */ diff --git a/firmware/application/protocols/rds.hpp b/firmware/application/protocols/rds.hpp index f7657767d..1deca2bd4 100644 --- a/firmware/application/protocols/rds.hpp +++ b/firmware/application/protocols/rds.hpp @@ -28,43 +28,37 @@ #define __RDS_H__ namespace rds { - -#define RDS_OFFSET_A 0b0011111100 -#define RDS_OFFSET_B 0b0110011000 -#define RDS_OFFSET_C 0b0101101000 -#define RDS_OFFSET_Cp 0b1101010000 -#define RDS_OFFSET_D 0b0110110100 + +#define RDS_OFFSET_A 0b0011111100 +#define RDS_OFFSET_B 0b0110011000 +#define RDS_OFFSET_C 0b0101101000 +#define RDS_OFFSET_Cp 0b1101010000 +#define RDS_OFFSET_D 0b0110110100 struct RDS_flags { - uint16_t PI_code; - uint8_t PTY; - uint8_t DI; - bool TP; - bool TA; - bool MS; + uint16_t PI_code; + uint8_t PTY; + uint8_t DI; + bool TP; + bool TA; + bool MS; }; struct RDSGroup { - uint32_t block[4]; + uint32_t block[4]; }; uint32_t make_block(uint32_t blockdata, uint16_t offset); uint8_t b2b(const bool in); -RDSGroup make_0B_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, - const bool MS, const bool DI, const uint8_t C, const std::string chars); -RDSGroup make_2A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, - const uint8_t segment, const std::string chars); -RDSGroup make_4A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, - const uint16_t year, const uint8_t month, const uint8_t day, - const uint8_t hour, const uint8_t minute, const int8_t local_offset); +RDSGroup make_0B_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, const bool MS, const bool DI, const uint8_t C, const std::string chars); +RDSGroup make_2A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, const uint8_t segment, const std::string chars); +RDSGroup make_4A_group(const uint16_t PI_code, const bool TP, const uint8_t PTY, const uint16_t year, const uint8_t month, const uint8_t day, const uint8_t hour, const uint8_t minute, const int8_t local_offset); -void gen_PSN(std::vector& frame, const std::string& psname, const RDS_flags * rds_flags); -void gen_RadioText(std::vector& frame, const std::string& text, const bool AB, const RDS_flags * rds_flags); -void gen_ClockTime(std::vector& frame, const RDS_flags * rds_flags, - const uint16_t year, const uint8_t month, const uint8_t day, - const uint8_t hour, const uint8_t minute, const int8_t local_offset); +void gen_PSN(std::vector& frame, const std::string& psname, const RDS_flags* rds_flags); +void gen_RadioText(std::vector& frame, const std::string& text, const bool AB, const RDS_flags* rds_flags); +void gen_ClockTime(std::vector& frame, const RDS_flags* rds_flags, const uint16_t year, const uint8_t month, const uint8_t day, const uint8_t hour, const uint8_t minute, const int8_t local_offset); } /* namespace rds */ -#endif/*__RDS_H__*/ +#endif /*__RDS_H__*/ diff --git a/firmware/application/qrcodegen.cpp b/firmware/application/qrcodegen.cpp index 2dcdbee50..1c9a4d8c5 100644 --- a/firmware/application/qrcodegen.cpp +++ b/firmware/application/qrcodegen.cpp @@ -44,40 +44,37 @@ static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium - { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low - { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High - { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile + {10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium + {7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low + {17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High + {13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile }; static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { // Version: (note that index 0 is for padding, and is set to an illegal value) // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium - { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low - { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High - { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High + {1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile }; static const uint16_t NUM_RAW_DATA_MODULES[40] = { - // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, - // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, - // 32, 33, 34, 35, 36, 37, 38, 39, 40 - 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648 -}; + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, + // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, + // 32, 33, 34, 35, 36, 37, 38, 39, 40 + 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648}; // @TODO: Put other LOCK_VERSIONS here #elif LOCK_VERSION == 3 static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = { - 26, 15, 44, 36 -}; + 26, 15, 44, 36}; static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = { - 1, 1, 2, 2 -}; + 1, 1, 2, 2}; static const uint16_t NUM_RAW_DATA_MODULES = 567; @@ -87,9 +84,10 @@ static const uint16_t NUM_RAW_DATA_MODULES = 567; #endif - static int max(int a, int b) { - if (a > b) { return a; } + if (a > b) { + return a; + } return b; } @@ -100,46 +98,59 @@ static int abs(int value) { } */ - /* #pragma mark - Mode testing and conversion */ static int8_t getAlphanumeric(char c) { - - if (c >= '0' && c <= '9') { return (c - '0'); } - if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); } - + if (c >= '0' && c <= '9') { + return (c - '0'); + } + if (c >= 'A' && c <= 'Z') { + return (c - 'A' + 10); + } + switch (c) { - case ' ': return 36; - case '$': return 37; - case '%': return 38; - case '*': return 39; - case '+': return 40; - case '-': return 41; - case '.': return 42; - case '/': return 43; - case ':': return 44; - } - + case ' ': + return 36; + case '$': + return 37; + case '%': + return 38; + case '*': + return 39; + case '+': + return 40; + case '-': + return 41; + case '.': + return 42; + case '/': + return 43; + case ':': + return 44; + } + return -1; } -static bool isAlphanumeric(const char *text, uint16_t length) { +static bool isAlphanumeric(const char* text, uint16_t length) { while (length != 0) { - if (getAlphanumeric(text[--length]) == -1) { return false; } + if (getAlphanumeric(text[--length]) == -1) { + return false; + } } return true; } - -static bool isNumeric(const char *text, uint16_t length) { +static bool isNumeric(const char* text, uint16_t length) { while (length != 0) { char c = text[--length]; - if (c < '0' || c > '9') { return false; } + if (c < '0' || c > '9') { + return false; + } } return true; } - /* #pragma mark - Counting */ // We store the following tightly packed (less 8) in modeInfo @@ -151,28 +162,33 @@ static char getModeBits(uint8_t version, uint8_t mode) { // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) unsigned int modeInfo = 0x7bbb80a; - + #if LOCK_VERSION == 0 || LOCK_VERSION > 9 - if (version > 9) { modeInfo >>= 9; } + if (version > 9) { + modeInfo >>= 9; + } #endif - + #if LOCK_VERSION == 0 || LOCK_VERSION > 26 - if (version > 26) { modeInfo >>= 9; } + if (version > 26) { + modeInfo >>= 9; + } #endif - + char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); - if (result == 15) { result = 16; } - + if (result == 15) { + result = 16; + } + return result; } - /* #pragma mark - BitBucket */ typedef struct BitBucket { uint32_t bitOffsetOrWidth; uint16_t capacityBytes; - uint8_t *data; + uint8_t* data; } BitBucket; /* @@ -194,15 +210,15 @@ static uint16_t bb_getBufferSizeBytes(uint32_t bits) { return ((bits + 7) / 8); } -static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) { +static void bb_initBuffer(BitBucket* bitBuffer, uint8_t* data, int32_t capacityBytes) { bitBuffer->bitOffsetOrWidth = 0; bitBuffer->capacityBytes = capacityBytes; bitBuffer->data = data; - + memset(data, 0, bitBuffer->capacityBytes); } -static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { +static void bb_initGrid(BitBucket* bitGrid, uint8_t* data, uint8_t size) { bitGrid->bitOffsetOrWidth = size; bitGrid->capacityBytes = bb_getGridSizeBytes(size); bitGrid->data = data; @@ -210,7 +226,7 @@ static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { memset(data, 0, bitGrid->capacityBytes); } -static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) { +static void bb_appendBits(BitBucket* bitBuffer, uint32_t val, uint8_t length) { uint32_t offset = bitBuffer->bitOffsetOrWidth; for (int8_t i = length - 1; i >= 0; i--, offset++) { bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); @@ -224,7 +240,7 @@ void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) } } */ -static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { +static void bb_setBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool on) { uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; uint8_t mask = 1 << (7 - (offset & 0x07)); if (on) { @@ -234,7 +250,7 @@ static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { } } -static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) { +static void bb_invertBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool invert) { uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; uint8_t mask = 1 << (7 - (offset & 0x07)); bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); @@ -245,48 +261,65 @@ static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) } } -static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) { +static bool bb_getBit(BitBucket* bitGrid, uint8_t x, uint8_t y) { uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; } - /* #pragma mark - Drawing Patterns */ // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. // This means it is possible to apply a mask, undo it, and try another mask. Note that a final // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). -static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) { +static void applyMask(BitBucket* modules, BitBucket* isFunction, uint8_t mask) { uint8_t size = modules->bitOffsetOrWidth; - + for (uint8_t y = 0; y < size; y++) { for (uint8_t x = 0; x < size; x++) { - if (bb_getBit(isFunction, x, y)) { continue; } - + if (bb_getBit(isFunction, x, y)) { + continue; + } + bool invert = 0; switch (mask) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + case 0: + invert = (x + y) % 2 == 0; + break; + case 1: + invert = y % 2 == 0; + break; + case 2: + invert = x % 3 == 0; + break; + case 3: + invert = (x + y) % 3 == 0; + break; + case 4: + invert = (x / 3 + y / 2) % 2 == 0; + break; + case 5: + invert = x * y % 2 + x * y % 3 == 0; + break; + case 6: + invert = (x * y % 2 + x * y % 3) % 2 == 0; + break; + case 7: + invert = ((x + y) % 2 + x * y % 3) % 2 == 0; + break; } bb_invertBit(modules, x, y, invert); } } } -static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) { +static void setFunctionModule(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y, bool on) { bb_setBit(modules, x, y, on); bb_setBit(isFunction, x, y, true); } // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). -static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { +static void drawFinderPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) { uint8_t size = modules->bitOffsetOrWidth; for (int8_t i = -4; i <= 4; i++) { @@ -301,7 +334,7 @@ static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t } // Draws a 5*5 alignment pattern, with the center module at (x, y). -static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { +static void drawAlignmentPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) { for (int8_t i = -2; i <= 2; i++) { for (int8_t j = -2; j <= 2; j++) { setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); @@ -311,8 +344,7 @@ static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint // Draws two copies of the format bits (with its own error correction code) // based on the given mask and this object's error correction level field. -static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) { - +static void drawFormatBits(BitBucket* modules, BitBucket* isFunction, uint8_t ecc, uint8_t mask) { uint8_t size = modules->bitOffsetOrWidth; // Calculate error correction code and pack bits @@ -321,56 +353,56 @@ static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ec for (int i = 0; i < 10; i++) { rem = (rem << 1) ^ ((rem >> 9) * 0x537); } - + data = data << 10 | rem; data ^= 0x5412; // uint15 - + // Draw first copy for (uint8_t i = 0; i <= 5; i++) { setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); } - + setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); - + for (int8_t i = 9; i < 15; i++) { setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); } - + // Draw second copy for (int8_t i = 0; i <= 7; i++) { setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); } - + for (int8_t i = 8; i < 15; i++) { setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); } - + setFunctionModule(modules, isFunction, 8, size - 8, true); } - // Draws two copies of the version bits (with its own error correction code), // based on this object's version field (which only has an effect for 7 <= version <= 40). -static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) { - +static void drawVersion(BitBucket* modules, BitBucket* isFunction, uint8_t version) { int8_t size = modules->bitOffsetOrWidth; #if LOCK_VERSION != 0 && LOCK_VERSION < 7 return; - + #else - if (version < 7) { return; } - + if (version < 7) { + return; + } + // Calculate error correction code and pack bits uint32_t rem = version; // version is uint6, in the range [7, 40] for (uint8_t i = 0; i < 12; i++) { rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); } - + uint32_t data = version << 12 | rem; // uint18 - + // Draw two copies for (uint8_t i = 0; i < 18; i++) { bool bit = ((data >> i) & 1) != 0; @@ -378,12 +410,11 @@ static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t versi setFunctionModule(modules, isFunction, a, b, bit); setFunctionModule(modules, isFunction, b, a, bit); } - + #endif } -static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) { - +static void drawFunctionPatterns(BitBucket* modules, BitBucket* isFunction, uint8_t version, uint8_t ecc) { uint8_t size = modules->bitOffsetOrWidth; // Draw the horizontal and vertical timing patterns @@ -391,36 +422,35 @@ static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); } - + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) drawFinderPattern(modules, isFunction, 3, 3); drawFinderPattern(modules, isFunction, size - 4, 3); drawFinderPattern(modules, isFunction, 3, size - 4); - + #if LOCK_VERSION == 0 || LOCK_VERSION > 1 if (version > 1) { - // Draw the numerous alignment patterns - + uint8_t alignCount = version / 7 + 2; uint8_t step; if (version != 32) { step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 - } else { // C-C-C-Combo breaker! + } else { // C-C-C-Combo breaker! step = 26; } - + uint8_t alignPositionIndex = alignCount - 1; uint8_t alignPosition[alignCount]; - + alignPosition[0] = 6; - + uint8_t size = version * 4 + 17; for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { alignPosition[alignPositionIndex--] = pos; } - + for (uint8_t i = 0; i < alignCount; i++) { for (uint8_t j = 0; j < alignCount; j++) { if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) { @@ -431,31 +461,31 @@ static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint } } } - + #endif - + // Draw configuration data drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor drawVersion(modules, isFunction, version); } - // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire // data area of this QR Code symbol. Function modules need to be marked off before this is called. -static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) { - +static void drawCodewords(BitBucket* modules, BitBucket* isFunction, BitBucket* codewords) { uint32_t bitLength = codewords->bitOffsetOrWidth; - uint8_t *data = codewords->data; - + uint8_t* data = codewords->data; + uint8_t size = modules->bitOffsetOrWidth; - + // Bit index into the data uint32_t i = 0; - + // Do the funny zigzag scan for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) { right = 5; } - + if (right == 6) { + right = 5; + } + for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter for (int j = 0; j < 2; j++) { uint8_t x = right - j; // Actual x coordinate @@ -472,33 +502,30 @@ static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket * } } - - /* #pragma mark - Penalty Calculation */ -#define PENALTY_N1 3 -#define PENALTY_N2 3 -#define PENALTY_N3 40 -#define PENALTY_N4 10 +#define PENALTY_N1 3 +#define PENALTY_N2 3 +#define PENALTY_N3 40 +#define PENALTY_N4 10 // Calculates and returns the penalty score based on state of this QR Code's current modules. // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. // @TODO: This can be optimized by working with the bytes instead of bits. -static uint32_t getPenaltyScore(BitBucket *modules) { +static uint32_t getPenaltyScore(BitBucket* modules) { uint32_t result = 0; - + uint8_t size = modules->bitOffsetOrWidth; - + // Adjacent modules in row having same color for (uint8_t y = 0; y < size; y++) { - bool colorX = bb_getBit(modules, 0, y); for (uint8_t x = 1, runX = 1; x < size; x++) { bool cx = bb_getBit(modules, x, y); if (cx != colorX) { colorX = cx; runX = 1; - + } else { runX++; if (runX == 5) { @@ -509,7 +536,7 @@ static uint32_t getPenaltyScore(BitBucket *modules) { } } } - + // Adjacent modules in column having same color for (uint8_t x = 0; x < size; x++) { bool colorY = bb_getBit(modules, x, 0); @@ -528,7 +555,7 @@ static uint32_t getPenaltyScore(BitBucket *modules) { } } } - + uint16_t black = 0; for (uint8_t y = 0; y < size; y++) { uint16_t bitsRow = 0, bitsCol = 0; @@ -560,7 +587,9 @@ static uint32_t getPenaltyScore(BitBucket *modules) { } // Balance of black and white modules - if (color) { black++; } + if (color) { + black++; + } } } @@ -569,11 +598,10 @@ static uint32_t getPenaltyScore(BitBucket *modules) { for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { result += PENALTY_N4; } - + return result; } - /* #pragma mark - Reed-Solomon Generator */ static uint8_t rs_multiply(uint8_t x, uint8_t y) { @@ -587,10 +615,10 @@ static uint8_t rs_multiply(uint8_t x, uint8_t y) { return z; } -static void rs_init(uint8_t degree, uint8_t *coeff) { +static void rs_init(uint8_t degree, uint8_t* coeff) { memset(coeff, 0, degree); coeff[degree - 1] = 1; - + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), // drop the highest term, and store the rest of the coefficients in order of descending powers. // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). @@ -607,32 +635,30 @@ static void rs_init(uint8_t degree, uint8_t *coeff) { } } -static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) { +static void rs_getRemainder(uint8_t degree, uint8_t* coeff, uint8_t* data, uint8_t length, uint8_t* result, uint8_t stride) { // Compute the remainder by performing polynomial division - - //for (uint8_t i = 0; i < degree; i++) { result[] = 0; } - //memset(result, 0, degree); - + + // for (uint8_t i = 0; i < degree; i++) { result[] = 0; } + // memset(result, 0, degree); + for (uint8_t i = 0; i < length; i++) { uint8_t factor = data[i] ^ result[0]; for (uint8_t j = 1; j < degree; j++) { result[(j - 1) * stride] = result[j * stride]; } result[(degree - 1) * stride] = 0; - + for (uint8_t j = 0; j < degree; j++) { result[j * stride] ^= rs_multiply(coeff[j], factor); } } } - - /* #pragma mark - QrCode */ -static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) { +static int8_t encodeDataCodewords(BitBucket* dataCodewords, const uint8_t* text, uint16_t length, uint8_t version) { int8_t mode = MODE_BYTE; - + if (isNumeric((char*)text, length)) { mode = MODE_NUMERIC; bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); @@ -649,12 +675,12 @@ static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, accumCount = 0; } } - + // 1 or 2 digits remaining if (accumCount > 0) { bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); } - + } else if (isAlphanumeric((char*)text, length)) { mode = MODE_ALPHANUMERIC; bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); @@ -662,7 +688,7 @@ static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t accumData = 0; uint8_t accumCount = 0; - for (uint16_t i = 0; i < length; i++) { + for (uint16_t i = 0; i < length; i++) { accumData = accumData * 45 + getAlphanumeric((char)(text[i])); accumCount++; if (accumCount == 2) { @@ -671,12 +697,12 @@ static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, accumCount = 0; } } - + // 1 character remaining if (accumCount > 0) { bb_appendBits(dataCodewords, accumData, 6); } - + } else { bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); @@ -684,16 +710,15 @@ static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, bb_appendBits(dataCodewords, (char)(text[i]), 8); } } - - //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); - + + // bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); + return mode; } -static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) { - +static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket* data) { // See: http://www.thonky.com/qr-code-tutorial/structure-final-message - + #if LOCK_VERSION == 0 uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; @@ -703,37 +728,38 @@ static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; uint16_t moduleCount = NUM_RAW_DATA_MODULES; #endif - + uint8_t blockEccLen = totalEcc / numBlocks; uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; uint8_t shortBlockLen = moduleCount / 8 / numBlocks; - + uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; - + uint8_t result[data->capacityBytes]; memset(result, 0, sizeof(result)); - + uint8_t coeff[blockEccLen]; rs_init(blockEccLen, coeff); - + uint16_t offset = 0; - uint8_t *dataBytes = data->data; - - + uint8_t* dataBytes = data->data; + // Interleave all short blocks for (uint8_t i = 0; i < shortDataBlockLen; i++) { uint16_t index = i; uint8_t stride = shortDataBlockLen; for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { result[offset++] = dataBytes[index]; - + #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 - if (blockNum == numShortBlocks) { stride++; } + if (blockNum == numShortBlocks) { + stride++; + } #endif index += stride; } } - + // Version less than 5 only have short blocks #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 { @@ -742,24 +768,27 @@ static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data uint8_t stride = shortDataBlockLen; for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { result[offset++] = dataBytes[index]; - - if (blockNum == 0) { stride++; } + + if (blockNum == 0) { + stride++; + } index += stride; } } #endif - + // Add all ecc blocks, interleaved uint8_t blockSize = shortDataBlockLen; for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { - #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 - if (blockNum == numShortBlocks) { blockSize++; } + if (blockNum == numShortBlocks) { + blockSize++; + } #endif rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); dataBytes += blockSize; } - + memcpy(data->data, result, data->capacityBytes); data->bitOffsetOrWidth = moduleCount; } @@ -768,7 +797,6 @@ static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data // The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); - /* #pragma mark - Public QRCode functions */ uint16_t qrcode_getBufferSize(uint8_t version) { @@ -776,15 +804,15 @@ uint16_t qrcode_getBufferSize(uint8_t version) { } // @TODO: Return error if data is too big. -int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { +int8_t qrcode_initBytes(QRCode* qrcode, uint8_t* modules, uint8_t version, uint8_t ecc, uint8_t* data, uint16_t length) { uint8_t size = version * 4 + 17; qrcode->version = version; qrcode->size = size; qrcode->ecc = ecc; qrcode->modules = modules; - + uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; - + #if LOCK_VERSION == 0 uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; @@ -793,20 +821,24 @@ int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8 uint16_t moduleCount = NUM_RAW_DATA_MODULES; uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; #endif - + struct BitBucket codewords; uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)]; bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes)); - + // Place the data code words into the buffer int8_t mode = encodeDataCodewords(&codewords, data, length, version); - - if (mode < 0) { return -1; } + + if (mode < 0) { + return -1; + } qrcode->mode = mode; - + // Add terminator and pad up to a byte if applicable uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; - if (padding > 4) { padding = 4; } + if (padding > 4) { + padding = 4; + } bb_appendBits(&codewords, 0, padding); bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); @@ -817,16 +849,16 @@ int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8 BitBucket modulesGrid; bb_initGrid(&modulesGrid, modules, size); - + BitBucket isFunctionGrid; uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)]; bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); - + // Draw function patterns, draw all codewords, do masking drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); performErrorCorrection(version, eccFormatBits, &codewords); drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); - + // Find the best (lowest penalty) mask uint8_t mask = 0; int32_t minPenalty = INT32_MAX; @@ -840,24 +872,24 @@ int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8 } applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR } - + qrcode->mask = mask; - + // Overwrite old format bits drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); - + // Apply the final choice of mask applyMask(&modulesGrid, &isFunctionGrid, mask); return 0; } -int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) { +int8_t qrcode_initText(QRCode* qrcode, uint8_t* modules, uint8_t version, uint8_t ecc, const char* data) { return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); } -bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { - if ( x >= qrcode->size || y >= qrcode->size) { +bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y) { + if (x >= qrcode->size || y >= qrcode->size) { return false; } @@ -871,6 +903,6 @@ uint8_t qrcode_getHexLength(QRCode *qrcode) { } void qrcode_getHex(QRCode *qrcode, char *result) { - + } */ diff --git a/firmware/application/qrcodegen.hpp b/firmware/application/qrcodegen.hpp index 6687184a1..154469d7a 100644 --- a/firmware/application/qrcodegen.hpp +++ b/firmware/application/qrcodegen.hpp @@ -33,7 +33,6 @@ * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp */ - #ifndef __QRCODEGEN_H_ #define __QRCODEGEN_H_ @@ -45,55 +44,45 @@ static const bool true = 1; #include - // QR Code Format Encoding -#define MODE_NUMERIC 0 -#define MODE_ALPHANUMERIC 1 -#define MODE_BYTE 2 - +#define MODE_NUMERIC 0 +#define MODE_ALPHANUMERIC 1 +#define MODE_BYTE 2 // Error Correction Code Levels -#define ECC_LOW 0 -#define ECC_MEDIUM 1 -#define ECC_QUARTILE 2 -#define ECC_HIGH 3 - +#define ECC_LOW 0 +#define ECC_MEDIUM 1 +#define ECC_QUARTILE 2 +#define ECC_HIGH 3 // If set to non-zero, this library can ONLY produce QR codes at that version // This saves a lot of dynamic memory, as the codeword tables are skipped #ifndef LOCK_VERSION -#define LOCK_VERSION 0 +#define LOCK_VERSION 0 #endif - typedef struct QRCode { uint8_t version; uint8_t size; uint8_t ecc; uint8_t mode; uint8_t mask; - uint8_t *modules; + uint8_t* modules; } QRCode; - #ifdef __cplusplus -extern "C"{ -#endif /* __cplusplus */ - - +extern "C" { +#endif /* __cplusplus */ uint16_t qrcode_getBufferSize(uint8_t version); -int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); -int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); - -bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); - +int8_t qrcode_initText(QRCode* qrcode, uint8_t* modules, uint8_t version, uint8_t ecc, const char* data); +int8_t qrcode_initBytes(QRCode* qrcode, uint8_t* modules, uint8_t version, uint8_t ecc, uint8_t* data, uint16_t length); +bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y); #ifdef __cplusplus } -#endif /* __cplusplus */ - +#endif /* __cplusplus */ -#endif /* __QRCODEGEN_H_ */ \ No newline at end of file +#endif /* __QRCODEGEN_H_ */ \ No newline at end of file diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 28e52e3b6..159924e4b 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -44,228 +44,217 @@ using namespace hackrf::one; namespace radio { -static constexpr uint32_t ssp1_cpsr = 2; +static constexpr uint32_t ssp1_cpsr = 2; static constexpr uint32_t ssp_scr( - const float pclk_f, - const uint32_t cpsr, - const float spi_f -) { - return static_cast(pclk_f / cpsr / spi_f - 1); + const float pclk_f, + const uint32_t cpsr, + const float spi_f) { + return static_cast(pclk_f / cpsr / spi_f - 1); } static constexpr SPIConfig ssp_config_max283x = { - .end_cb = NULL, - .ssport = gpio_max283x_select.port(), - .sspad = gpio_max283x_select.pad(), - .cr0 = - CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f)) - | CR0_FRFSPI - | CR0_DSS16BIT - , - .cpsr = ssp1_cpsr, + .end_cb = NULL, + .ssport = gpio_max283x_select.port(), + .sspad = gpio_max283x_select.pad(), + .cr0 = + CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f)) | CR0_FRFSPI | CR0_DSS16BIT, + .cpsr = ssp1_cpsr, }; static constexpr SPIConfig ssp_config_max5864 = { - .end_cb = NULL, - .ssport = gpio_max5864_select.port(), - .sspad = gpio_max5864_select.pad(), - .cr0 = - CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max5864_spi_f)) - | CR0_FRFSPI - | CR0_DSS8BIT - , - .cpsr = ssp1_cpsr, + .end_cb = NULL, + .ssport = gpio_max5864_select.port(), + .sspad = gpio_max5864_select.pad(), + .cr0 = + CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max5864_spi_f)) | CR0_FRFSPI | CR0_DSS8BIT, + .cpsr = ssp1_cpsr, }; static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1); -static spi::arbiter::Target ssp1_target_max283x { - ssp1_arbiter, - ssp_config_max283x -}; +static spi::arbiter::Target ssp1_target_max283x{ + ssp1_arbiter, + ssp_config_max283x}; -static spi::arbiter::Target ssp1_target_max5864 { - ssp1_arbiter, - ssp_config_max5864 -}; +static spi::arbiter::Target ssp1_target_max5864{ + ssp1_arbiter, + ssp_config_max5864}; static rf::path::Path rf_path; rffc507x::RFFC507x first_if; -max283x::MAX283x *second_if; -max2837::MAX2837 second_if_max2837 { ssp1_target_max283x }; -max2839::MAX2839 second_if_max2839 { ssp1_target_max283x }; -static max5864::MAX5864 baseband_codec { ssp1_target_max5864 }; +max283x::MAX283x* second_if; +max2837::MAX2837 second_if_max2837{ssp1_target_max283x}; +max2839::MAX2839 second_if_max2839{ssp1_target_max283x}; +static max5864::MAX5864 baseband_codec{ssp1_target_max5864}; static baseband::CPLD baseband_cpld; -static rf::Direction direction { rf::Direction::Receive }; +static rf::Direction direction{rf::Direction::Receive}; static bool baseband_invert = false; static bool mixer_invert = false; void init() { - if (hackrf_r9) { - gpio_r9_not_ant_pwr.write(1); - gpio_r9_not_ant_pwr.output(); - } - rf_path.init(); - first_if.init(); - second_if = hackrf_r9 - ? (max283x::MAX283x *) &second_if_max2839 - : (max283x::MAX283x *) &second_if_max2837; - second_if->init(); - baseband_codec.init(); - baseband_cpld.init(); + if (hackrf_r9) { + gpio_r9_not_ant_pwr.write(1); + gpio_r9_not_ant_pwr.output(); + } + rf_path.init(); + first_if.init(); + second_if = hackrf_r9 + ? (max283x::MAX283x*)&second_if_max2839 + : (max283x::MAX283x*)&second_if_max2837; + second_if->init(); + baseband_codec.init(); + baseband_cpld.init(); } void set_direction(const rf::Direction new_direction) { - /* TODO: Refactor all the various "Direction" enumerations into one. */ - /* TODO: Only make changes if direction changes, but beware of clock enabling. */ - - // Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction - // Use CPLD's EEPROM config when transmitting - // Use the SRAM overlay when receiving - - // teixeluis: undone "Hack to fix the CPLD (clocking ?) bug". - // Apparently with current CPLD code from the hackrf repo, - // toggling CPLD overlay should no longer be necessary: - if (direction != new_direction && new_direction == rf::Direction::Transmit) { - hackrf::cpld::init_from_eeprom(); - } - - direction = new_direction; - - if (hackrf_r9) { - /* - * HackRF One r9 inverts analog baseband only for RX. Previous hardware - * revisions inverted analog baseband for neither direction because of - * compensation in the CPLD. If we ever simplify the CPLD to handle RX - * and TX the same way, we will need to update this baseband_invert - * logic. - */ - baseband_invert = (direction == rf::Direction::Receive); - } else { - /* - * Analog baseband is inverted in RX but not TX. The RX inversion is - * corrected by the CPLD, but future hardware or CPLD changes may - * change this for either or both directions. For a given hardware+CPLD - * platform, baseband inversion is set here for RX and/or TX. Spectrum - * inversion resulting from the mixer is tracked separately according - * to the tuning configuration. We ask the CPLD to apply a correction - * for the total inversion. - */ - baseband_invert = false; - } - baseband_cpld.set_invert(mixer_invert ^ baseband_invert); - - second_if->set_mode((direction == rf::Direction::Transmit) ? max283x::Mode::Transmit : max283x::Mode::Receive); - rf_path.set_direction(direction); - - baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive); - - if (direction == rf::Direction::Receive) - led_rx.on(); - else - led_tx.on(); + /* TODO: Refactor all the various "Direction" enumerations into one. */ + /* TODO: Only make changes if direction changes, but beware of clock enabling. */ + + // Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction + // Use CPLD's EEPROM config when transmitting + // Use the SRAM overlay when receiving + + // teixeluis: undone "Hack to fix the CPLD (clocking ?) bug". + // Apparently with current CPLD code from the hackrf repo, + // toggling CPLD overlay should no longer be necessary: + if (direction != new_direction && new_direction == rf::Direction::Transmit) { + hackrf::cpld::init_from_eeprom(); + } + + direction = new_direction; + + if (hackrf_r9) { + /* + * HackRF One r9 inverts analog baseband only for RX. Previous hardware + * revisions inverted analog baseband for neither direction because of + * compensation in the CPLD. If we ever simplify the CPLD to handle RX + * and TX the same way, we will need to update this baseband_invert + * logic. + */ + baseband_invert = (direction == rf::Direction::Receive); + } else { + /* + * Analog baseband is inverted in RX but not TX. The RX inversion is + * corrected by the CPLD, but future hardware or CPLD changes may + * change this for either or both directions. For a given hardware+CPLD + * platform, baseband inversion is set here for RX and/or TX. Spectrum + * inversion resulting from the mixer is tracked separately according + * to the tuning configuration. We ask the CPLD to apply a correction + * for the total inversion. + */ + baseband_invert = false; + } + baseband_cpld.set_invert(mixer_invert ^ baseband_invert); + + second_if->set_mode((direction == rf::Direction::Transmit) ? max283x::Mode::Transmit : max283x::Mode::Receive); + rf_path.set_direction(direction); + + baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive); + + if (direction == rf::Direction::Receive) + led_rx.on(); + else + led_tx.on(); } bool set_tuning_frequency(const rf::Frequency frequency) { - rf::Frequency final_frequency = frequency ; - // if feature is enabled - if( portapack::persistent_memory::config_converter() ) { - //downconvert - if( portapack::persistent_memory::config_updown_converter() ) - { - final_frequency = frequency - portapack::persistent_memory::config_converter_freq(); - } - else //upconvert - { - final_frequency = frequency + portapack::persistent_memory::config_converter_freq(); - } - } - const auto tuning_config = tuning::config::create(final_frequency); - if( tuning_config.is_valid() ) { - first_if.disable(); - - if( tuning_config.first_lo_frequency ) { - first_if.set_frequency(tuning_config.first_lo_frequency); - first_if.enable(); - } - - const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency); - - rf_path.set_band(tuning_config.rf_path_band); - mixer_invert = tuning_config.mixer_invert; - baseband_cpld.set_invert(mixer_invert ^ baseband_invert); - - return result_second_if; - } else { - return false; - } + rf::Frequency final_frequency = frequency; + // if feature is enabled + if (portapack::persistent_memory::config_converter()) { + // downconvert + if (portapack::persistent_memory::config_updown_converter()) { + final_frequency = frequency - portapack::persistent_memory::config_converter_freq(); + } else // upconvert + { + final_frequency = frequency + portapack::persistent_memory::config_converter_freq(); + } + } + const auto tuning_config = tuning::config::create(final_frequency); + if (tuning_config.is_valid()) { + first_if.disable(); + + if (tuning_config.first_lo_frequency) { + first_if.set_frequency(tuning_config.first_lo_frequency); + first_if.enable(); + } + + const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency); + + rf_path.set_band(tuning_config.rf_path_band); + mixer_invert = tuning_config.mixer_invert; + baseband_cpld.set_invert(mixer_invert ^ baseband_invert); + + return result_second_if; + } else { + return false; + } } void set_rf_amp(const bool rf_amp) { - rf_path.set_rf_amp(rf_amp); - - if (direction == rf::Direction::Transmit) { - if (rf_amp) - led_tx.on(); - else - led_tx.off(); - } + rf_path.set_rf_amp(rf_amp); + + if (direction == rf::Direction::Transmit) { + if (rf_amp) + led_tx.on(); + else + led_tx.off(); + } } void set_lna_gain(const int_fast8_t db) { - second_if->set_lna_gain(db); + second_if->set_lna_gain(db); } void set_vga_gain(const int_fast8_t db) { - second_if->set_vga_gain(db); + second_if->set_vga_gain(db); } void set_tx_gain(const int_fast8_t db) { - second_if->set_tx_vga_gain(db); + second_if->set_tx_vga_gain(db); } void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) { - second_if->set_lpf_rf_bandwidth(bandwidth_minimum); + second_if->set_lpf_rf_bandwidth(bandwidth_minimum); } void set_baseband_rate(const uint32_t rate) { - portapack::clock_manager.set_sampling_frequency(rate); + portapack::clock_manager.set_sampling_frequency(rate); } void set_antenna_bias(const bool on) { - /* Pull MOSFET gate low to turn on antenna bias. */ - if (hackrf_r9) { - gpio_r9_not_ant_pwr.write(on ? 0 : 1); - } else { - first_if.set_gpo1(on ? 0 : 1); - } + /* Pull MOSFET gate low to turn on antenna bias. */ + if (hackrf_r9) { + gpio_r9_not_ant_pwr.write(on ? 0 : 1); + } else { + first_if.set_gpo1(on ? 0 : 1); + } } void disable() { - set_antenna_bias(false); - baseband_codec.set_mode(max5864::Mode::Shutdown); - second_if->set_mode(max2837::Mode::Standby); - first_if.disable(); - set_rf_amp(false); - - led_rx.off(); - led_tx.off(); + set_antenna_bias(false); + baseband_codec.set_mode(max5864::Mode::Shutdown); + second_if->set_mode(max2837::Mode::Standby); + first_if.disable(); + set_rf_amp(false); + + led_rx.off(); + led_tx.off(); } void enable(Configuration configuration) { - configure(configuration); + configure(configuration); } void configure(Configuration configuration) { - set_tuning_frequency(configuration.tuning_frequency); - set_rf_amp(configuration.rf_amp); - set_lna_gain(configuration.lna_gain); - set_vga_gain(configuration.vga_gain); - set_baseband_rate(configuration.baseband_rate); - set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth); - set_direction(configuration.direction); + set_tuning_frequency(configuration.tuning_frequency); + set_rf_amp(configuration.rf_amp); + set_lna_gain(configuration.lna_gain); + set_vga_gain(configuration.vga_gain); + set_baseband_rate(configuration.baseband_rate); + set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth); + set_direction(configuration.direction); } namespace debug { @@ -273,7 +262,7 @@ namespace debug { namespace first_if { uint32_t register_read(const size_t register_number) { - return radio::first_if.read(register_number); + return radio::first_if.read(register_number); } } /* namespace first_if */ @@ -281,11 +270,11 @@ uint32_t register_read(const size_t register_number) { namespace second_if { uint32_t register_read(const size_t register_number) { - return radio::second_if->read(register_number); + return radio::second_if->read(register_number); } uint8_t temp_sense() { - return radio::second_if->temp_sense() & 0x1f; + return radio::second_if->temp_sense() & 0x1f; } } /* namespace second_if */ diff --git a/firmware/application/radio.hpp b/firmware/application/radio.hpp index cb1929c8a..f261d32c7 100644 --- a/firmware/application/radio.hpp +++ b/firmware/application/radio.hpp @@ -30,13 +30,13 @@ namespace radio { struct Configuration { - rf::Frequency tuning_frequency; - uint32_t baseband_rate; - uint32_t baseband_filter_bandwidth; - rf::Direction direction; - bool rf_amp; - int8_t lna_gain; - int8_t vga_gain; + rf::Frequency tuning_frequency; + uint32_t baseband_rate; + uint32_t baseband_filter_bandwidth; + rf::Direction direction; + bool rf_amp; + int8_t lna_gain; + int8_t vga_gain; }; void init(); @@ -76,4 +76,4 @@ uint8_t temp_sense(); } /* namespace radio */ -#endif/*__RADIO_H__*/ +#endif /*__RADIO_H__*/ diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index e347c3f80..5419368e7 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -38,289 +38,290 @@ using namespace portapack; namespace { -static constexpr std::array am_configs { { // we config here all the non COMMON parameters to each AM modulation type in RX. - { taps_9k0_decim_2, taps_9k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth . - { taps_6k0_decim_2, taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments. - { taps_6k0_decim_2, taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 2K8 (+ 2K8) - { taps_6k0_decim_2, taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB }, // SSB LSB BW 2K8 (- 2K8) - { taps_6k0_decim_2, taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 0K7 (+ 0K7) used to get audio tone from CW Morse, assuming tx shifted +700hz aprox -} }; - -static constexpr std::array nbfm_configs { { - { taps_4k25_decim_0, taps_4k25_decim_1, taps_4k25_channel, 2500 }, - { taps_11k0_decim_0, taps_11k0_decim_1, taps_11k0_channel, 2500 }, - { taps_16k0_decim_0, taps_16k0_decim_1, taps_16k0_channel, 5000 }, -} }; - -static constexpr std::array wfm_configs { { - {taps_200k_wfm_decim_0, taps_200k_wfm_decim_1 }, - {taps_180k_wfm_decim_0, taps_180k_wfm_decim_1 }, - {taps_40k_wfm_decim_0, taps_40k_wfm_decim_1 }, -} }; +static constexpr std::array am_configs{{ + // we config here all the non COMMON parameters to each AM modulation type in RX. + {taps_9k0_decim_2, taps_9k0_dsb_channel, AMConfigureMessage::Modulation::DSB}, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth . + {taps_6k0_decim_2, taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB}, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments. + {taps_6k0_decim_2, taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB}, // SSB USB BW 2K8 (+ 2K8) + {taps_6k0_decim_2, taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB}, // SSB LSB BW 2K8 (- 2K8) + {taps_6k0_decim_2, taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB}, // SSB USB BW 0K7 (+ 0K7) used to get audio tone from CW Morse, assuming tx shifted +700hz aprox +}}; + +static constexpr std::array nbfm_configs{{ + {taps_4k25_decim_0, taps_4k25_decim_1, taps_4k25_channel, 2500}, + {taps_11k0_decim_0, taps_11k0_decim_1, taps_11k0_channel, 2500}, + {taps_16k0_decim_0, taps_16k0_decim_1, taps_16k0_channel, 5000}, +}}; + +static constexpr std::array wfm_configs{{ + {taps_200k_wfm_decim_0, taps_200k_wfm_decim_1}, + {taps_180k_wfm_decim_0, taps_180k_wfm_decim_1}, + {taps_40k_wfm_decim_0, taps_40k_wfm_decim_1}, +}}; } /* namespace */ rf::Frequency ReceiverModel::tuning_frequency() const { - return persistent_memory::tuned_frequency(); + return persistent_memory::tuned_frequency(); } void ReceiverModel::set_tuning_frequency(rf::Frequency f) { - persistent_memory::set_tuned_frequency(f); - update_tuning_frequency(); + persistent_memory::set_tuned_frequency(f); + update_tuning_frequency(); } rf::Frequency ReceiverModel::frequency_step() const { - return frequency_step_; + return frequency_step_; } void ReceiverModel::set_frequency_step(rf::Frequency f) { - frequency_step_ = f; + frequency_step_ = f; } void ReceiverModel::set_antenna_bias() { - update_antenna_bias(); + update_antenna_bias(); } bool ReceiverModel::rf_amp() const { - return rf_amp_; + return rf_amp_; } void ReceiverModel::set_rf_amp(bool enabled) { - rf_amp_ = enabled; - update_rf_amp(); + rf_amp_ = enabled; + update_rf_amp(); } int32_t ReceiverModel::lna() const { - return lna_gain_db_; + return lna_gain_db_; } void ReceiverModel::set_lna(int32_t v_db) { - lna_gain_db_ = v_db; - update_lna(); + lna_gain_db_ = v_db; + update_lna(); } uint32_t ReceiverModel::baseband_bandwidth() const { - return baseband_bandwidth_; + return baseband_bandwidth_; } void ReceiverModel::set_baseband_bandwidth(uint32_t v) { - baseband_bandwidth_ = v; - update_baseband_bandwidth(); + baseband_bandwidth_ = v; + update_baseband_bandwidth(); } int32_t ReceiverModel::vga() const { - return vga_gain_db_; + return vga_gain_db_; } void ReceiverModel::set_vga(int32_t v_db) { - vga_gain_db_ = v_db; - update_vga(); + vga_gain_db_ = v_db; + update_vga(); } int32_t ReceiverModel::tx_gain() const { - return tx_gain_db_; + return tx_gain_db_; } void ReceiverModel::set_tx_gain(int32_t v_db) { - tx_gain_db_ = v_db; - update_tx_gain(); + tx_gain_db_ = v_db; + update_tx_gain(); } uint32_t ReceiverModel::sampling_rate() const { - return sampling_rate_; + return sampling_rate_; } void ReceiverModel::set_sampling_rate(uint32_t v) { - sampling_rate_ = v; - update_sampling_rate(); + sampling_rate_ = v; + update_sampling_rate(); } ReceiverModel::Mode ReceiverModel::modulation() const { - return mode_; + return mode_; } void ReceiverModel::set_modulation(const Mode v) { - mode_ = v; - update_modulation(); + mode_ = v; + update_modulation(); } volume_t ReceiverModel::headphone_volume() const { - return headphone_volume_; + return headphone_volume_; } void ReceiverModel::set_headphone_volume(volume_t v) { - headphone_volume_ = v; - update_headphone_volume(); + headphone_volume_ = v; + update_headphone_volume(); } uint8_t ReceiverModel::squelch_level() const { - return squelch_level_; + return squelch_level_; } void ReceiverModel::set_squelch_level(uint8_t v) { - squelch_level_ = v; - update_modulation(); + squelch_level_ = v; + update_modulation(); } void ReceiverModel::enable() { - enabled_ = true; - radio::set_direction(rf::Direction::Receive); - update_tuning_frequency(); - update_antenna_bias(); - update_rf_amp(); - update_lna(); - update_vga(); - update_tx_gain(); - update_baseband_bandwidth(); - update_sampling_rate(); - update_modulation(); - - // TODO: would challenge if this should belong to the - // receiver_model namespace: - update_headphone_volume(); - - led_rx.on(); + enabled_ = true; + radio::set_direction(rf::Direction::Receive); + update_tuning_frequency(); + update_antenna_bias(); + update_rf_amp(); + update_lna(); + update_vga(); + update_tx_gain(); + update_baseband_bandwidth(); + update_sampling_rate(); + update_modulation(); + + // TODO: would challenge if this should belong to the + // receiver_model namespace: + update_headphone_volume(); + + led_rx.on(); } void ReceiverModel::disable() { - enabled_ = false; - radio::set_antenna_bias(false); + enabled_ = false; + radio::set_antenna_bias(false); - // TODO: Responsibility for enabling/disabling the radio is muddy. - // Some happens in ReceiverModel, some inside radio namespace. - radio::disable(); + // TODO: Responsibility for enabling/disabling the radio is muddy. + // Some happens in ReceiverModel, some inside radio namespace. + radio::disable(); - // TODO: we are doing this repeatedly in different levels of the - // call stack. Keeping it for now, but there seem to be too many - // redundant calls: - led_rx.off(); + // TODO: we are doing this repeatedly in different levels of the + // call stack. Keeping it for now, but there seem to be too many + // redundant calls: + led_rx.off(); } int32_t ReceiverModel::tuning_offset() { - if( (modulation() == Mode::SpectrumAnalysis) ) { - return 0; - } else { - return -(sampling_rate() / 4); - } + if ((modulation() == Mode::SpectrumAnalysis)) { + return 0; + } else { + return -(sampling_rate() / 4); + } } void ReceiverModel::update_tuning_frequency() { - radio::set_tuning_frequency(persistent_memory::tuned_frequency() + tuning_offset()); + radio::set_tuning_frequency(persistent_memory::tuned_frequency() + tuning_offset()); } void ReceiverModel::update_antenna_bias() { - if (enabled_) - radio::set_antenna_bias(portapack::get_antenna_bias()); + if (enabled_) + radio::set_antenna_bias(portapack::get_antenna_bias()); } void ReceiverModel::update_rf_amp() { - radio::set_rf_amp(rf_amp_); + radio::set_rf_amp(rf_amp_); } void ReceiverModel::update_lna() { - radio::set_lna_gain(lna_gain_db_); + radio::set_lna_gain(lna_gain_db_); } void ReceiverModel::update_baseband_bandwidth() { - radio::set_baseband_filter_bandwidth(baseband_bandwidth_); + radio::set_baseband_filter_bandwidth(baseband_bandwidth_); } void ReceiverModel::update_vga() { - radio::set_vga_gain(vga_gain_db_); + radio::set_vga_gain(vga_gain_db_); } void ReceiverModel::update_tx_gain() { - radio::set_tx_gain(tx_gain_db_); + radio::set_tx_gain(tx_gain_db_); } void ReceiverModel::set_am_configuration(const size_t n) { - if( n < am_configs.size() ) { - am_config_index = n; - update_modulation(); - } + if (n < am_configs.size()) { + am_config_index = n; + update_modulation(); + } } void ReceiverModel::set_nbfm_configuration(const size_t n) { - if( n < nbfm_configs.size() ) { - nbfm_config_index = n; - update_modulation(); - } + if (n < nbfm_configs.size()) { + nbfm_config_index = n; + update_modulation(); + } } void ReceiverModel::set_wfm_configuration(const size_t n) { - if( n < wfm_configs.size() ) { - wfm_config_index = n; - update_modulation(); - } + if (n < wfm_configs.size()) { + wfm_config_index = n; + update_modulation(); + } } void ReceiverModel::update_sampling_rate() { - // TODO: Move more low-level radio control stuff to M4. It'll enable tighter - // synchronization for things like wideband (sweeping) spectrum analysis, and - // protocols that need quick RX/TX turn-around. + // TODO: Move more low-level radio control stuff to M4. It'll enable tighter + // synchronization for things like wideband (sweeping) spectrum analysis, and + // protocols that need quick RX/TX turn-around. - // Disabling baseband while changing sampling rates seems like a good idea... - radio::set_baseband_rate(sampling_rate()); - update_tuning_frequency(); + // Disabling baseband while changing sampling rates seems like a good idea... + radio::set_baseband_rate(sampling_rate()); + update_tuning_frequency(); } void ReceiverModel::update_headphone_volume() { - // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do - // both? + // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do + // both? - audio::headphone::set_volume(headphone_volume_); + audio::headphone::set_volume(headphone_volume_); } void ReceiverModel::update_modulation() { - switch(modulation()) { - default: - case Mode::AMAudio: - update_am_configuration(); - break; + switch (modulation()) { + default: + case Mode::AMAudio: + update_am_configuration(); + break; - case Mode::NarrowbandFMAudio: - update_nbfm_configuration(); - break; + case Mode::NarrowbandFMAudio: + update_nbfm_configuration(); + break; - case Mode::WidebandFMAudio: - update_wfm_configuration(); - break; + case Mode::WidebandFMAudio: + update_wfm_configuration(); + break; - case Mode::SpectrumAnalysis: - case Mode::Capture: - break; - } + case Mode::SpectrumAnalysis: + case Mode::Capture: + break; + } } size_t ReceiverModel::am_configuration() const { - return am_config_index; + return am_config_index; } void ReceiverModel::update_am_configuration() { - am_configs[am_config_index].apply(); + am_configs[am_config_index].apply(); } size_t ReceiverModel::nbfm_configuration() const { - return nbfm_config_index; + return nbfm_config_index; } void ReceiverModel::update_nbfm_configuration() { - nbfm_configs[nbfm_config_index].apply(squelch_level_); + nbfm_configs[nbfm_config_index].apply(squelch_level_); } size_t ReceiverModel::wfm_configuration() const { - return wfm_config_index; + return wfm_config_index; } void ReceiverModel::update_wfm_configuration() { - wfm_configs[wfm_config_index].apply(); + wfm_configs[wfm_config_index].apply(); } void ReceiverModel::set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level) { - mode_ = new_mode; - frequency_step_ = new_frequency_step; - am_config_index = new_am_config_index; - nbfm_config_index = new_nbfm_config_index; - wfm_config_index = new_wfm_config_index; - squelch_level_ = new_squelch_level; + mode_ = new_mode; + frequency_step_ = new_frequency_step; + am_config_index = new_am_config_index; + nbfm_config_index = new_nbfm_config_index; + wfm_config_index = new_wfm_config_index; + squelch_level_ = new_squelch_level; } diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index 2a658ae4e..c633277f6 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -31,96 +31,96 @@ #include "volume.hpp" class ReceiverModel { -public: - enum class Mode { - AMAudio = 0, - NarrowbandFMAudio = 1, - WidebandFMAudio = 2, - SpectrumAnalysis = 3, - Capture = 4 - }; - - rf::Frequency tuning_frequency() const; - void set_tuning_frequency(rf::Frequency f); - - rf::Frequency frequency_step() const; - void set_frequency_step(rf::Frequency f); - - void set_antenna_bias(); - - bool rf_amp() const; - void set_rf_amp(bool enabled); - - int32_t lna() const; - void set_lna(int32_t v_db); - - uint32_t baseband_bandwidth() const; - void set_baseband_bandwidth(uint32_t v); - - int32_t vga() const; - void set_vga(int32_t v_db); - - int32_t tx_gain() const; - void set_tx_gain(int32_t v_db); - - uint32_t sampling_rate() const; - void set_sampling_rate(uint32_t v); - - Mode modulation() const; - void set_modulation(Mode v); - - volume_t headphone_volume() const; - void set_headphone_volume(volume_t v); - - uint8_t squelch_level() const; - void set_squelch_level(uint8_t v); - - void enable(); - void disable(); - - size_t am_configuration() const; - void set_am_configuration(const size_t n); - - size_t nbfm_configuration() const; - void set_nbfm_configuration(const size_t n); - - size_t wfm_configuration() const; - void set_wfm_configuration(const size_t n); - - void set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level); - -private: - rf::Frequency frequency_step_ { 25000 }; - bool enabled_ { false }; - bool rf_amp_ { false }; - int32_t lna_gain_db_ { 32 }; - uint32_t baseband_bandwidth_ { max283x::filter::bandwidth_minimum }; - int32_t vga_gain_db_ { 32 }; - int32_t tx_gain_db_ { 47 }; - Mode mode_ { Mode::NarrowbandFMAudio }; - uint32_t sampling_rate_ { 3072000 }; - size_t am_config_index = 0; - size_t nbfm_config_index = 0; - size_t wfm_config_index = 0; - volume_t headphone_volume_ { -43.0_dB }; - uint8_t squelch_level_ { 80 }; - - int32_t tuning_offset(); - - void update_tuning_frequency(); - void update_antenna_bias(); - void update_rf_amp(); - void update_lna(); - void update_baseband_bandwidth(); - void update_vga(); - void update_tx_gain(); - void update_sampling_rate(); - void update_headphone_volume(); - - void update_modulation(); - void update_am_configuration(); - void update_nbfm_configuration(); - void update_wfm_configuration(); + public: + enum class Mode { + AMAudio = 0, + NarrowbandFMAudio = 1, + WidebandFMAudio = 2, + SpectrumAnalysis = 3, + Capture = 4 + }; + + rf::Frequency tuning_frequency() const; + void set_tuning_frequency(rf::Frequency f); + + rf::Frequency frequency_step() const; + void set_frequency_step(rf::Frequency f); + + void set_antenna_bias(); + + bool rf_amp() const; + void set_rf_amp(bool enabled); + + int32_t lna() const; + void set_lna(int32_t v_db); + + uint32_t baseband_bandwidth() const; + void set_baseband_bandwidth(uint32_t v); + + int32_t vga() const; + void set_vga(int32_t v_db); + + int32_t tx_gain() const; + void set_tx_gain(int32_t v_db); + + uint32_t sampling_rate() const; + void set_sampling_rate(uint32_t v); + + Mode modulation() const; + void set_modulation(Mode v); + + volume_t headphone_volume() const; + void set_headphone_volume(volume_t v); + + uint8_t squelch_level() const; + void set_squelch_level(uint8_t v); + + void enable(); + void disable(); + + size_t am_configuration() const; + void set_am_configuration(const size_t n); + + size_t nbfm_configuration() const; + void set_nbfm_configuration(const size_t n); + + size_t wfm_configuration() const; + void set_wfm_configuration(const size_t n); + + void set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level); + + private: + rf::Frequency frequency_step_{25000}; + bool enabled_{false}; + bool rf_amp_{false}; + int32_t lna_gain_db_{32}; + uint32_t baseband_bandwidth_{max283x::filter::bandwidth_minimum}; + int32_t vga_gain_db_{32}; + int32_t tx_gain_db_{47}; + Mode mode_{Mode::NarrowbandFMAudio}; + uint32_t sampling_rate_{3072000}; + size_t am_config_index = 0; + size_t nbfm_config_index = 0; + size_t wfm_config_index = 0; + volume_t headphone_volume_{-43.0_dB}; + uint8_t squelch_level_{80}; + + int32_t tuning_offset(); + + void update_tuning_frequency(); + void update_antenna_bias(); + void update_rf_amp(); + void update_lna(); + void update_baseband_bandwidth(); + void update_vga(); + void update_tx_gain(); + void update_sampling_rate(); + void update_headphone_volume(); + + void update_modulation(); + void update_am_configuration(); + void update_nbfm_configuration(); + void update_wfm_configuration(); }; -#endif/*__RECEIVER_MODEL_H__*/ +#endif /*__RECEIVER_MODEL_H__*/ diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp index bc6ef17aa..61a61e1e7 100644 --- a/firmware/application/recent_entries.cpp +++ b/firmware/application/recent_entries.cpp @@ -24,38 +24,36 @@ namespace ui { RecentEntriesColumns::RecentEntriesColumns( - const std::initializer_list columns -) : _columns { columns } -{ + const std::initializer_list columns) + : _columns{columns} { } RecentEntriesHeader::RecentEntriesHeader( - const RecentEntriesColumns& columns -) : _columns { columns } -{ + const RecentEntriesColumns& columns) + : _columns{columns} { } void RecentEntriesHeader::paint(Painter& painter) { - const auto r = screen_rect(); - const auto& parent_style = style(); - - const Style style { - .font = parent_style.font, - .background = Color::blue(), - .foreground = parent_style.foreground, - }; - - auto p = r.location(); - for(const auto& column : _columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string(p, style, text); - p += { static_cast((width * 8) + 8), 0 }; - } + const auto r = screen_rect(); + const auto& parent_style = style(); + + const Style style{ + .font = parent_style.font, + .background = Color::blue(), + .foreground = parent_style.foreground, + }; + + auto p = r.location(); + for (const auto& column : _columns) { + const auto width = column.second; + auto text = column.first; + if (width > text.length()) { + text.append(width - text.length(), ' '); + } + + painter.draw_string(p, style, text); + p += {static_cast((width * 8) + 8), 0}; + } } } /* namespace ui */ diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 661d76604..bdd927721 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -33,62 +33,60 @@ #include #include -template +template using RecentEntries = std::list; -template +template typename ContainerType::const_iterator find(const ContainerType& entries, const Key key) { - return std::find_if( - std::begin(entries), std::end(entries), - [key](typename ContainerType::const_reference e) { return e.key() == key; } - ); + return std::find_if( + std::begin(entries), std::end(entries), + [key](typename ContainerType::const_reference e) { return e.key() == key; }); } -template +template static void truncate_entries(ContainerType& entries, const size_t entries_max = 64) { - while(entries.size() > entries_max) { - entries.pop_back(); - } + while (entries.size() > entries_max) { + entries.pop_back(); + } } -template +template typename ContainerType::reference on_packet(ContainerType& entries, const Key key) { - auto matching_recent = find(entries, key); - if( matching_recent != std::end(entries) ) { - // Found within. Move to front of list, increment counter. - entries.push_front(*matching_recent); - entries.erase(matching_recent); - } else { - entries.emplace_front(key); - truncate_entries(entries); - } - - return entries.front(); + auto matching_recent = find(entries, key); + if (matching_recent != std::end(entries)) { + // Found within. Move to front of list, increment counter. + entries.push_front(*matching_recent); + entries.erase(matching_recent); + } else { + entries.emplace_front(key); + truncate_entries(entries); + } + + return entries.front(); } -template +template static std::pair range_around( - const ContainerType& entries, - typename ContainerType::const_iterator item, - const size_t count -) { - auto start = item; - auto end = item; - size_t i = 0; - - // Move start iterator toward first entry. - while( (start != std::begin(entries)) && (i < count / 2) ) { - std::advance(start, -1); - i++; - } - - // Move end iterator toward last entry. - while( (end != std::end(entries)) && (i < count) ) { - std::advance(end, 1); - i++; - } - - return { start, end }; + const ContainerType& entries, + typename ContainerType::const_iterator item, + const size_t count) { + auto start = item; + auto end = item; + size_t i = 0; + + // Move start iterator toward first entry. + while ((start != std::begin(entries)) && (i < count / 2)) { + std::advance(start, -1); + i++; + } + + // Move end iterator toward last entry. + while ((end != std::end(entries)) && (i < count)) { + std::advance(end, 1); + i++; + } + + return {start, end}; } namespace ui { @@ -96,201 +94,189 @@ namespace ui { using RecentEntriesColumn = std::pair; class RecentEntriesColumns { -public: - using ContainerType = std::vector; + public: + using ContainerType = std::vector; - RecentEntriesColumns( - const std::initializer_list columns - ); + RecentEntriesColumns( + const std::initializer_list columns); - ContainerType::const_iterator begin() const { return std::begin(_columns); } - ContainerType::const_iterator end() const { return std::end(_columns); } + ContainerType::const_iterator begin() const { return std::begin(_columns); } + ContainerType::const_iterator end() const { return std::end(_columns); } -private: - const ContainerType _columns; + private: + const ContainerType _columns; }; class RecentEntriesHeader : public Widget { -public: - RecentEntriesHeader( - const RecentEntriesColumns& columns - ); + public: + RecentEntriesHeader( + const RecentEntriesColumns& columns); - void paint(Painter& painter) override; + void paint(Painter& painter) override; -private: - const RecentEntriesColumns& _columns; + private: + const RecentEntriesColumns& _columns; }; -template +template class RecentEntriesTable : public Widget { -public: - using Entry = typename Entries::value_type; - - std::function on_select { }; - - RecentEntriesTable( - Entries& recent - ) : recent { recent } - { - set_focusable(true); - } - - void paint(Painter& painter) override { - const auto r = screen_rect(); - const auto& s = style(); - - Rect target_rect { r.location(), { r.width(), s.font.line_height() }}; - const size_t visible_item_count = r.height() / s.font.line_height(); - - auto selected = find(recent, selected_key); - if( selected == std::end(recent) ) { - selected = std::begin(recent); - } - - auto range = range_around(recent, selected, visible_item_count); - - for(auto p = range.first; p != range.second; p++) { - const auto& entry = *p; - const auto is_selected_key = (selected_key == entry.key()); - const auto item_style = (has_focus() && is_selected_key) ? s.invert() : s; - draw(entry, target_rect, painter, item_style); - target_rect += { 0, target_rect.height() }; - } - - painter.fill_rectangle( - { target_rect.left(), target_rect.top(), target_rect.width(), r.bottom() - target_rect.top() }, - style().background - ); - } - - bool on_encoder(const EncoderEvent event) override { - advance(event); - return true; - } - - bool on_key(const ui::KeyEvent event) override { - if( event == ui::KeyEvent::Select ) { - if( on_select ) { - const auto selected = find(recent, selected_key); - if( selected != std::end(recent) ) { - on_select(*selected); - return true; - } - } - } - else if( event == ui::KeyEvent::Up ) { - if(selected_key == recent.front().key()){ - return false; - } - else { - advance(-1); - return true; - } - } - else if( event == ui::KeyEvent::Down ) { - if( selected_key == recent.back().key()) { - return false; - } - else { - advance(1); - return true; - } - } - return false; - } - - void on_focus() override { - advance(0); - } - -private: - Entries& recent; - - using EntryKey = typename Entry::Key; - EntryKey selected_key = Entry::invalid_key; - - void advance(const int32_t amount) { - auto selected = find(recent, selected_key); - if( selected == std::end(recent) ) { - if( recent.empty() ) { - selected_key = Entry::invalid_key; - } else { - selected_key = recent.front().key(); - } - } else { - if( amount < 0 ) { - if( selected != std::begin(recent) ) { - std::advance(selected, -1); - } - } - if( amount > 0 ) { - std::advance(selected, 1); - if( selected == std::end(recent) ) { - return; - } - } - selected_key = selected->key(); - } - - set_dirty(); - } - - void draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style - ); + public: + using Entry = typename Entries::value_type; + + std::function on_select{}; + + RecentEntriesTable( + Entries& recent) + : recent{recent} { + set_focusable(true); + } + + void paint(Painter& painter) override { + const auto r = screen_rect(); + const auto& s = style(); + + Rect target_rect{r.location(), {r.width(), s.font.line_height()}}; + const size_t visible_item_count = r.height() / s.font.line_height(); + + auto selected = find(recent, selected_key); + if (selected == std::end(recent)) { + selected = std::begin(recent); + } + + auto range = range_around(recent, selected, visible_item_count); + + for (auto p = range.first; p != range.second; p++) { + const auto& entry = *p; + const auto is_selected_key = (selected_key == entry.key()); + const auto item_style = (has_focus() && is_selected_key) ? s.invert() : s; + draw(entry, target_rect, painter, item_style); + target_rect += {0, target_rect.height()}; + } + + painter.fill_rectangle( + {target_rect.left(), target_rect.top(), target_rect.width(), r.bottom() - target_rect.top()}, + style().background); + } + + bool on_encoder(const EncoderEvent event) override { + advance(event); + return true; + } + + bool on_key(const ui::KeyEvent event) override { + if (event == ui::KeyEvent::Select) { + if (on_select) { + const auto selected = find(recent, selected_key); + if (selected != std::end(recent)) { + on_select(*selected); + return true; + } + } + } else if (event == ui::KeyEvent::Up) { + if (selected_key == recent.front().key()) { + return false; + } else { + advance(-1); + return true; + } + } else if (event == ui::KeyEvent::Down) { + if (selected_key == recent.back().key()) { + return false; + } else { + advance(1); + return true; + } + } + return false; + } + + void on_focus() override { + advance(0); + } + + private: + Entries& recent; + + using EntryKey = typename Entry::Key; + EntryKey selected_key = Entry::invalid_key; + + void advance(const int32_t amount) { + auto selected = find(recent, selected_key); + if (selected == std::end(recent)) { + if (recent.empty()) { + selected_key = Entry::invalid_key; + } else { + selected_key = recent.front().key(); + } + } else { + if (amount < 0) { + if (selected != std::begin(recent)) { + std::advance(selected, -1); + } + } + if (amount > 0) { + std::advance(selected, 1); + if (selected == std::end(recent)) { + return; + } + } + selected_key = selected->key(); + } + + set_dirty(); + } + + void draw( + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style); }; -template +template class RecentEntriesView : public View { -public: - using Entry = typename Entries::value_type; - - std::function on_select { }; - - RecentEntriesView( - const RecentEntriesColumns& columns, - Entries& recent - ) : _header { columns }, - _table { recent } - { - add_children({ - &_header, - &_table, - }); - - _table.on_select = [this](const Entry& entry) { if( this->on_select ) { this->on_select(entry); } }; - } - - void set_parent_rect(const Rect new_parent_rect) override { - constexpr Dim scale_height = 16; - - View::set_parent_rect(new_parent_rect); - _header.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height }); - _table.set_parent_rect({ - 0, scale_height, - new_parent_rect.width(), - new_parent_rect.height() - scale_height - }); - } - - void paint(Painter&) override { - // Children completely cover this View, do not paint. - // TODO: What happens here shouldn't matter if I do proper damage detection! - } - - void focus() override { - _table.focus(); - } - -private: - RecentEntriesHeader _header; - RecentEntriesTable _table; + public: + using Entry = typename Entries::value_type; + + std::function on_select{}; + + RecentEntriesView( + const RecentEntriesColumns& columns, + Entries& recent) + : _header{columns}, + _table{recent} { + add_children({ + &_header, + &_table, + }); + + _table.on_select = [this](const Entry& entry) { if( this->on_select ) { this->on_select(entry); } }; + } + + void set_parent_rect(const Rect new_parent_rect) override { + constexpr Dim scale_height = 16; + + View::set_parent_rect(new_parent_rect); + _header.set_parent_rect({0, 0, new_parent_rect.width(), scale_height}); + _table.set_parent_rect({0, scale_height, + new_parent_rect.width(), + new_parent_rect.height() - scale_height}); + } + + void paint(Painter&) override { + // Children completely cover this View, do not paint. + // TODO: What happens here shouldn't matter if I do proper damage detection! + } + + void focus() override { + _table.focus(); + } + + private: + RecentEntriesHeader _header; + RecentEntriesTable _table; }; } /* namespace ui */ -#endif/*__RECENT_ENTRIES_H__*/ +#endif /*__RECENT_ENTRIES_H__*/ diff --git a/firmware/application/replay_thread.cpp b/firmware/application/replay_thread.cpp index c303f75dd..02f5b0132 100644 --- a/firmware/application/replay_thread.cpp +++ b/firmware/application/replay_thread.cpp @@ -26,101 +26,100 @@ #include "buffer_exchange.hpp" struct BasebandReplay { - BasebandReplay(ReplayConfig* const config) { - baseband::replay_start(config); - } + BasebandReplay(ReplayConfig* const config) { + baseband::replay_start(config); + } - ~BasebandReplay() { - baseband::replay_stop(); - } + ~BasebandReplay() { + baseband::replay_stop(); + } }; // ReplayThread /////////////////////////////////////////////////////////// ReplayThread::ReplayThread( - std::unique_ptr reader, - size_t read_size, - size_t buffer_count, - bool* ready_signal, - std::function terminate_callback -) : config { read_size, buffer_count }, - reader { std::move(reader) }, - ready_sig { ready_signal }, - terminate_callback { std::move(terminate_callback) } -{ - // Need significant stack for FATFS - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this); + std::unique_ptr reader, + size_t read_size, + size_t buffer_count, + bool* ready_signal, + std::function terminate_callback) + : config{read_size, buffer_count}, + reader{std::move(reader)}, + ready_sig{ready_signal}, + terminate_callback{std::move(terminate_callback)} { + // Need significant stack for FATFS + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this); } ReplayThread::~ReplayThread() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } + if (thread) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } } msg_t ReplayThread::static_fn(void* arg) { - auto obj = static_cast(arg); - const auto return_code = obj->run(); - if( obj->terminate_callback ) { - obj->terminate_callback(return_code); - } - return 0; + auto obj = static_cast(arg); + const auto return_code = obj->run(); + if (obj->terminate_callback) { + obj->terminate_callback(return_code); + } + return 0; } uint32_t ReplayThread::run() { - BasebandReplay replay { &config }; - BufferExchange buffers { &config }; - - StreamBuffer* prefill_buffer { nullptr }; - - // Wait for FIFOs to be allocated in baseband - // Wait for ui_replay_view to tell us that the buffers are ready (awful :( ) - while (!(*ready_sig)) { - chThdSleep(100); - }; - - // While empty buffers fifo is not empty... - while (!buffers.empty()) { - prefill_buffer = buffers.get_prefill(); - - if (prefill_buffer == nullptr) { - buffers.put_app(prefill_buffer); - } else { - size_t blocks = config.read_size / 512; - - for (size_t c = 0; c < blocks; c++) { - auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512); - if( read_result.is_error() ) { - return READ_ERROR; - } - } - - prefill_buffer->set_size(config.read_size); - - buffers.put(prefill_buffer); - } - }; - - baseband::set_fifo_data(nullptr); - - while( !chThdShouldTerminate() ) { - auto buffer = buffers.get(); - - auto read_result = reader->read(buffer->data(), buffer->capacity()); - if( read_result.is_error() ) { - return READ_ERROR; - } else { - if (read_result.value() == 0) { - return END_OF_FILE; - } - } - - buffer->set_size(buffer->capacity()); - - buffers.put(buffer); - } - - return TERMINATED; + BasebandReplay replay{&config}; + BufferExchange buffers{&config}; + + StreamBuffer* prefill_buffer{nullptr}; + + // Wait for FIFOs to be allocated in baseband + // Wait for ui_replay_view to tell us that the buffers are ready (awful :( ) + while (!(*ready_sig)) { + chThdSleep(100); + }; + + // While empty buffers fifo is not empty... + while (!buffers.empty()) { + prefill_buffer = buffers.get_prefill(); + + if (prefill_buffer == nullptr) { + buffers.put_app(prefill_buffer); + } else { + size_t blocks = config.read_size / 512; + + for (size_t c = 0; c < blocks; c++) { + auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512); + if (read_result.is_error()) { + return READ_ERROR; + } + } + + prefill_buffer->set_size(config.read_size); + + buffers.put(prefill_buffer); + } + }; + + baseband::set_fifo_data(nullptr); + + while (!chThdShouldTerminate()) { + auto buffer = buffers.get(); + + auto read_result = reader->read(buffer->data(), buffer->capacity()); + if (read_result.is_error()) { + return READ_ERROR; + } else { + if (read_result.value() == 0) { + return END_OF_FILE; + } + } + + buffer->set_size(buffer->capacity()); + + buffers.put(buffer); + } + + return TERMINATED; } diff --git a/firmware/application/replay_thread.hpp b/firmware/application/replay_thread.hpp index c7c509c9a..61b1d4715 100644 --- a/firmware/application/replay_thread.hpp +++ b/firmware/application/replay_thread.hpp @@ -35,41 +35,40 @@ #include class ReplayThread { -public: - ReplayThread( - std::unique_ptr reader, - size_t read_size, - size_t buffer_count, - bool* ready_signal, - std::function terminate_callback - ); - ~ReplayThread(); + public: + ReplayThread( + std::unique_ptr reader, + size_t read_size, + size_t buffer_count, + bool* ready_signal, + std::function terminate_callback); + ~ReplayThread(); - ReplayThread(const ReplayThread&) = delete; - ReplayThread(ReplayThread&&) = delete; - ReplayThread& operator=(const ReplayThread&) = delete; - ReplayThread& operator=(ReplayThread&&) = delete; + ReplayThread(const ReplayThread&) = delete; + ReplayThread(ReplayThread&&) = delete; + ReplayThread& operator=(const ReplayThread&) = delete; + ReplayThread& operator=(ReplayThread&&) = delete; - const ReplayConfig& state() const { - return config; - }; + const ReplayConfig& state() const { + return config; + }; - enum replaythread_return { - READ_ERROR = 0, - END_OF_FILE, - TERMINATED - }; + enum replaythread_return { + READ_ERROR = 0, + END_OF_FILE, + TERMINATED + }; -private: - ReplayConfig config; - std::unique_ptr reader; - bool* ready_sig; - std::function terminate_callback; - Thread* thread { nullptr }; + private: + ReplayConfig config; + std::unique_ptr reader; + bool* ready_sig; + std::function terminate_callback; + Thread* thread{nullptr}; - static msg_t static_fn(void* arg); + static msg_t static_fn(void* arg); - uint32_t run(); + uint32_t run(); }; -#endif/*__REPLAY_THREAD_H__*/ +#endif /*__REPLAY_THREAD_H__*/ diff --git a/firmware/application/rf_path.cpp b/firmware/application/rf_path.cpp index 54d4d13cb..260a44c29 100644 --- a/firmware/application/rf_path.cpp +++ b/firmware/application/rf_path.cpp @@ -40,122 +40,118 @@ using GPIOs = std::array; /* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config. * No idea why. */ -constexpr GPIOs gpios { - gpio_mix_bypass, - gpio_not_mix_bypass, - gpio_tx_mix_bp, - gpio_rx_mix_bp, - gpio_hp, - gpio_lp, - gpio_amp_bypass, - gpio_tx_amp, - gpio_not_tx_amp_pwr, - gpio_rx_amp, - gpio_not_rx_amp_pwr, +constexpr GPIOs gpios{ + gpio_mix_bypass, + gpio_not_mix_bypass, + gpio_tx_mix_bp, + gpio_rx_mix_bp, + gpio_hp, + gpio_lp, + gpio_amp_bypass, + gpio_tx_amp, + gpio_not_tx_amp_pwr, + gpio_rx_amp, + gpio_not_rx_amp_pwr, }; struct Config { - using base_type = uint16_t; - - union { - struct { - bool tx : 1; - bool rx : 1; - bool mix_bypass : 1; - bool not_mix_bypass : 1; - bool tx_mix_bp : 1; - bool rx_mix_bp : 1; - bool hp : 1; - bool lp : 1; - bool amp_bypass : 1; - bool tx_amp : 1; - bool not_tx_amp : 1; - bool rx_amp : 1; - bool not_rx_amp : 1; - }; - base_type w; - }; - - constexpr Config( - const Direction direction, - const Band band, - const bool amplify - ) : - tx(direction == Direction::Transmit), - rx(direction == Direction::Receive), - mix_bypass(band == Band::Mid), - not_mix_bypass(band != Band::Mid), - tx_mix_bp((direction == Direction::Transmit) && (band == Band::Mid)), - rx_mix_bp((direction == Direction::Receive) && (band == Band::Mid)), - hp(band == Band::High), - lp(band == Band::Low), - amp_bypass(!amplify), - tx_amp((direction == Direction::Transmit) && amplify), - not_tx_amp(!tx_amp), - rx_amp((direction == Direction::Receive) && amplify), - not_rx_amp(!rx_amp) - { - } - - constexpr Config( - ) : Config(Direction::Receive, Band::Mid, false) - { - } - - constexpr Config( - const base_type w - ) : w(w) - { - } - - constexpr Config operator^(const Config& r) const { - return w ^ r.w; - } - - constexpr Config operator&(const Config& r) const { - return w & r.w; - } - - constexpr bool operator[](const size_t n) const { - return (w >> n) & 1; - } - - static void gpio_init() { - if (hackrf_r9) { - gpio_r9_rx.output(); - } else { - gpio_og_tx.output(); - gpio_og_rx.output(); - } - for (auto gpio : gpios) { - gpio.output(); - } - } - - void apply() const { - /* NOTE: Assumes order in gpios[] and Config bitfield match, - * after the 'tx' and 'rx' fields which are handled specially. */ - for (size_t n = 0; n < gpios.size() + 2; n++) { - bool value = (*this)[n]; - switch (n) { - case 0: - if (!hackrf_r9) { - gpio_og_tx.write(value); - } - break; - case 1: - if (hackrf_r9) { - gpio_r9_rx.write(value); - } else { - gpio_og_rx.write(value); - } - break; - default: - gpios[n - 2].write(value); - break; - } - } - } + using base_type = uint16_t; + + union { + struct { + bool tx : 1; + bool rx : 1; + bool mix_bypass : 1; + bool not_mix_bypass : 1; + bool tx_mix_bp : 1; + bool rx_mix_bp : 1; + bool hp : 1; + bool lp : 1; + bool amp_bypass : 1; + bool tx_amp : 1; + bool not_tx_amp : 1; + bool rx_amp : 1; + bool not_rx_amp : 1; + }; + base_type w; + }; + + constexpr Config( + const Direction direction, + const Band band, + const bool amplify) + : tx(direction == Direction::Transmit), + rx(direction == Direction::Receive), + mix_bypass(band == Band::Mid), + not_mix_bypass(band != Band::Mid), + tx_mix_bp((direction == Direction::Transmit) && (band == Band::Mid)), + rx_mix_bp((direction == Direction::Receive) && (band == Band::Mid)), + hp(band == Band::High), + lp(band == Band::Low), + amp_bypass(!amplify), + tx_amp((direction == Direction::Transmit) && amplify), + not_tx_amp(!tx_amp), + rx_amp((direction == Direction::Receive) && amplify), + not_rx_amp(!rx_amp) { + } + + constexpr Config() + : Config(Direction::Receive, Band::Mid, false) { + } + + constexpr Config( + const base_type w) + : w(w) { + } + + constexpr Config operator^(const Config& r) const { + return w ^ r.w; + } + + constexpr Config operator&(const Config& r) const { + return w & r.w; + } + + constexpr bool operator[](const size_t n) const { + return (w >> n) & 1; + } + + static void gpio_init() { + if (hackrf_r9) { + gpio_r9_rx.output(); + } else { + gpio_og_tx.output(); + gpio_og_rx.output(); + } + for (auto gpio : gpios) { + gpio.output(); + } + } + + void apply() const { + /* NOTE: Assumes order in gpios[] and Config bitfield match, + * after the 'tx' and 'rx' fields which are handled specially. */ + for (size_t n = 0; n < gpios.size() + 2; n++) { + bool value = (*this)[n]; + switch (n) { + case 0: + if (!hackrf_r9) { + gpio_og_tx.write(value); + } + break; + case 1: + if (hackrf_r9) { + gpio_r9_rx.write(value); + } else { + gpio_og_rx.write(value); + } + break; + default: + gpios[n - 2].write(value); + break; + } + } + } }; using ConfigAmp = std::array; @@ -163,30 +159,28 @@ using ConfigDirection = std::array; using ConfigBand = std::array; constexpr ConfigAmp config_amp( - const Direction direction, - const Band band -) { - return { { - { .direction = direction, .band = band, .amplify = false }, - { .direction = direction, .band = band, .amplify = true }, - } }; + const Direction direction, + const Band band) { + return {{ + {.direction = direction, .band = band, .amplify = false}, + {.direction = direction, .band = band, .amplify = true}, + }}; } constexpr ConfigDirection config_rx_tx( - const Band band -) { - return { - config_amp(Direction::Receive, band), - config_amp(Direction::Transmit, band), - }; + const Band band) { + return { + config_amp(Direction::Receive, band), + config_amp(Direction::Transmit, band), + }; } constexpr ConfigBand config_band() { - return { - config_rx_tx(Band::Low), - config_rx_tx(Band::Mid), - config_rx_tx(Band::High), - }; + return { + config_rx_tx(Band::Low), + config_rx_tx(Band::Mid), + config_rx_tx(Band::High), + }; } constexpr ConfigBand config_table = config_band(); @@ -194,54 +188,53 @@ constexpr ConfigBand config_table = config_band(); static_assert(sizeof(config_table) == sizeof(Config::base_type) * 3 * 2 * 2, "rf path config table unexpected size"); constexpr Config get_config( - const Direction direction, - const Band band, - const bool amplify -) { - return config_table[toUType(band)][toUType(direction)][amplify ? 1 : 0]; + const Direction direction, + const Band band, + const bool amplify) { + return config_table[toUType(band)][toUType(direction)][amplify ? 1 : 0]; } } /* namespace */ void Path::init() { - update(); - Config::gpio_init(); + update(); + Config::gpio_init(); } void Path::set_direction(const Direction new_direction) { - direction = new_direction; - update(); + direction = new_direction; + update(); } void Path::set_band(const Band new_band) { - band = new_band; - update(); + band = new_band; + update(); } void Path::set_rf_amp(const bool new_rf_amp) { - rf_amp = new_rf_amp; - update(); + rf_amp = new_rf_amp; + update(); } void Path::update() { - /* 0 ^ 0 => 0 & 0 = 0 ^ 0 = 0 (no change) - * 0 ^ 1 => 1 & 0 = 0 ^ 0 = 0 (ignore change to 1) - * 1 ^ 0 => 1 & 1 = 1 ^ 1 = 0 (allow change to 0) - * 1 ^ 1 => 0 & 1 = 0 ^ 1 = 1 (no change) - */ - //const Config changed = _config ^ config_next; - //const Config turned_off = _config & changed; - - /* In transition, ignore the bits that are turning on. So this transition phase - * only turns off signals. It doesn't turn on signals. - */ - //const Config transition_config = _config ^ turned_off; - //update_signals(transition_config); - - /* Move to the final state by turning on required signals. */ - const auto config = get_config(direction, band, rf_amp); - config.apply(); + /* 0 ^ 0 => 0 & 0 = 0 ^ 0 = 0 (no change) + * 0 ^ 1 => 1 & 0 = 0 ^ 0 = 0 (ignore change to 1) + * 1 ^ 0 => 1 & 1 = 1 ^ 1 = 0 (allow change to 0) + * 1 ^ 1 => 0 & 1 = 0 ^ 1 = 1 (no change) + */ + // const Config changed = _config ^ config_next; + // const Config turned_off = _config & changed; + + /* In transition, ignore the bits that are turning on. So this transition phase + * only turns off signals. It doesn't turn on signals. + */ + // const Config transition_config = _config ^ turned_off; + // update_signals(transition_config); + + /* Move to the final state by turning on required signals. */ + const auto config = get_config(direction, band, rf_amp); + config.apply(); } -} /* path */ -} /* rf */ +} // namespace path +} // namespace rf diff --git a/firmware/application/rf_path.hpp b/firmware/application/rf_path.hpp index 0246a6985..d6622f80f 100644 --- a/firmware/application/rf_path.hpp +++ b/firmware/application/rf_path.hpp @@ -32,44 +32,44 @@ using Frequency = int64_t; using FrequencyRange = range_t; enum class Direction { - /* Zero-based, used as index into table */ - Receive = 0, - Transmit = 1, + /* Zero-based, used as index into table */ + Receive = 0, + Transmit = 1, }; namespace path { -constexpr FrequencyRange band_low { 0, 2170000000 }; -constexpr FrequencyRange band_high { 2740000000, 7250000000 }; -constexpr FrequencyRange band_mid { band_low.maximum, band_high.minimum }; +constexpr FrequencyRange band_low{0, 2170000000}; +constexpr FrequencyRange band_high{2740000000, 7250000000}; +constexpr FrequencyRange band_mid{band_low.maximum, band_high.minimum}; enum class Band { - /* Zero-based, used as index into frequency_bands table */ - Low = 0, - Mid = 1, - High = 2, + /* Zero-based, used as index into frequency_bands table */ + Low = 0, + Mid = 1, + High = 2, }; class Path { -public: - void init(); + public: + void init(); - void set_direction(const Direction direction); - void set_band(const Band band); - void set_rf_amp(const bool rf_amp); + void set_direction(const Direction direction); + void set_band(const Band band); + void set_rf_amp(const bool rf_amp); -private: - Direction direction { Direction::Receive }; - Band band { Band::Mid }; - bool rf_amp { false }; + private: + Direction direction{Direction::Receive}; + Band band{Band::Mid}; + bool rf_amp{false}; - void update(); + void update(); }; -} /* path */ +} // namespace path -constexpr FrequencyRange tuning_range { path::band_low.minimum, path::band_high.maximum }; +constexpr FrequencyRange tuning_range{path::band_low.minimum, path::band_high.maximum}; -} /* rf */ +} // namespace rf -#endif/*__RF_PATH_H__*/ +#endif /*__RF_PATH_H__*/ diff --git a/firmware/application/rfm69.cpp b/firmware/application/rfm69.cpp index 0978098d4..5c8971cd7 100644 --- a/firmware/application/rfm69.cpp +++ b/firmware/application/rfm69.cpp @@ -25,52 +25,52 @@ #include "portapack_shared_memory.hpp" uint32_t RFM69::gen_frame(std::vector& payload) { - CRC<16> crc { 0x1021, 0x1D0F, 0xFFFF }; - std::vector frame { }; - uint8_t byte_out = 0; - - // Preamble - // Really is 0xAA but the RFM69 skips the very last bit (bug ?) - // so the whole preamble is shifted right to simulate that - frame.insert(frame.begin(), num_preamble_, 0x55); - - // Sync word - frame.push_back(sync_word_ >> 8); - frame.push_back(sync_word_ & 0xFF); - - // Payload length - payload.insert(payload.begin(), payload.size()); - - crc.process_bytes(payload.data(), payload.size()); - - if (CRC_) { - payload.push_back(crc.checksum() >> 8); - payload.push_back(crc.checksum() & 0xFF); - } - - // Manchester-encode payload - if (manchester_) { - for (auto byte : payload) { - for (uint32_t b = 0; b < 8; b++) { - byte_out <<= 2; - - if (byte & 0x80) - byte_out |= 0b10; - else - byte_out |= 0b01; - - if ((b & 3) == 3) - frame.push_back(byte_out); - - byte <<= 1; - } - } - } else { - frame.insert(frame.end(), payload.begin(), payload.end()); - } - - memcpy(shared_memory.bb_data.data, frame.data(), frame.size()); - - // Copy for baseband - return frame.size(); + CRC<16> crc{0x1021, 0x1D0F, 0xFFFF}; + std::vector frame{}; + uint8_t byte_out = 0; + + // Preamble + // Really is 0xAA but the RFM69 skips the very last bit (bug ?) + // so the whole preamble is shifted right to simulate that + frame.insert(frame.begin(), num_preamble_, 0x55); + + // Sync word + frame.push_back(sync_word_ >> 8); + frame.push_back(sync_word_ & 0xFF); + + // Payload length + payload.insert(payload.begin(), payload.size()); + + crc.process_bytes(payload.data(), payload.size()); + + if (CRC_) { + payload.push_back(crc.checksum() >> 8); + payload.push_back(crc.checksum() & 0xFF); + } + + // Manchester-encode payload + if (manchester_) { + for (auto byte : payload) { + for (uint32_t b = 0; b < 8; b++) { + byte_out <<= 2; + + if (byte & 0x80) + byte_out |= 0b10; + else + byte_out |= 0b01; + + if ((b & 3) == 3) + frame.push_back(byte_out); + + byte <<= 1; + } + } + } else { + frame.insert(frame.end(), payload.begin(), payload.end()); + } + + memcpy(shared_memory.bb_data.data, frame.data(), frame.size()); + + // Copy for baseband + return frame.size(); } diff --git a/firmware/application/rfm69.hpp b/firmware/application/rfm69.hpp index f1053230e..21e42dc7d 100644 --- a/firmware/application/rfm69.hpp +++ b/firmware/application/rfm69.hpp @@ -29,30 +29,29 @@ #include "utility.hpp" class RFM69 { -public: + public: RFM69(const uint8_t num_preamble, const uint16_t sync_word, const bool CRC, const bool manchester) - : num_preamble_(num_preamble), sync_word_(sync_word), CRC_(CRC), manchester_(manchester) - { } - //~RFM69(); - - void set_sync_word(const uint16_t sync_word) { - sync_word_ = sync_word; - }; - void set_data_config(const bool CRC, const bool manchester) { - CRC_ = CRC; - manchester_ = manchester; - }; - void set_num_preamble(const uint8_t num_preamble) { - num_preamble_ = num_preamble; - }; - - uint32_t gen_frame(std::vector& payload); - -private: - uint8_t num_preamble_ { 5 }; - uint16_t sync_word_ { 0x2DD4 }; - bool CRC_ { true }; - bool manchester_ { false }; + : num_preamble_(num_preamble), sync_word_(sync_word), CRC_(CRC), manchester_(manchester) {} + //~RFM69(); + + void set_sync_word(const uint16_t sync_word) { + sync_word_ = sync_word; + }; + void set_data_config(const bool CRC, const bool manchester) { + CRC_ = CRC; + manchester_ = manchester; + }; + void set_num_preamble(const uint8_t num_preamble) { + num_preamble_ = num_preamble; + }; + + uint32_t gen_frame(std::vector& payload); + + private: + uint8_t num_preamble_{5}; + uint16_t sync_word_{0x2DD4}; + bool CRC_{true}; + bool manchester_{false}; }; -#endif/*__RFM69_H__*/ +#endif /*__RFM69_H__*/ diff --git a/firmware/application/rtc_time.cpp b/firmware/application/rtc_time.cpp index d4a9f506e..c5b803a49 100644 --- a/firmware/application/rtc_time.cpp +++ b/firmware/application/rtc_time.cpp @@ -26,7 +26,7 @@ namespace rtc_time { Signal<> signal_tick_second; void on_tick_second() { - signal_tick_second.emit(); + signal_tick_second.emit(); } } /* namespace rtc_time */ diff --git a/firmware/application/rtc_time.hpp b/firmware/application/rtc_time.hpp index e43793be4..9a0bf568e 100644 --- a/firmware/application/rtc_time.hpp +++ b/firmware/application/rtc_time.hpp @@ -32,4 +32,4 @@ void on_tick_second(); } /* namespace rtc_time */ -#endif/*__RTC_TIME_H__*/ +#endif /*__RTC_TIME_H__*/ diff --git a/firmware/application/sd_card.cpp b/firmware/application/sd_card.cpp index b8f5fec77..6edfe9aa8 100644 --- a/firmware/application/sd_card.cpp +++ b/firmware/application/sd_card.cpp @@ -33,10 +33,10 @@ namespace { bool card_present = false; -Status status_ { Status::NotPresent }; +Status status_{Status::NotPresent}; FRESULT mount() { - return f_mount(&fs, reinterpret_cast(_T("")), 0); + return f_mount(&fs, reinterpret_cast(_T("")), 0); } } /* namespace */ @@ -44,33 +44,33 @@ FRESULT mount() { Signal status_signal; void poll_inserted() { - const auto card_present_now = sdcIsCardInserted(&SDCD1); - if( card_present_now != card_present ) { - card_present = card_present_now; - - Status new_status { card_present ? Status::Present : Status::NotPresent }; - - if( card_present ) { - if( sdcConnect(&SDCD1) == CH_SUCCESS ) { - if( mount() == FR_OK ) { - new_status = Status::Mounted; - } else { - new_status = Status::MountError; - } - } else { - new_status = Status::ConnectError; - } - } else { - sdcDisconnect(&SDCD1); - } - - status_ = new_status; - status_signal.emit(status_); - } + const auto card_present_now = sdcIsCardInserted(&SDCD1); + if (card_present_now != card_present) { + card_present = card_present_now; + + Status new_status{card_present ? Status::Present : Status::NotPresent}; + + if (card_present) { + if (sdcConnect(&SDCD1) == CH_SUCCESS) { + if (mount() == FR_OK) { + new_status = Status::Mounted; + } else { + new_status = Status::MountError; + } + } else { + new_status = Status::ConnectError; + } + } else { + sdcDisconnect(&SDCD1); + } + + status_ = new_status; + status_signal.emit(status_); + } } Status status() { - return status_; + return status_; } } /* namespace sd_card */ diff --git a/firmware/application/sd_card.hpp b/firmware/application/sd_card.hpp index 8fbbc7ae5..f449baf29 100644 --- a/firmware/application/sd_card.hpp +++ b/firmware/application/sd_card.hpp @@ -28,16 +28,16 @@ #include "signal.hpp" namespace sd_card { - + extern FATFS fs; enum class Status : int32_t { - IOError = -3, - MountError = -2, - ConnectError = -1, - NotPresent = 0, - Present = 1, - Mounted = 2, + IOError = -3, + MountError = -2, + ConnectError = -1, + NotPresent = 0, + Present = 1, + Mounted = 2, }; extern Signal status_signal; @@ -47,4 +47,4 @@ Status status(); } /* namespace sd_card */ -#endif/*__SD_CARD_H__*/ +#endif /*__SD_CARD_H__*/ diff --git a/firmware/application/serializer.cpp b/firmware/application/serializer.cpp index 38c896753..4c26b44cc 100644 --- a/firmware/application/serializer.cpp +++ b/firmware/application/serializer.cpp @@ -27,7 +27,7 @@ using namespace portapack; namespace serializer { - + /* Raw: 00110110100111 * NRZ-L: 00110110100111 * NRZ-M: 00100100111010 (1 = transition) @@ -40,12 +40,12 @@ namespace serializer { */ size_t symbol_count(const serial_format_t& serial_format) { - size_t count; - - count = 1 + serial_format.data_bits + serial_format.stop_bits; // Start + data + stop - if (serial_format.parity) count++; - - return count; + size_t count; + + count = 1 + serial_format.data_bits + serial_format.stop_bits; // Start + data + stop + if (serial_format.parity) count++; + + return count; }; } /* namespace serializer */ diff --git a/firmware/application/serializer.hpp b/firmware/application/serializer.hpp index 124dd69e6..9eea75fcc 100644 --- a/firmware/application/serializer.hpp +++ b/firmware/application/serializer.hpp @@ -30,38 +30,37 @@ namespace serializer { enum parity_enum : uint8_t { - NONE = 0, - EVEN = 1, - ODD = 2 + NONE = 0, + EVEN = 1, + ODD = 2 }; enum order_enum : uint8_t { - MSB_FIRST = 0, - LSB_FIRST = 1 + MSB_FIRST = 0, + LSB_FIRST = 1 }; struct serial_format_t { - uint8_t data_bits; - parity_enum parity; - uint8_t stop_bits; - order_enum bit_order; + uint8_t data_bits; + parity_enum parity; + uint8_t stop_bits; + order_enum bit_order; - constexpr serial_format_t() : - data_bits(7), - parity(parity_enum::EVEN), - stop_bits(1), - bit_order(order_enum::LSB_FIRST) - { - } + constexpr serial_format_t() + : data_bits(7), + parity(parity_enum::EVEN), + stop_bits(1), + bit_order(order_enum::LSB_FIRST) { + } }; size_t symbol_count(const serial_format_t& serial_format); - /*{ "7-Even-1 R", "7E1", 7, EVEN, 1, false, false }, - { "7E1 LUT ", "7Ea", 7, EVEN, 1, true, true }, - { "7-Odd-1 ", "7o1", 7, ODD, 1, true, false }, - { "8-Even-0 ", "8E0", 8, EVEN, 1, true, false }*/ +/*{ "7-Even-1 R", "7E1", 7, EVEN, 1, false, false }, + { "7E1 LUT ", "7Ea", 7, EVEN, 1, true, true }, + { "7-Odd-1 ", "7o1", 7, ODD, 1, true, false }, + { "8-Even-0 ", "8E0", 8, EVEN, 1, true, false }*/ } /* namespace serializer */ -#endif/*__SERIALIZER_H__*/ +#endif /*__SERIALIZER_H__*/ diff --git a/firmware/application/signal.hpp b/firmware/application/signal.hpp index 262e47983..a2e8f3aad 100644 --- a/firmware/application/signal.hpp +++ b/firmware/application/signal.hpp @@ -39,47 +39,46 @@ using SignalToken = uint32_t; -template +template struct Signal { - using Callback = std::function; + using Callback = std::function; - SignalToken operator+=(const Callback& callback) { - const SignalToken token = next_token++; - entries.emplace_back(std::make_unique(callback, token)); - return token; - } + SignalToken operator+=(const Callback& callback) { + const SignalToken token = next_token++; + entries.emplace_back(std::make_unique(callback, token)); + return token; + } - bool operator-=(const SignalToken token) { - entries.remove_if([token](EntryType& entry) { - return entry.get()->token == token; - }); - return true; - } + bool operator-=(const SignalToken token) { + entries.remove_if([token](EntryType& entry) { + return entry.get()->token == token; + }); + return true; + } - void emit(Args... args) { - for(auto& entry : entries) { - entry.get()->callback(args...); - }; - } + void emit(Args... args) { + for (auto& entry : entries) { + entry.get()->callback(args...); + }; + } -private: - struct CallbackEntry { - const Callback callback; - const SignalToken token; + private: + struct CallbackEntry { + const Callback callback; + const SignalToken token; - constexpr CallbackEntry( - const Callback& callback, - const SignalToken token - ) : callback { callback }, - token { token } - { - } - }; + constexpr CallbackEntry( + const Callback& callback, + const SignalToken token) + : callback{callback}, + token{token} { + } + }; - using EntryType = std::unique_ptr; - - std::list entries { }; - SignalToken next_token = 1; + using EntryType = std::unique_ptr; + + std::list entries{}; + SignalToken next_token = 1; }; -#endif/*__SIGNAL_H__*/ +#endif /*__SIGNAL_H__*/ diff --git a/firmware/application/spectrum_color_lut.cpp b/firmware/application/spectrum_color_lut.cpp index e78a5322f..a2877542e 100644 --- a/firmware/application/spectrum_color_lut.cpp +++ b/firmware/application/spectrum_color_lut.cpp @@ -21,779 +21,779 @@ #include "spectrum_color_lut.hpp" -const std::array spectrum_rgb2_lut { { - { 0, 0, 128 }, - { 0, 0, 132 }, - { 0, 0, 136 }, - { 0, 0, 140 }, - { 0, 0, 144 }, - { 0, 0, 148 }, - { 0, 0, 152 }, - { 0, 0, 156 }, - { 0, 0, 160 }, - { 0, 0, 164 }, - { 0, 0, 168 }, - { 0, 0, 172 }, - { 0, 0, 176 }, - { 0, 0, 180 }, - { 0, 0, 184 }, - { 0, 0, 188 }, - { 0, 0, 192 }, - { 0, 0, 195 }, - { 0, 0, 199 }, - { 0, 0, 203 }, - { 0, 0, 207 }, - { 0, 0, 211 }, - { 0, 0, 215 }, - { 0, 0, 219 }, - { 0, 0, 223 }, - { 0, 0, 227 }, - { 0, 0, 231 }, - { 0, 0, 235 }, - { 0, 0, 239 }, - { 0, 0, 243 }, - { 0, 0, 247 }, - { 0, 0, 251 }, - { 0, 0, 255 }, - { 0, 4, 255 }, - { 0, 8, 255 }, - { 0, 12, 255 }, - { 0, 16, 255 }, - { 0, 20, 255 }, - { 0, 24, 255 }, - { 0, 28, 255 }, - { 0, 32, 255 }, - { 0, 36, 255 }, - { 0, 40, 255 }, - { 0, 44, 255 }, - { 0, 48, 255 }, - { 0, 52, 255 }, - { 0, 56, 255 }, - { 0, 60, 255 }, - { 0, 64, 255 }, - { 0, 68, 255 }, - { 0, 72, 255 }, - { 0, 76, 255 }, - { 0, 80, 255 }, - { 0, 84, 255 }, - { 0, 88, 255 }, - { 0, 92, 255 }, - { 0, 96, 255 }, - { 0, 100, 255 }, - { 0, 104, 255 }, - { 0, 108, 255 }, - { 0, 112, 255 }, - { 0, 116, 255 }, - { 0, 120, 255 }, - { 0, 124, 255 }, - { 0, 128, 255 }, - { 0, 131, 255 }, - { 0, 135, 255 }, - { 0, 139, 255 }, - { 0, 143, 255 }, - { 0, 147, 255 }, - { 0, 151, 255 }, - { 0, 155, 255 }, - { 0, 159, 255 }, - { 0, 163, 255 }, - { 0, 167, 255 }, - { 0, 171, 255 }, - { 0, 175, 255 }, - { 0, 179, 255 }, - { 0, 183, 255 }, - { 0, 187, 255 }, - { 0, 191, 255 }, - { 0, 195, 255 }, - { 0, 199, 255 }, - { 0, 203, 255 }, - { 0, 207, 255 }, - { 0, 211, 255 }, - { 0, 215, 255 }, - { 0, 219, 255 }, - { 0, 223, 255 }, - { 0, 227, 255 }, - { 0, 231, 255 }, - { 0, 235, 255 }, - { 0, 239, 255 }, - { 0, 243, 255 }, - { 0, 247, 255 }, - { 0, 251, 255 }, - { 0, 255, 255 }, - { 4, 255, 251 }, - { 8, 255, 247 }, - { 12, 255, 243 }, - { 16, 255, 239 }, - { 20, 255, 235 }, - { 24, 255, 231 }, - { 28, 255, 227 }, - { 32, 255, 223 }, - { 36, 255, 219 }, - { 40, 255, 215 }, - { 44, 255, 211 }, - { 48, 255, 207 }, - { 52, 255, 203 }, - { 56, 255, 199 }, - { 60, 255, 195 }, - { 64, 255, 191 }, - { 68, 255, 187 }, - { 72, 255, 183 }, - { 76, 255, 179 }, - { 80, 255, 175 }, - { 84, 255, 171 }, - { 88, 255, 167 }, - { 92, 255, 163 }, - { 96, 255, 159 }, - { 100, 255, 155 }, - { 104, 255, 151 }, - { 108, 255, 147 }, - { 112, 255, 143 }, - { 116, 255, 139 }, - { 120, 255, 135 }, - { 124, 255, 131 }, - { 128, 255, 128 }, - { 131, 255, 124 }, - { 135, 255, 120 }, - { 139, 255, 116 }, - { 143, 255, 112 }, - { 147, 255, 108 }, - { 151, 255, 104 }, - { 155, 255, 100 }, - { 159, 255, 96 }, - { 163, 255, 92 }, - { 167, 255, 88 }, - { 171, 255, 84 }, - { 175, 255, 80 }, - { 179, 255, 76 }, - { 183, 255, 72 }, - { 187, 255, 68 }, - { 191, 255, 64 }, - { 195, 255, 60 }, - { 199, 255, 56 }, - { 203, 255, 52 }, - { 207, 255, 48 }, - { 211, 255, 44 }, - { 215, 255, 40 }, - { 219, 255, 36 }, - { 223, 255, 32 }, - { 227, 255, 28 }, - { 231, 255, 24 }, - { 235, 255, 20 }, - { 239, 255, 16 }, - { 243, 255, 12 }, - { 247, 255, 8 }, - { 251, 255, 4 }, - { 255, 255, 0 }, - { 255, 251, 0 }, - { 255, 247, 0 }, - { 255, 243, 0 }, - { 255, 239, 0 }, - { 255, 235, 0 }, - { 255, 231, 0 }, - { 255, 227, 0 }, - { 255, 223, 0 }, - { 255, 219, 0 }, - { 255, 215, 0 }, - { 255, 211, 0 }, - { 255, 207, 0 }, - { 255, 203, 0 }, - { 255, 199, 0 }, - { 255, 195, 0 }, - { 255, 191, 0 }, - { 255, 187, 0 }, - { 255, 183, 0 }, - { 255, 179, 0 }, - { 255, 175, 0 }, - { 255, 171, 0 }, - { 255, 167, 0 }, - { 255, 163, 0 }, - { 255, 159, 0 }, - { 255, 155, 0 }, - { 255, 151, 0 }, - { 255, 147, 0 }, - { 255, 143, 0 }, - { 255, 139, 0 }, - { 255, 135, 0 }, - { 255, 131, 0 }, - { 255, 128, 0 }, - { 255, 124, 0 }, - { 255, 120, 0 }, - { 255, 116, 0 }, - { 255, 112, 0 }, - { 255, 108, 0 }, - { 255, 104, 0 }, - { 255, 100, 0 }, - { 255, 96, 0 }, - { 255, 92, 0 }, - { 255, 88, 0 }, - { 255, 84, 0 }, - { 255, 80, 0 }, - { 255, 76, 0 }, - { 255, 72, 0 }, - { 255, 68, 0 }, - { 255, 64, 0 }, - { 255, 60, 0 }, - { 255, 56, 0 }, - { 255, 52, 0 }, - { 255, 48, 0 }, - { 255, 44, 0 }, - { 255, 40, 0 }, - { 255, 36, 0 }, - { 255, 32, 0 }, - { 255, 28, 0 }, - { 255, 24, 0 }, - { 255, 20, 0 }, - { 255, 16, 0 }, - { 255, 12, 0 }, - { 255, 8, 0 }, - { 255, 4, 0 }, - { 255, 0, 0 }, - { 251, 0, 0 }, - { 247, 0, 0 }, - { 243, 0, 0 }, - { 239, 0, 0 }, - { 235, 0, 0 }, - { 231, 0, 0 }, - { 227, 0, 0 }, - { 223, 0, 0 }, - { 219, 0, 0 }, - { 215, 0, 0 }, - { 211, 0, 0 }, - { 207, 0, 0 }, - { 203, 0, 0 }, - { 199, 0, 0 }, - { 195, 0, 0 }, - { 192, 0, 0 }, - { 188, 0, 0 }, - { 184, 0, 0 }, - { 180, 0, 0 }, - { 176, 0, 0 }, - { 172, 0, 0 }, - { 168, 0, 0 }, - { 164, 0, 0 }, - { 160, 0, 0 }, - { 156, 0, 0 }, - { 152, 0, 0 }, - { 148, 0, 0 }, - { 144, 0, 0 }, - { 140, 0, 0 }, - { 136, 0, 0 }, - { 132, 0, 0 }, -} }; +const std::array spectrum_rgb2_lut{{ + {0, 0, 128}, + {0, 0, 132}, + {0, 0, 136}, + {0, 0, 140}, + {0, 0, 144}, + {0, 0, 148}, + {0, 0, 152}, + {0, 0, 156}, + {0, 0, 160}, + {0, 0, 164}, + {0, 0, 168}, + {0, 0, 172}, + {0, 0, 176}, + {0, 0, 180}, + {0, 0, 184}, + {0, 0, 188}, + {0, 0, 192}, + {0, 0, 195}, + {0, 0, 199}, + {0, 0, 203}, + {0, 0, 207}, + {0, 0, 211}, + {0, 0, 215}, + {0, 0, 219}, + {0, 0, 223}, + {0, 0, 227}, + {0, 0, 231}, + {0, 0, 235}, + {0, 0, 239}, + {0, 0, 243}, + {0, 0, 247}, + {0, 0, 251}, + {0, 0, 255}, + {0, 4, 255}, + {0, 8, 255}, + {0, 12, 255}, + {0, 16, 255}, + {0, 20, 255}, + {0, 24, 255}, + {0, 28, 255}, + {0, 32, 255}, + {0, 36, 255}, + {0, 40, 255}, + {0, 44, 255}, + {0, 48, 255}, + {0, 52, 255}, + {0, 56, 255}, + {0, 60, 255}, + {0, 64, 255}, + {0, 68, 255}, + {0, 72, 255}, + {0, 76, 255}, + {0, 80, 255}, + {0, 84, 255}, + {0, 88, 255}, + {0, 92, 255}, + {0, 96, 255}, + {0, 100, 255}, + {0, 104, 255}, + {0, 108, 255}, + {0, 112, 255}, + {0, 116, 255}, + {0, 120, 255}, + {0, 124, 255}, + {0, 128, 255}, + {0, 131, 255}, + {0, 135, 255}, + {0, 139, 255}, + {0, 143, 255}, + {0, 147, 255}, + {0, 151, 255}, + {0, 155, 255}, + {0, 159, 255}, + {0, 163, 255}, + {0, 167, 255}, + {0, 171, 255}, + {0, 175, 255}, + {0, 179, 255}, + {0, 183, 255}, + {0, 187, 255}, + {0, 191, 255}, + {0, 195, 255}, + {0, 199, 255}, + {0, 203, 255}, + {0, 207, 255}, + {0, 211, 255}, + {0, 215, 255}, + {0, 219, 255}, + {0, 223, 255}, + {0, 227, 255}, + {0, 231, 255}, + {0, 235, 255}, + {0, 239, 255}, + {0, 243, 255}, + {0, 247, 255}, + {0, 251, 255}, + {0, 255, 255}, + {4, 255, 251}, + {8, 255, 247}, + {12, 255, 243}, + {16, 255, 239}, + {20, 255, 235}, + {24, 255, 231}, + {28, 255, 227}, + {32, 255, 223}, + {36, 255, 219}, + {40, 255, 215}, + {44, 255, 211}, + {48, 255, 207}, + {52, 255, 203}, + {56, 255, 199}, + {60, 255, 195}, + {64, 255, 191}, + {68, 255, 187}, + {72, 255, 183}, + {76, 255, 179}, + {80, 255, 175}, + {84, 255, 171}, + {88, 255, 167}, + {92, 255, 163}, + {96, 255, 159}, + {100, 255, 155}, + {104, 255, 151}, + {108, 255, 147}, + {112, 255, 143}, + {116, 255, 139}, + {120, 255, 135}, + {124, 255, 131}, + {128, 255, 128}, + {131, 255, 124}, + {135, 255, 120}, + {139, 255, 116}, + {143, 255, 112}, + {147, 255, 108}, + {151, 255, 104}, + {155, 255, 100}, + {159, 255, 96}, + {163, 255, 92}, + {167, 255, 88}, + {171, 255, 84}, + {175, 255, 80}, + {179, 255, 76}, + {183, 255, 72}, + {187, 255, 68}, + {191, 255, 64}, + {195, 255, 60}, + {199, 255, 56}, + {203, 255, 52}, + {207, 255, 48}, + {211, 255, 44}, + {215, 255, 40}, + {219, 255, 36}, + {223, 255, 32}, + {227, 255, 28}, + {231, 255, 24}, + {235, 255, 20}, + {239, 255, 16}, + {243, 255, 12}, + {247, 255, 8}, + {251, 255, 4}, + {255, 255, 0}, + {255, 251, 0}, + {255, 247, 0}, + {255, 243, 0}, + {255, 239, 0}, + {255, 235, 0}, + {255, 231, 0}, + {255, 227, 0}, + {255, 223, 0}, + {255, 219, 0}, + {255, 215, 0}, + {255, 211, 0}, + {255, 207, 0}, + {255, 203, 0}, + {255, 199, 0}, + {255, 195, 0}, + {255, 191, 0}, + {255, 187, 0}, + {255, 183, 0}, + {255, 179, 0}, + {255, 175, 0}, + {255, 171, 0}, + {255, 167, 0}, + {255, 163, 0}, + {255, 159, 0}, + {255, 155, 0}, + {255, 151, 0}, + {255, 147, 0}, + {255, 143, 0}, + {255, 139, 0}, + {255, 135, 0}, + {255, 131, 0}, + {255, 128, 0}, + {255, 124, 0}, + {255, 120, 0}, + {255, 116, 0}, + {255, 112, 0}, + {255, 108, 0}, + {255, 104, 0}, + {255, 100, 0}, + {255, 96, 0}, + {255, 92, 0}, + {255, 88, 0}, + {255, 84, 0}, + {255, 80, 0}, + {255, 76, 0}, + {255, 72, 0}, + {255, 68, 0}, + {255, 64, 0}, + {255, 60, 0}, + {255, 56, 0}, + {255, 52, 0}, + {255, 48, 0}, + {255, 44, 0}, + {255, 40, 0}, + {255, 36, 0}, + {255, 32, 0}, + {255, 28, 0}, + {255, 24, 0}, + {255, 20, 0}, + {255, 16, 0}, + {255, 12, 0}, + {255, 8, 0}, + {255, 4, 0}, + {255, 0, 0}, + {251, 0, 0}, + {247, 0, 0}, + {243, 0, 0}, + {239, 0, 0}, + {235, 0, 0}, + {231, 0, 0}, + {227, 0, 0}, + {223, 0, 0}, + {219, 0, 0}, + {215, 0, 0}, + {211, 0, 0}, + {207, 0, 0}, + {203, 0, 0}, + {199, 0, 0}, + {195, 0, 0}, + {192, 0, 0}, + {188, 0, 0}, + {184, 0, 0}, + {180, 0, 0}, + {176, 0, 0}, + {172, 0, 0}, + {168, 0, 0}, + {164, 0, 0}, + {160, 0, 0}, + {156, 0, 0}, + {152, 0, 0}, + {148, 0, 0}, + {144, 0, 0}, + {140, 0, 0}, + {136, 0, 0}, + {132, 0, 0}, +}}; -const std::array spectrum_rgb3_lut { { - { 0, 0, 0 }, - { 0, 0, 3 }, - { 0, 0, 6 }, - { 0, 0, 9 }, - { 0, 0, 12 }, - { 0, 0, 15 }, - { 0, 0, 18 }, - { 0, 0, 21 }, - { 0, 0, 24 }, - { 0, 0, 27 }, - { 0, 0, 30 }, - { 0, 0, 33 }, - { 0, 0, 36 }, - { 0, 0, 39 }, - { 0, 0, 42 }, - { 0, 0, 45 }, - { 0, 0, 48 }, - { 0, 0, 51 }, - { 0, 0, 54 }, - { 0, 0, 57 }, - { 0, 0, 60 }, - { 0, 0, 63 }, - { 0, 0, 66 }, - { 0, 0, 69 }, - { 0, 0, 72 }, - { 0, 0, 75 }, - { 0, 0, 78 }, - { 0, 0, 81 }, - { 0, 0, 84 }, - { 0, 0, 87 }, - { 0, 0, 90 }, - { 0, 0, 93 }, - { 0, 0, 96 }, - { 0, 0, 99 }, - { 0, 0, 102 }, - { 0, 0, 105 }, - { 0, 0, 108 }, - { 0, 0, 111 }, - { 0, 0, 114 }, - { 0, 0, 117 }, - { 0, 0, 120 }, - { 0, 0, 123 }, - { 0, 0, 126 }, - { 0, 0, 129 }, - { 0, 0, 132 }, - { 0, 0, 135 }, - { 0, 0, 138 }, - { 0, 0, 141 }, - { 0, 0, 144 }, - { 0, 0, 147 }, - { 0, 0, 150 }, - { 0, 0, 153 }, - { 0, 0, 156 }, - { 0, 0, 159 }, - { 0, 0, 162 }, - { 0, 0, 165 }, - { 0, 0, 168 }, - { 0, 0, 171 }, - { 0, 0, 174 }, - { 0, 0, 177 }, - { 0, 0, 180 }, - { 0, 0, 183 }, - { 0, 0, 186 }, - { 0, 0, 189 }, - { 0, 0, 192 }, - { 0, 0, 195 }, - { 0, 0, 198 }, - { 0, 0, 201 }, - { 0, 0, 204 }, - { 0, 0, 207 }, - { 0, 0, 210 }, - { 0, 0, 213 }, - { 0, 0, 216 }, - { 0, 0, 219 }, - { 0, 0, 222 }, - { 0, 0, 225 }, - { 0, 0, 228 }, - { 0, 0, 231 }, - { 0, 0, 234 }, - { 0, 0, 237 }, - { 0, 0, 240 }, - { 0, 0, 243 }, - { 0, 0, 246 }, - { 0, 0, 249 }, - { 0, 0, 252 }, - { 0, 0, 255 }, - { 0, 3, 252 }, - { 0, 6, 249 }, - { 0, 9, 246 }, - { 0, 12, 243 }, - { 0, 15, 240 }, - { 0, 18, 237 }, - { 0, 21, 234 }, - { 0, 24, 231 }, - { 0, 27, 228 }, - { 0, 30, 225 }, - { 0, 33, 222 }, - { 0, 36, 219 }, - { 0, 39, 216 }, - { 0, 42, 213 }, - { 0, 45, 210 }, - { 0, 48, 207 }, - { 0, 51, 204 }, - { 0, 54, 201 }, - { 0, 57, 198 }, - { 0, 60, 195 }, - { 0, 63, 192 }, - { 0, 66, 189 }, - { 0, 69, 186 }, - { 0, 72, 183 }, - { 0, 75, 180 }, - { 0, 78, 177 }, - { 0, 81, 174 }, - { 0, 84, 171 }, - { 0, 87, 168 }, - { 0, 90, 165 }, - { 0, 93, 162 }, - { 0, 96, 159 }, - { 0, 99, 156 }, - { 0, 102, 153 }, - { 0, 105, 150 }, - { 0, 108, 147 }, - { 0, 111, 144 }, - { 0, 114, 141 }, - { 0, 117, 138 }, - { 0, 120, 135 }, - { 0, 123, 132 }, - { 0, 126, 129 }, - { 0, 129, 126 }, - { 0, 132, 123 }, - { 0, 135, 120 }, - { 0, 138, 117 }, - { 0, 141, 114 }, - { 0, 144, 111 }, - { 0, 147, 108 }, - { 0, 150, 105 }, - { 0, 153, 102 }, - { 0, 156, 99 }, - { 0, 159, 96 }, - { 0, 162, 93 }, - { 0, 165, 90 }, - { 0, 168, 87 }, - { 0, 171, 84 }, - { 0, 174, 81 }, - { 0, 177, 78 }, - { 0, 180, 75 }, - { 0, 183, 72 }, - { 0, 186, 69 }, - { 0, 189, 66 }, - { 0, 192, 63 }, - { 0, 195, 60 }, - { 0, 198, 57 }, - { 0, 201, 54 }, - { 0, 204, 51 }, - { 0, 207, 48 }, - { 0, 210, 45 }, - { 0, 213, 42 }, - { 0, 216, 39 }, - { 0, 219, 36 }, - { 0, 222, 33 }, - { 0, 225, 30 }, - { 0, 228, 27 }, - { 0, 231, 24 }, - { 0, 234, 21 }, - { 0, 237, 18 }, - { 0, 240, 15 }, - { 0, 243, 12 }, - { 0, 246, 9 }, - { 0, 249, 6 }, - { 0, 252, 3 }, - { 0, 255, 0 }, - { 3, 252, 0 }, - { 6, 249, 0 }, - { 9, 246, 0 }, - { 12, 243, 0 }, - { 15, 240, 0 }, - { 18, 237, 0 }, - { 21, 234, 0 }, - { 24, 231, 0 }, - { 27, 228, 0 }, - { 30, 225, 0 }, - { 33, 222, 0 }, - { 36, 219, 0 }, - { 39, 216, 0 }, - { 42, 213, 0 }, - { 45, 210, 0 }, - { 48, 207, 0 }, - { 51, 204, 0 }, - { 54, 201, 0 }, - { 57, 198, 0 }, - { 60, 195, 0 }, - { 63, 192, 0 }, - { 66, 189, 0 }, - { 69, 186, 0 }, - { 72, 183, 0 }, - { 75, 180, 0 }, - { 78, 177, 0 }, - { 81, 174, 0 }, - { 84, 171, 0 }, - { 87, 168, 0 }, - { 90, 165, 0 }, - { 93, 162, 0 }, - { 96, 159, 0 }, - { 99, 156, 0 }, - { 102, 153, 0 }, - { 105, 150, 0 }, - { 108, 147, 0 }, - { 111, 144, 0 }, - { 114, 141, 0 }, - { 117, 138, 0 }, - { 120, 135, 0 }, - { 123, 132, 0 }, - { 126, 129, 0 }, - { 129, 126, 0 }, - { 132, 123, 0 }, - { 135, 120, 0 }, - { 138, 117, 0 }, - { 141, 114, 0 }, - { 144, 111, 0 }, - { 147, 108, 0 }, - { 150, 105, 0 }, - { 153, 102, 0 }, - { 156, 99, 0 }, - { 159, 96, 0 }, - { 162, 93, 0 }, - { 165, 90, 0 }, - { 168, 87, 0 }, - { 171, 84, 0 }, - { 174, 81, 0 }, - { 177, 78, 0 }, - { 180, 75, 0 }, - { 183, 72, 0 }, - { 186, 69, 0 }, - { 189, 66, 0 }, - { 192, 63, 0 }, - { 195, 60, 0 }, - { 198, 57, 0 }, - { 201, 54, 0 }, - { 204, 51, 0 }, - { 207, 48, 0 }, - { 210, 45, 0 }, - { 213, 42, 0 }, - { 216, 39, 0 }, - { 219, 36, 0 }, - { 222, 33, 0 }, - { 225, 30, 0 }, - { 228, 27, 0 }, - { 231, 24, 0 }, - { 234, 21, 0 }, - { 237, 18, 0 }, - { 240, 15, 0 }, - { 243, 12, 0 }, - { 246, 9, 0 }, - { 249, 6, 0 }, - { 252, 3, 0 }, - { 255, 0, 0 }, -} }; +const std::array spectrum_rgb3_lut{{ + {0, 0, 0}, + {0, 0, 3}, + {0, 0, 6}, + {0, 0, 9}, + {0, 0, 12}, + {0, 0, 15}, + {0, 0, 18}, + {0, 0, 21}, + {0, 0, 24}, + {0, 0, 27}, + {0, 0, 30}, + {0, 0, 33}, + {0, 0, 36}, + {0, 0, 39}, + {0, 0, 42}, + {0, 0, 45}, + {0, 0, 48}, + {0, 0, 51}, + {0, 0, 54}, + {0, 0, 57}, + {0, 0, 60}, + {0, 0, 63}, + {0, 0, 66}, + {0, 0, 69}, + {0, 0, 72}, + {0, 0, 75}, + {0, 0, 78}, + {0, 0, 81}, + {0, 0, 84}, + {0, 0, 87}, + {0, 0, 90}, + {0, 0, 93}, + {0, 0, 96}, + {0, 0, 99}, + {0, 0, 102}, + {0, 0, 105}, + {0, 0, 108}, + {0, 0, 111}, + {0, 0, 114}, + {0, 0, 117}, + {0, 0, 120}, + {0, 0, 123}, + {0, 0, 126}, + {0, 0, 129}, + {0, 0, 132}, + {0, 0, 135}, + {0, 0, 138}, + {0, 0, 141}, + {0, 0, 144}, + {0, 0, 147}, + {0, 0, 150}, + {0, 0, 153}, + {0, 0, 156}, + {0, 0, 159}, + {0, 0, 162}, + {0, 0, 165}, + {0, 0, 168}, + {0, 0, 171}, + {0, 0, 174}, + {0, 0, 177}, + {0, 0, 180}, + {0, 0, 183}, + {0, 0, 186}, + {0, 0, 189}, + {0, 0, 192}, + {0, 0, 195}, + {0, 0, 198}, + {0, 0, 201}, + {0, 0, 204}, + {0, 0, 207}, + {0, 0, 210}, + {0, 0, 213}, + {0, 0, 216}, + {0, 0, 219}, + {0, 0, 222}, + {0, 0, 225}, + {0, 0, 228}, + {0, 0, 231}, + {0, 0, 234}, + {0, 0, 237}, + {0, 0, 240}, + {0, 0, 243}, + {0, 0, 246}, + {0, 0, 249}, + {0, 0, 252}, + {0, 0, 255}, + {0, 3, 252}, + {0, 6, 249}, + {0, 9, 246}, + {0, 12, 243}, + {0, 15, 240}, + {0, 18, 237}, + {0, 21, 234}, + {0, 24, 231}, + {0, 27, 228}, + {0, 30, 225}, + {0, 33, 222}, + {0, 36, 219}, + {0, 39, 216}, + {0, 42, 213}, + {0, 45, 210}, + {0, 48, 207}, + {0, 51, 204}, + {0, 54, 201}, + {0, 57, 198}, + {0, 60, 195}, + {0, 63, 192}, + {0, 66, 189}, + {0, 69, 186}, + {0, 72, 183}, + {0, 75, 180}, + {0, 78, 177}, + {0, 81, 174}, + {0, 84, 171}, + {0, 87, 168}, + {0, 90, 165}, + {0, 93, 162}, + {0, 96, 159}, + {0, 99, 156}, + {0, 102, 153}, + {0, 105, 150}, + {0, 108, 147}, + {0, 111, 144}, + {0, 114, 141}, + {0, 117, 138}, + {0, 120, 135}, + {0, 123, 132}, + {0, 126, 129}, + {0, 129, 126}, + {0, 132, 123}, + {0, 135, 120}, + {0, 138, 117}, + {0, 141, 114}, + {0, 144, 111}, + {0, 147, 108}, + {0, 150, 105}, + {0, 153, 102}, + {0, 156, 99}, + {0, 159, 96}, + {0, 162, 93}, + {0, 165, 90}, + {0, 168, 87}, + {0, 171, 84}, + {0, 174, 81}, + {0, 177, 78}, + {0, 180, 75}, + {0, 183, 72}, + {0, 186, 69}, + {0, 189, 66}, + {0, 192, 63}, + {0, 195, 60}, + {0, 198, 57}, + {0, 201, 54}, + {0, 204, 51}, + {0, 207, 48}, + {0, 210, 45}, + {0, 213, 42}, + {0, 216, 39}, + {0, 219, 36}, + {0, 222, 33}, + {0, 225, 30}, + {0, 228, 27}, + {0, 231, 24}, + {0, 234, 21}, + {0, 237, 18}, + {0, 240, 15}, + {0, 243, 12}, + {0, 246, 9}, + {0, 249, 6}, + {0, 252, 3}, + {0, 255, 0}, + {3, 252, 0}, + {6, 249, 0}, + {9, 246, 0}, + {12, 243, 0}, + {15, 240, 0}, + {18, 237, 0}, + {21, 234, 0}, + {24, 231, 0}, + {27, 228, 0}, + {30, 225, 0}, + {33, 222, 0}, + {36, 219, 0}, + {39, 216, 0}, + {42, 213, 0}, + {45, 210, 0}, + {48, 207, 0}, + {51, 204, 0}, + {54, 201, 0}, + {57, 198, 0}, + {60, 195, 0}, + {63, 192, 0}, + {66, 189, 0}, + {69, 186, 0}, + {72, 183, 0}, + {75, 180, 0}, + {78, 177, 0}, + {81, 174, 0}, + {84, 171, 0}, + {87, 168, 0}, + {90, 165, 0}, + {93, 162, 0}, + {96, 159, 0}, + {99, 156, 0}, + {102, 153, 0}, + {105, 150, 0}, + {108, 147, 0}, + {111, 144, 0}, + {114, 141, 0}, + {117, 138, 0}, + {120, 135, 0}, + {123, 132, 0}, + {126, 129, 0}, + {129, 126, 0}, + {132, 123, 0}, + {135, 120, 0}, + {138, 117, 0}, + {141, 114, 0}, + {144, 111, 0}, + {147, 108, 0}, + {150, 105, 0}, + {153, 102, 0}, + {156, 99, 0}, + {159, 96, 0}, + {162, 93, 0}, + {165, 90, 0}, + {168, 87, 0}, + {171, 84, 0}, + {174, 81, 0}, + {177, 78, 0}, + {180, 75, 0}, + {183, 72, 0}, + {186, 69, 0}, + {189, 66, 0}, + {192, 63, 0}, + {195, 60, 0}, + {198, 57, 0}, + {201, 54, 0}, + {204, 51, 0}, + {207, 48, 0}, + {210, 45, 0}, + {213, 42, 0}, + {216, 39, 0}, + {219, 36, 0}, + {222, 33, 0}, + {225, 30, 0}, + {228, 27, 0}, + {231, 24, 0}, + {234, 21, 0}, + {237, 18, 0}, + {240, 15, 0}, + {243, 12, 0}, + {246, 9, 0}, + {249, 6, 0}, + {252, 3, 0}, + {255, 0, 0}, +}}; -const std::array spectrum_rgb4_lut { { - { 0, 0, 0 }, - { 1, 1, 1 }, - { 2, 2, 2 }, - { 3, 3, 3 }, - { 4, 4, 4 }, - { 5, 5, 5 }, - { 6, 6, 6 }, - { 7, 7, 7 }, - { 8, 8, 8 }, - { 9, 9, 9 }, - { 10, 10, 10 }, - { 11, 11, 11 }, - { 12, 12, 12 }, - { 13, 13, 13 }, - { 14, 14, 14 }, - { 15, 15, 15 }, - { 16, 16, 16 }, - { 17, 17, 17 }, - { 18, 18, 18 }, - { 19, 19, 19 }, - { 20, 20, 20 }, - { 21, 21, 21 }, - { 22, 22, 22 }, - { 23, 23, 23 }, - { 24, 24, 24 }, - { 25, 25, 25 }, - { 26, 26, 26 }, - { 27, 27, 27 }, - { 28, 28, 28 }, - { 29, 29, 29 }, - { 30, 30, 30 }, - { 31, 31, 31 }, - { 32, 32, 32 }, - { 33, 33, 33 }, - { 34, 34, 34 }, - { 35, 35, 35 }, - { 36, 36, 36 }, - { 37, 37, 37 }, - { 38, 38, 38 }, - { 39, 39, 39 }, - { 40, 40, 40 }, - { 41, 41, 41 }, - { 42, 42, 42 }, - { 43, 43, 43 }, - { 44, 44, 44 }, - { 45, 45, 45 }, - { 46, 46, 46 }, - { 47, 47, 47 }, - { 48, 48, 48 }, - { 49, 49, 49 }, - { 50, 50, 50 }, - { 51, 51, 51 }, - { 52, 52, 52 }, - { 53, 53, 53 }, - { 54, 54, 54 }, - { 55, 55, 55 }, - { 56, 56, 56 }, - { 57, 57, 57 }, - { 58, 58, 58 }, - { 59, 59, 59 }, - { 60, 60, 60 }, - { 61, 61, 61 }, - { 62, 62, 62 }, - { 63, 63, 63 }, - { 64, 64, 64 }, - { 65, 65, 65 }, - { 66, 66, 66 }, - { 67, 67, 67 }, - { 68, 68, 68 }, - { 69, 69, 69 }, - { 70, 70, 70 }, - { 71, 71, 71 }, - { 72, 72, 72 }, - { 73, 73, 73 }, - { 74, 74, 74 }, - { 75, 75, 75 }, - { 76, 76, 76 }, - { 77, 77, 77 }, - { 78, 78, 78 }, - { 79, 79, 79 }, - { 80, 80, 80 }, - { 81, 81, 81 }, - { 82, 82, 82 }, - { 83, 83, 83 }, - { 84, 84, 84 }, - { 85, 85, 85 }, - { 86, 86, 86 }, - { 87, 87, 87 }, - { 88, 88, 88 }, - { 89, 89, 89 }, - { 90, 90, 90 }, - { 91, 91, 91 }, - { 92, 92, 92 }, - { 93, 93, 93 }, - { 94, 94, 94 }, - { 95, 95, 95 }, - { 96, 96, 96 }, - { 97, 97, 97 }, - { 98, 98, 98 }, - { 99, 99, 99 }, - { 100, 100, 100 }, - { 101, 101, 101 }, - { 102, 102, 102 }, - { 103, 103, 103 }, - { 104, 104, 104 }, - { 105, 105, 105 }, - { 106, 106, 106 }, - { 107, 107, 107 }, - { 108, 108, 108 }, - { 109, 109, 109 }, - { 110, 110, 110 }, - { 111, 111, 111 }, - { 112, 112, 112 }, - { 113, 113, 113 }, - { 114, 114, 114 }, - { 115, 115, 115 }, - { 116, 116, 116 }, - { 117, 117, 117 }, - { 118, 118, 118 }, - { 119, 119, 119 }, - { 120, 120, 120 }, - { 121, 121, 121 }, - { 122, 122, 122 }, - { 123, 123, 123 }, - { 124, 124, 124 }, - { 125, 125, 125 }, - { 126, 126, 126 }, - { 127, 127, 127 }, - { 128, 128, 128 }, - { 129, 129, 129 }, - { 130, 130, 130 }, - { 131, 131, 131 }, - { 132, 132, 132 }, - { 133, 133, 133 }, - { 134, 134, 134 }, - { 135, 135, 135 }, - { 136, 136, 136 }, - { 137, 137, 137 }, - { 138, 138, 138 }, - { 139, 139, 139 }, - { 140, 140, 140 }, - { 141, 141, 141 }, - { 142, 142, 142 }, - { 143, 143, 143 }, - { 144, 144, 144 }, - { 145, 145, 145 }, - { 146, 146, 146 }, - { 147, 147, 147 }, - { 148, 148, 148 }, - { 149, 149, 149 }, - { 150, 150, 150 }, - { 151, 151, 151 }, - { 152, 152, 152 }, - { 153, 153, 153 }, - { 154, 154, 154 }, - { 155, 155, 155 }, - { 156, 156, 156 }, - { 157, 157, 157 }, - { 158, 158, 158 }, - { 159, 159, 159 }, - { 160, 160, 160 }, - { 161, 161, 161 }, - { 162, 162, 162 }, - { 163, 163, 163 }, - { 164, 164, 164 }, - { 165, 165, 165 }, - { 166, 166, 166 }, - { 167, 167, 167 }, - { 168, 168, 168 }, - { 169, 169, 169 }, - { 170, 170, 170 }, - { 171, 171, 171 }, - { 172, 172, 172 }, - { 173, 173, 173 }, - { 174, 174, 174 }, - { 175, 175, 175 }, - { 176, 176, 176 }, - { 177, 177, 177 }, - { 178, 178, 178 }, - { 179, 179, 179 }, - { 180, 180, 180 }, - { 181, 181, 181 }, - { 182, 182, 182 }, - { 183, 183, 183 }, - { 184, 184, 184 }, - { 185, 185, 185 }, - { 186, 186, 186 }, - { 187, 187, 187 }, - { 188, 188, 188 }, - { 189, 189, 189 }, - { 190, 190, 190 }, - { 191, 191, 191 }, - { 192, 192, 192 }, - { 193, 193, 193 }, - { 194, 194, 194 }, - { 195, 195, 195 }, - { 196, 196, 196 }, - { 197, 197, 197 }, - { 198, 198, 198 }, - { 199, 199, 199 }, - { 200, 200, 200 }, - { 201, 201, 201 }, - { 202, 202, 202 }, - { 203, 203, 203 }, - { 204, 204, 204 }, - { 205, 205, 205 }, - { 206, 206, 206 }, - { 207, 207, 207 }, - { 208, 208, 208 }, - { 209, 209, 209 }, - { 210, 210, 210 }, - { 211, 211, 211 }, - { 212, 212, 212 }, - { 213, 213, 213 }, - { 214, 214, 214 }, - { 215, 215, 215 }, - { 216, 216, 216 }, - { 217, 217, 217 }, - { 218, 218, 218 }, - { 219, 219, 219 }, - { 220, 220, 220 }, - { 221, 221, 221 }, - { 222, 222, 222 }, - { 223, 223, 223 }, - { 224, 224, 224 }, - { 225, 225, 225 }, - { 226, 226, 226 }, - { 227, 227, 227 }, - { 228, 228, 228 }, - { 229, 229, 229 }, - { 230, 230, 230 }, - { 231, 231, 231 }, - { 232, 232, 232 }, - { 233, 233, 233 }, - { 234, 234, 234 }, - { 235, 235, 235 }, - { 236, 236, 236 }, - { 237, 237, 237 }, - { 238, 238, 238 }, - { 239, 239, 239 }, - { 240, 240, 240 }, - { 241, 241, 241 }, - { 242, 242, 242 }, - { 243, 243, 243 }, - { 244, 244, 244 }, - { 245, 245, 245 }, - { 246, 246, 246 }, - { 247, 247, 247 }, - { 248, 248, 248 }, - { 249, 249, 249 }, - { 250, 250, 250 }, - { 251, 251, 251 }, - { 252, 252, 252 }, - { 253, 253, 253 }, - { 254, 254, 254 }, - { 255, 255, 255 }, -} }; +const std::array spectrum_rgb4_lut{{ + {0, 0, 0}, + {1, 1, 1}, + {2, 2, 2}, + {3, 3, 3}, + {4, 4, 4}, + {5, 5, 5}, + {6, 6, 6}, + {7, 7, 7}, + {8, 8, 8}, + {9, 9, 9}, + {10, 10, 10}, + {11, 11, 11}, + {12, 12, 12}, + {13, 13, 13}, + {14, 14, 14}, + {15, 15, 15}, + {16, 16, 16}, + {17, 17, 17}, + {18, 18, 18}, + {19, 19, 19}, + {20, 20, 20}, + {21, 21, 21}, + {22, 22, 22}, + {23, 23, 23}, + {24, 24, 24}, + {25, 25, 25}, + {26, 26, 26}, + {27, 27, 27}, + {28, 28, 28}, + {29, 29, 29}, + {30, 30, 30}, + {31, 31, 31}, + {32, 32, 32}, + {33, 33, 33}, + {34, 34, 34}, + {35, 35, 35}, + {36, 36, 36}, + {37, 37, 37}, + {38, 38, 38}, + {39, 39, 39}, + {40, 40, 40}, + {41, 41, 41}, + {42, 42, 42}, + {43, 43, 43}, + {44, 44, 44}, + {45, 45, 45}, + {46, 46, 46}, + {47, 47, 47}, + {48, 48, 48}, + {49, 49, 49}, + {50, 50, 50}, + {51, 51, 51}, + {52, 52, 52}, + {53, 53, 53}, + {54, 54, 54}, + {55, 55, 55}, + {56, 56, 56}, + {57, 57, 57}, + {58, 58, 58}, + {59, 59, 59}, + {60, 60, 60}, + {61, 61, 61}, + {62, 62, 62}, + {63, 63, 63}, + {64, 64, 64}, + {65, 65, 65}, + {66, 66, 66}, + {67, 67, 67}, + {68, 68, 68}, + {69, 69, 69}, + {70, 70, 70}, + {71, 71, 71}, + {72, 72, 72}, + {73, 73, 73}, + {74, 74, 74}, + {75, 75, 75}, + {76, 76, 76}, + {77, 77, 77}, + {78, 78, 78}, + {79, 79, 79}, + {80, 80, 80}, + {81, 81, 81}, + {82, 82, 82}, + {83, 83, 83}, + {84, 84, 84}, + {85, 85, 85}, + {86, 86, 86}, + {87, 87, 87}, + {88, 88, 88}, + {89, 89, 89}, + {90, 90, 90}, + {91, 91, 91}, + {92, 92, 92}, + {93, 93, 93}, + {94, 94, 94}, + {95, 95, 95}, + {96, 96, 96}, + {97, 97, 97}, + {98, 98, 98}, + {99, 99, 99}, + {100, 100, 100}, + {101, 101, 101}, + {102, 102, 102}, + {103, 103, 103}, + {104, 104, 104}, + {105, 105, 105}, + {106, 106, 106}, + {107, 107, 107}, + {108, 108, 108}, + {109, 109, 109}, + {110, 110, 110}, + {111, 111, 111}, + {112, 112, 112}, + {113, 113, 113}, + {114, 114, 114}, + {115, 115, 115}, + {116, 116, 116}, + {117, 117, 117}, + {118, 118, 118}, + {119, 119, 119}, + {120, 120, 120}, + {121, 121, 121}, + {122, 122, 122}, + {123, 123, 123}, + {124, 124, 124}, + {125, 125, 125}, + {126, 126, 126}, + {127, 127, 127}, + {128, 128, 128}, + {129, 129, 129}, + {130, 130, 130}, + {131, 131, 131}, + {132, 132, 132}, + {133, 133, 133}, + {134, 134, 134}, + {135, 135, 135}, + {136, 136, 136}, + {137, 137, 137}, + {138, 138, 138}, + {139, 139, 139}, + {140, 140, 140}, + {141, 141, 141}, + {142, 142, 142}, + {143, 143, 143}, + {144, 144, 144}, + {145, 145, 145}, + {146, 146, 146}, + {147, 147, 147}, + {148, 148, 148}, + {149, 149, 149}, + {150, 150, 150}, + {151, 151, 151}, + {152, 152, 152}, + {153, 153, 153}, + {154, 154, 154}, + {155, 155, 155}, + {156, 156, 156}, + {157, 157, 157}, + {158, 158, 158}, + {159, 159, 159}, + {160, 160, 160}, + {161, 161, 161}, + {162, 162, 162}, + {163, 163, 163}, + {164, 164, 164}, + {165, 165, 165}, + {166, 166, 166}, + {167, 167, 167}, + {168, 168, 168}, + {169, 169, 169}, + {170, 170, 170}, + {171, 171, 171}, + {172, 172, 172}, + {173, 173, 173}, + {174, 174, 174}, + {175, 175, 175}, + {176, 176, 176}, + {177, 177, 177}, + {178, 178, 178}, + {179, 179, 179}, + {180, 180, 180}, + {181, 181, 181}, + {182, 182, 182}, + {183, 183, 183}, + {184, 184, 184}, + {185, 185, 185}, + {186, 186, 186}, + {187, 187, 187}, + {188, 188, 188}, + {189, 189, 189}, + {190, 190, 190}, + {191, 191, 191}, + {192, 192, 192}, + {193, 193, 193}, + {194, 194, 194}, + {195, 195, 195}, + {196, 196, 196}, + {197, 197, 197}, + {198, 198, 198}, + {199, 199, 199}, + {200, 200, 200}, + {201, 201, 201}, + {202, 202, 202}, + {203, 203, 203}, + {204, 204, 204}, + {205, 205, 205}, + {206, 206, 206}, + {207, 207, 207}, + {208, 208, 208}, + {209, 209, 209}, + {210, 210, 210}, + {211, 211, 211}, + {212, 212, 212}, + {213, 213, 213}, + {214, 214, 214}, + {215, 215, 215}, + {216, 216, 216}, + {217, 217, 217}, + {218, 218, 218}, + {219, 219, 219}, + {220, 220, 220}, + {221, 221, 221}, + {222, 222, 222}, + {223, 223, 223}, + {224, 224, 224}, + {225, 225, 225}, + {226, 226, 226}, + {227, 227, 227}, + {228, 228, 228}, + {229, 229, 229}, + {230, 230, 230}, + {231, 231, 231}, + {232, 232, 232}, + {233, 233, 233}, + {234, 234, 234}, + {235, 235, 235}, + {236, 236, 236}, + {237, 237, 237}, + {238, 238, 238}, + {239, 239, 239}, + {240, 240, 240}, + {241, 241, 241}, + {242, 242, 242}, + {243, 243, 243}, + {244, 244, 244}, + {245, 245, 245}, + {246, 246, 246}, + {247, 247, 247}, + {248, 248, 248}, + {249, 249, 249}, + {250, 250, 250}, + {251, 251, 251}, + {252, 252, 252}, + {253, 253, 253}, + {254, 254, 254}, + {255, 255, 255}, +}}; diff --git a/firmware/application/spectrum_color_lut.hpp b/firmware/application/spectrum_color_lut.hpp index 74c554a81..1516ae3b1 100644 --- a/firmware/application/spectrum_color_lut.hpp +++ b/firmware/application/spectrum_color_lut.hpp @@ -30,4 +30,4 @@ extern const std::array spectrum_rgb2_lut; extern const std::array spectrum_rgb3_lut; extern const std::array spectrum_rgb4_lut; -#endif/*__SPECTRUM_COLOR_LUT_H__*/ +#endif /*__SPECTRUM_COLOR_LUT_H__*/ diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index 9da9a23d9..1b387b553 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -22,251 +22,246 @@ #include "string_format.hpp" static char* to_string_dec_uint_internal( - char* p, - uint32_t n -) { - *p = 0; - auto q = p; - - do { - const uint32_t d = n % 10; - const char c = d + 48; - *(--q) = c; - n /= 10; - } while( n != 0 ); - - return q; + char* p, + uint32_t n) { + *p = 0; + auto q = p; + + do { + const uint32_t d = n % 10; + const char c = d + 48; + *(--q) = c; + n /= 10; + } while (n != 0); + + return q; } static char* to_string_dec_uint_pad_internal( - char* const term, - const uint32_t n, - const int32_t l, - const char fill -) { - auto q = to_string_dec_uint_internal(term, n); - - if( fill ) { - while( (term - q) < l ) { - *(--q) = fill; - } - } - - return q; + char* const term, + const uint32_t n, + const int32_t l, + const char fill) { + auto q = to_string_dec_uint_internal(term, n); + + if (fill) { + while ((term - q) < l) { + *(--q) = fill; + } + } + + return q; } std::string to_string_bin( - const uint32_t n, - const uint8_t l) -{ - char p[33]; - for (uint8_t c = 0; c < l; c++) { - if (n & (1 << (l - 1 - c))) - p[c] = '1'; - else - p[c] = '0'; - } - p[l] = 0; - return p; + const uint32_t n, + const uint8_t l) { + char p[33]; + for (uint8_t c = 0; c < l; c++) { + if (n & (1 << (l - 1 - c))) + p[c] = '1'; + else + p[c] = '0'; + } + p[l] = 0; + return p; } std::string to_string_dec_uint( - const uint32_t n, - const int32_t l, - const char fill -) { - char p[16]; - auto term = p + sizeof(p) - 1; - auto q = to_string_dec_uint_pad_internal(term, n, l, fill); - - // Right justify. - while( (term - q) < l ) { - *(--q) = ' '; - } - - return q; + const uint32_t n, + const int32_t l, + const char fill) { + char p[16]; + auto term = p + sizeof(p) - 1; + auto q = to_string_dec_uint_pad_internal(term, n, l, fill); + + // Right justify. + while ((term - q) < l) { + *(--q) = ' '; + } + + return q; } std::string to_string_dec_int( - const int32_t n, - const int32_t l, - const char fill -) { - const size_t negative = (n < 0) ? 1 : 0; - uint32_t n_abs = negative ? -n : n; - - char p[16]; - auto term = p + sizeof(p) - 1; - auto q = to_string_dec_uint_pad_internal(term, n_abs, l - negative, fill); - - // Add sign. - if( negative ) { - *(--q) = '-'; - } - - // Right justify. - while( (term - q) < l ) { - *(--q) = ' '; - } - - return q; + const int32_t n, + const int32_t l, + const char fill) { + const size_t negative = (n < 0) ? 1 : 0; + uint32_t n_abs = negative ? -n : n; + + char p[16]; + auto term = p + sizeof(p) - 1; + auto q = to_string_dec_uint_pad_internal(term, n_abs, l - negative, fill); + + // Add sign. + if (negative) { + *(--q) = '-'; + } + + // Right justify. + while ((term - q) < l) { + *(--q) = ' '; + } + + return q; } std::string to_string_decimal(float decimal, int8_t precision) { - double integer_part; - double fractional_part; + double integer_part; + double fractional_part; - std::string result; - - fractional_part = modf(decimal, &integer_part) * pow(10, precision); + std::string result; - if (fractional_part < 0) { - fractional_part = -fractional_part; - } + fractional_part = modf(decimal, &integer_part) * pow(10, precision); - result = to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, precision, '0'); + if (fractional_part < 0) { + fractional_part = -fractional_part; + } - return result; + result = to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, precision, '0'); + + return result; } std::string to_string_freq(const uint64_t f) { - auto final_str = to_string_dec_int(f / 1000000,4) + to_string_dec_int(f % 1000000, 6, '0'); - return final_str; + auto final_str = to_string_dec_int(f / 1000000, 4) + to_string_dec_int(f % 1000000, 6, '0'); + return final_str; } std::string to_string_short_freq(const uint64_t f) { - auto final_str = to_string_dec_int(f / 1000000,4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); - return final_str; + auto final_str = to_string_dec_int(f / 1000000, 4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); + return final_str; } std::string to_string_time_ms(const uint32_t ms) { - std::string final_str { "" }; - - if (ms < 1000) { - final_str = to_string_dec_uint(ms) + "ms"; - } else { - auto seconds = ms / 1000; - - if (seconds >= 60) - final_str = to_string_dec_uint(seconds / 60) + "m"; - - return final_str + to_string_dec_uint(seconds % 60) + "s"; - } - - return final_str; + std::string final_str{""}; + + if (ms < 1000) { + final_str = to_string_dec_uint(ms) + "ms"; + } else { + auto seconds = ms / 1000; + + if (seconds >= 60) + final_str = to_string_dec_uint(seconds / 60) + "m"; + + return final_str + to_string_dec_uint(seconds % 60) + "s"; + } + + return final_str; } static void to_string_hex_internal(char* p, const uint64_t n, const int32_t l) { - const uint32_t d = n & 0xf; - p[l] = (d > 9) ? (d + 55) : (d + 48); - if( l > 0 ) { - to_string_hex_internal(p, n >> 4, l - 1); - } + const uint32_t d = n & 0xf; + p[l] = (d > 9) ? (d + 55) : (d + 48); + if (l > 0) { + to_string_hex_internal(p, n >> 4, l - 1); + } } std::string to_string_hex(const uint64_t n, int32_t l) { - char p[32]; - - l = std::min(l, 31L); - to_string_hex_internal(p, n, l - 1); - p[l] = 0; - return p; + char p[32]; + + l = std::min(l, 31L); + to_string_hex_internal(p, n, l - 1); + p[l] = 0; + return p; } -std::string to_string_hex_array(uint8_t * const array, const int32_t l) { - std::string str_return = ""; - uint8_t bytes; - - for (bytes = 0; bytes < l; bytes++) - str_return += to_string_hex(array[bytes], 2); - - return str_return; +std::string to_string_hex_array(uint8_t* const array, const int32_t l) { + std::string str_return = ""; + uint8_t bytes; + + for (bytes = 0; bytes < l; bytes++) + str_return += to_string_hex(array[bytes], 2); + + return str_return; } std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format) { - std::string string { "" }; - - if (format == YMDHMS) { - string += to_string_dec_uint(value.year(), 4) + "-" + - to_string_dec_uint(value.month(), 2, '0') + "-" + - to_string_dec_uint(value.day(), 2, '0') + " "; - } - - string += to_string_dec_uint(value.hour(), 2, '0') + ":" + - to_string_dec_uint(value.minute(), 2, '0'); - - if ((format == YMDHMS) || (format == HMS)) - string += ":" + to_string_dec_uint(value.second(), 2, '0'); - - return string; + std::string string{""}; + + if (format == YMDHMS) { + string += to_string_dec_uint(value.year(), 4) + "-" + + to_string_dec_uint(value.month(), 2, '0') + "-" + + to_string_dec_uint(value.day(), 2, '0') + " "; + } + + string += to_string_dec_uint(value.hour(), 2, '0') + ":" + + to_string_dec_uint(value.minute(), 2, '0'); + + if ((format == YMDHMS) || (format == HMS)) + string += ":" + to_string_dec_uint(value.second(), 2, '0'); + + return string; } std::string to_string_timestamp(const rtc::RTC& value) { - return to_string_dec_uint(value.year(), 4, '0') + - to_string_dec_uint(value.month(), 2, '0') + - to_string_dec_uint(value.day(), 2, '0') + - to_string_dec_uint(value.hour(), 2, '0') + - to_string_dec_uint(value.minute(), 2, '0') + - to_string_dec_uint(value.second(), 2, '0'); + return to_string_dec_uint(value.year(), 4, '0') + + to_string_dec_uint(value.month(), 2, '0') + + to_string_dec_uint(value.day(), 2, '0') + + to_string_dec_uint(value.hour(), 2, '0') + + to_string_dec_uint(value.minute(), 2, '0') + + to_string_dec_uint(value.second(), 2, '0'); } std::string to_string_FAT_timestamp(const FATTimestamp& timestamp) { - return to_string_dec_uint((timestamp.FAT_date >> 9) + 1980) + "-" + - to_string_dec_uint((timestamp.FAT_date >> 5) & 0xF, 2, '0') + "-" + - to_string_dec_uint((timestamp.FAT_date & 0x1F), 2, '0') + " " + - to_string_dec_uint((timestamp.FAT_time >> 11), 2, '0') + ":" + - to_string_dec_uint((timestamp.FAT_time >> 5) & 0x3F, 2, '0'); + return to_string_dec_uint((timestamp.FAT_date >> 9) + 1980) + "-" + + to_string_dec_uint((timestamp.FAT_date >> 5) & 0xF, 2, '0') + "-" + + to_string_dec_uint((timestamp.FAT_date & 0x1F), 2, '0') + " " + + to_string_dec_uint((timestamp.FAT_time >> 11), 2, '0') + ":" + + to_string_dec_uint((timestamp.FAT_time >> 5) & 0x3F, 2, '0'); } std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision) { - const uint32_t powers_of_ten[5] = { 1, 10, 100, 1000, 10000 }; - std::string string { "" }; - uint32_t prefix_index = base_nano; - double integer_part; - double fractional_part; - - precision = std::min((uint32_t)4, precision); - - while (n > 1000) { - n /= 1000.0; - prefix_index++; - } - - fractional_part = modf(n, &integer_part) * powers_of_ten[precision]; - if (fractional_part < 0) - fractional_part = -fractional_part; - - string = to_string_dec_int(integer_part); - if (precision) - string += '.' + to_string_dec_uint(fractional_part, precision); - - if (prefix_index != 3) - string += unit_prefix[prefix_index]; - - return string; + const uint32_t powers_of_ten[5] = {1, 10, 100, 1000, 10000}; + std::string string{""}; + uint32_t prefix_index = base_nano; + double integer_part; + double fractional_part; + + precision = std::min((uint32_t)4, precision); + + while (n > 1000) { + n /= 1000.0; + prefix_index++; + } + + fractional_part = modf(n, &integer_part) * powers_of_ten[precision]; + if (fractional_part < 0) + fractional_part = -fractional_part; + + string = to_string_dec_int(integer_part); + if (precision) + string += '.' + to_string_dec_uint(fractional_part, precision); + + if (prefix_index != 3) + string += unit_prefix[prefix_index]; + + return string; } double get_decimals(double num, int16_t mult, bool round) { - num -= int(num); //keep decimals only - num *= mult; //Shift decimals into integers - if (!round) return num; - int16_t intnum = int(num); //Round it up if necessary - num -= intnum; //Get decimal part - if (num > .5) intnum++; //Round up - return intnum; + num -= int(num); // keep decimals only + num *= mult; // Shift decimals into integers + if (!round) return num; + int16_t intnum = int(num); // Round it up if necessary + num -= intnum; // Get decimal part + if (num > .5) intnum++; // Round up + return intnum; } std::string trim(const std::string& str) { - auto first = str.find_first_not_of(' '); - auto last = str.find_last_not_of(' '); - return str.substr(first, last - first); + auto first = str.find_first_not_of(' '); + auto last = str.find_last_not_of(' '); + return str.substr(first, last - first); } std::string trimr(std::string str) { - size_t last = str.find_last_not_of(' '); - return (last!=std::string::npos) ? str.substr(0, last+1) : ""; // Remove the trailing spaces + size_t last = str.find_last_not_of(' '); + return (last != std::string::npos) ? str.substr(0, last + 1) : ""; // Remove the trailing spaces } std::string truncate(const std::string& str, size_t length) { - return str.length() <= length ? str : str.substr(0, length); + return str.length() <= length ? str : str.substr(0, length); } \ No newline at end of file diff --git a/firmware/application/string_format.hpp b/firmware/application/string_format.hpp index e74cb9b8a..43da6b3a6 100644 --- a/firmware/application/string_format.hpp +++ b/firmware/application/string_format.hpp @@ -32,12 +32,12 @@ using namespace lpc43xx; enum TimeFormat { - YMDHMS = 0, - HMS = 1, - HM = 2 + YMDHMS = 0, + HMS = 1, + HM = 2 }; -const char unit_prefix[7] { 'n', 'u', 'm', 0, 'k', 'M', 'G' }; +const char unit_prefix[7]{'n', 'u', 'm', 0, 'k', 'M', 'G'}; // TODO: Allow l=0 to not fill/justify? Already using this way in ui_spectrum.hpp... std::string to_string_bin(const uint32_t n, const uint8_t l = 0); @@ -46,7 +46,7 @@ std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char f std::string to_string_decimal(float decimal, int8_t precision); std::string to_string_hex(const uint64_t n, const int32_t l = 0); -std::string to_string_hex_array(uint8_t * const array, const int32_t l = 0); +std::string to_string_hex_array(uint8_t* const array, const int32_t l = 0); std::string to_string_freq(const uint64_t f); std::string to_string_short_freq(const uint64_t f); @@ -57,10 +57,10 @@ std::string to_string_timestamp(const rtc::RTC& value); std::string to_string_FAT_timestamp(const FATTimestamp& timestamp); std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision); -double get_decimals(double num, int16_t mult, bool round = false); //euquiq added +double get_decimals(double num, int16_t mult, bool round = false); // euquiq added -std::string trim(const std::string& str); // Remove whitespace at ends. -std::string trimr(std::string str); // Remove trailing spaces +std::string trim(const std::string& str); // Remove whitespace at ends. +std::string trimr(std::string str); // Remove trailing spaces std::string truncate(const std::string& str, size_t length); -#endif/*__STRING_FORMAT_H__*/ +#endif /*__STRING_FORMAT_H__*/ diff --git a/firmware/application/temperature_logger.cpp b/firmware/application/temperature_logger.cpp index 152c4d5b4..3d80d7213 100644 --- a/firmware/application/temperature_logger.cpp +++ b/firmware/application/temperature_logger.cpp @@ -26,44 +26,44 @@ #include void TemperatureLogger::second_tick() { - sample_phase++; - if( sample_phase >= sample_interval ) { - push_sample(read_sample()); - } + sample_phase++; + if (sample_phase >= sample_interval) { + push_sample(read_sample()); + } } size_t TemperatureLogger::size() const { - return std::min(capacity(), samples_count); + return std::min(capacity(), samples_count); } size_t TemperatureLogger::capacity() const { - return samples.size(); + return samples.size(); } std::vector TemperatureLogger::history() const { - std::vector result; + std::vector result; - const auto n = size(); - result.resize(n); - - // Copy the last N samples from the buffer, since new samples are added at the end. - std::copy(samples.cend() - n, samples.cend(), result.data()); - - return result; + const auto n = size(); + result.resize(n); + + // Copy the last N samples from the buffer, since new samples are added at the end. + std::copy(samples.cend() - n, samples.cend(), result.data()); + + return result; } TemperatureLogger::sample_t TemperatureLogger::read_sample() { - // MAX2837 does not return a valid temperature if in "shutdown" mode. - return radio::debug::second_if::temp_sense(); + // MAX2837 does not return a valid temperature if in "shutdown" mode. + return radio::debug::second_if::temp_sense(); } void TemperatureLogger::push_sample(const TemperatureLogger::sample_t sample) { - // Started out building a pseudo-FIFO, then got lazy. + // Started out building a pseudo-FIFO, then got lazy. - // Shift samples: samples[1:] -> samples[0:-1] - // New sample goes into samples[-1] - std::copy(samples.cbegin() + 1, samples.cend(), samples.begin()); - samples.back() = sample; - samples_count++; - sample_phase = 0; + // Shift samples: samples[1:] -> samples[0:-1] + // New sample goes into samples[-1] + std::copy(samples.cbegin() + 1, samples.cend(), samples.begin()); + samples.back() = sample; + samples_count++; + sample_phase = 0; } diff --git a/firmware/application/temperature_logger.hpp b/firmware/application/temperature_logger.hpp index 01f6715ce..8ccb48270 100644 --- a/firmware/application/temperature_logger.hpp +++ b/firmware/application/temperature_logger.hpp @@ -28,25 +28,25 @@ #include class TemperatureLogger { -public: - using sample_t = uint8_t; + public: + using sample_t = uint8_t; - void second_tick(); + void second_tick(); - size_t size() const; - size_t capacity() const; - - std::vector history() const; + size_t size() const; + size_t capacity() const; -private: - std::array samples { }; + std::vector history() const; - static constexpr size_t sample_interval = 5; - size_t sample_phase = 0; - size_t samples_count = 0; + private: + std::array samples{}; - sample_t read_sample(); - void push_sample(const sample_t sample); + static constexpr size_t sample_interval = 5; + size_t sample_phase = 0; + size_t samples_count = 0; + + sample_t read_sample(); + void push_sample(const sample_t sample); }; -#endif/*__TEMPERATURE_LOGGER_H__*/ +#endif /*__TEMPERATURE_LOGGER_H__*/ diff --git a/firmware/application/tone_key.cpp b/firmware/application/tone_key.cpp index ab8e11477..1774c86fb 100644 --- a/firmware/application/tone_key.cpp +++ b/firmware/application/tone_key.cpp @@ -26,108 +26,106 @@ namespace tonekey { const tone_key_t tone_keys = { - { "None", 0.0 }, - { "0 XZ", 67.000 }, - { "1 WZ", 69.400 }, - { "2 XA", 71.900 }, - { "3 WA", 74.400 }, - { "4 XB", 77.000 }, - { "5 WB", 79.700 }, - { "6 YZ", 82.500 }, - { "7 YA", 85.400 }, - { "8 YB", 88.500 }, - { "9 ZZ", 91.500 }, - { "10 ZA", 94.800 }, - { "11 ZB", 97.400 }, - { "12 1Z", 100.000 }, - { "13 1A", 103.500 }, - { "14 1B", 107.200 }, - { "15 2Z", 110.900 }, - { "16 2A", 114.800 }, - { "17 2B", 118.800 }, - { "18 3Z", 123.000 }, - { "19 3A", 127.300 }, - { "20 3B", 131.800 }, - { "21 4Z", 136.500 }, - { "22 4A", 141.300 }, - { "23 4B", 146.200 }, - { "24 5Z", 151.400 }, - { "25 5A", 156.700 }, - { "40 --", 159.800 }, - { "26 5B", 162.200 }, - { "41 --", 165.500 }, - { "27 6Z", 167.900 }, - { "42 --", 171.300 }, - { "28 6A", 173.800 }, - { "43 --", 177.300 }, - { "29 6B", 179.900 }, - { "44 --", 183.500 }, - { "30 7Z", 186.200 }, - { "45 --", 189.900 }, - { "31 7A", 192.800 }, - { "46 --", 196.600 }, - { "47 --", 199.500 }, - { "32 M1", 203.500 }, - { "48 8Z", 206.500 }, - { "33 M2", 210.700 }, - { "34 M3", 218.100 }, - { "35 M4", 225.700 }, - { "49 9Z", 229.100 }, - { "36 --", 233.600 }, - { "37 --", 241.800 }, - { "38 --", 250.300 }, - { "50 0Z", 254.100 }, - { "Axient 28kHz", 28000.0 }, - { "Senn. 32.768k", 32768.0 }, - { "Senn. 32.000k", 32000.0 }, - { "Sony 32.382k", 32382.0 }, - { "Shure 19kHz", 19000.0 } -}; + {"None", 0.0}, + {"0 XZ", 67.000}, + {"1 WZ", 69.400}, + {"2 XA", 71.900}, + {"3 WA", 74.400}, + {"4 XB", 77.000}, + {"5 WB", 79.700}, + {"6 YZ", 82.500}, + {"7 YA", 85.400}, + {"8 YB", 88.500}, + {"9 ZZ", 91.500}, + {"10 ZA", 94.800}, + {"11 ZB", 97.400}, + {"12 1Z", 100.000}, + {"13 1A", 103.500}, + {"14 1B", 107.200}, + {"15 2Z", 110.900}, + {"16 2A", 114.800}, + {"17 2B", 118.800}, + {"18 3Z", 123.000}, + {"19 3A", 127.300}, + {"20 3B", 131.800}, + {"21 4Z", 136.500}, + {"22 4A", 141.300}, + {"23 4B", 146.200}, + {"24 5Z", 151.400}, + {"25 5A", 156.700}, + {"40 --", 159.800}, + {"26 5B", 162.200}, + {"41 --", 165.500}, + {"27 6Z", 167.900}, + {"42 --", 171.300}, + {"28 6A", 173.800}, + {"43 --", 177.300}, + {"29 6B", 179.900}, + {"44 --", 183.500}, + {"30 7Z", 186.200}, + {"45 --", 189.900}, + {"31 7A", 192.800}, + {"46 --", 196.600}, + {"47 --", 199.500}, + {"32 M1", 203.500}, + {"48 8Z", 206.500}, + {"33 M2", 210.700}, + {"34 M3", 218.100}, + {"35 M4", 225.700}, + {"49 9Z", 229.100}, + {"36 --", 233.600}, + {"37 --", 241.800}, + {"38 --", 250.300}, + {"50 0Z", 254.100}, + {"Axient 28kHz", 28000.0}, + {"Senn. 32.768k", 32768.0}, + {"Senn. 32.000k", 32000.0}, + {"Sony 32.382k", 32382.0}, + {"Shure 19kHz", 19000.0}}; void tone_keys_populate(OptionsField& field) { - using option_t = std::pair; - using options_t = std::vector; - options_t tone_key_options; - std::string tone_name; - - for (size_t c = 0; c < tone_keys.size(); c++) { - if (c && c < 51) { - auto f = tone_keys[c].second; - tone_name = "CTCSS " + tone_keys[c].first + " " + to_string_dec_uint(f) + "." + to_string_dec_uint((uint32_t)(f * 10) % 10); - } else { - tone_name = tone_keys[c].first; - } - - tone_key_options.emplace_back(tone_name, c); - } - - field.set_options(tone_key_options); + using option_t = std::pair; + using options_t = std::vector; + options_t tone_key_options; + std::string tone_name; + + for (size_t c = 0; c < tone_keys.size(); c++) { + if (c && c < 51) { + auto f = tone_keys[c].second; + tone_name = "CTCSS " + tone_keys[c].first + " " + to_string_dec_uint(f) + "." + to_string_dec_uint((uint32_t)(f * 10) % 10); + } else { + tone_name = tone_keys[c].first; + } + + tone_key_options.emplace_back(tone_name, c); + } + + field.set_options(tone_key_options); } float tone_key_frequency(const tone_index index) { - return tone_keys[index].second; + return tone_keys[index].second; } -std::string tone_key_string( tone_index index ) { - if( index < 0 || (unsigned)index >= tone_keys . size() ) - return std::string( "" ); - return tone_keys[ index ] .first ; +std::string tone_key_string(tone_index index) { + if (index < 0 || (unsigned)index >= tone_keys.size()) + return std::string(""); + return tone_keys[index].first; } -tone_index tone_key_index_by_string( char *str ) { - if( !str ) - return -1 ; - for( tone_index index = 0 ; (unsigned)index < tone_keys . size() ; index ++ ) - { - if( tone_keys[ index ] . first . compare( str ) >= 0 ) - return index ; - } - return -1 ; -} +tone_index tone_key_index_by_string(char* str) { + if (!str) + return -1; + for (tone_index index = 0; (unsigned)index < tone_keys.size(); index++) { + if (tone_keys[index].first.compare(str) >= 0) + return index; + } + return -1; +} /* tone_index tone_key_index_by_value( int32_t freq ) { - return -1 ; + return -1 ; } */ -} +} // namespace tonekey diff --git a/firmware/application/tone_key.hpp b/firmware/application/tone_key.hpp index 0f5e4f243..88808cd8d 100644 --- a/firmware/application/tone_key.hpp +++ b/firmware/application/tone_key.hpp @@ -30,7 +30,7 @@ using namespace ui; namespace tonekey { -typedef int16_t tone_index ; +typedef int16_t tone_index; using tone_key_t = std::vector>; @@ -39,10 +39,10 @@ extern const tone_key_t tone_keys; void tone_keys_populate(OptionsField& field); float tone_key_frequency(const tone_index index); -std::string tone_key_string( const tone_index index ); -tone_index tone_key_index_by_string( char *str ); +std::string tone_key_string(const tone_index index); +tone_index tone_key_index_by_string(char* str); // tone_index tone_key_index_by_value( int32_t freq ); -} +} // namespace tonekey -#endif/*__TONE_KEY_H_*/ +#endif /*__TONE_KEY_H_*/ diff --git a/firmware/application/touch.cpp b/firmware/application/touch.cpp index a0c77a25e..16e9c2189 100644 --- a/firmware/application/touch.cpp +++ b/firmware/application/touch.cpp @@ -29,108 +29,108 @@ using namespace portapack; namespace touch { Metrics calculate_metrics(const Frame& frame) { - /* TODO: Yikes! M0 doesn't have floating point, so this code is - * expensive! On the other hand, it seems to be working well (and - * fast *enough*?), so maybe leave it alone at least for now. - */ - - const auto x_max = frame.x.xp; - const auto x_min = frame.x.xn; - const auto x_range = x_max - x_min; - const float x_position = (frame.x.yp + frame.x.yn) * 0.5f; - const float x_norm = (x_position - x_min) / x_range; - - const auto y_max = frame.y.yn; - const auto y_min = frame.y.yp; - const auto y_range = y_max - y_min; - const float y_position = (frame.y.xp + frame.y.xn) * 0.5f; - const float y_norm = (y_position - y_min) / y_range; - - const auto z_max = frame.pressure.yp; - const auto z_min = frame.pressure.xn; - const auto z_range = z_max - z_min; - const auto z1_position = frame.pressure.xp; - const float z1_norm = float(z1_position - z_min) / z_range; - const auto z2_position = frame.pressure.yn; - const float z2_norm = float(z2_position - z_min) / z_range; - - const float r_x_plate = 330.0f; - //const float r_y_plate = 600.0f; - const float r_touch = r_x_plate * x_norm * (z2_norm / z1_norm - 1.0f); - - return { - .x = x_norm, - .y = y_norm, - .r = r_touch, - }; + /* TODO: Yikes! M0 doesn't have floating point, so this code is + * expensive! On the other hand, it seems to be working well (and + * fast *enough*?), so maybe leave it alone at least for now. + */ + + const auto x_max = frame.x.xp; + const auto x_min = frame.x.xn; + const auto x_range = x_max - x_min; + const float x_position = (frame.x.yp + frame.x.yn) * 0.5f; + const float x_norm = (x_position - x_min) / x_range; + + const auto y_max = frame.y.yn; + const auto y_min = frame.y.yp; + const auto y_range = y_max - y_min; + const float y_position = (frame.y.xp + frame.y.xn) * 0.5f; + const float y_norm = (y_position - y_min) / y_range; + + const auto z_max = frame.pressure.yp; + const auto z_min = frame.pressure.xn; + const auto z_range = z_max - z_min; + const auto z1_position = frame.pressure.xp; + const float z1_norm = float(z1_position - z_min) / z_range; + const auto z2_position = frame.pressure.yn; + const float z2_norm = float(z2_position - z_min) / z_range; + + const float r_x_plate = 330.0f; + // const float r_y_plate = 600.0f; + const float r_touch = r_x_plate * x_norm * (z2_norm / z1_norm - 1.0f); + + return { + .x = x_norm, + .y = y_norm, + .r = r_touch, + }; } ui::Point Calibration::translate(const DigitizerPoint& p) const { - static constexpr range_t x_range { 0, 240 - 1 }; - static constexpr range_t y_range { 0, 320 - 1 }; - - const int32_t x = (a * p.x + b * p.y + c) / k; - const int32_t y = (d * p.x + e * p.y + f) / k; - const auto x_clipped = x_range.clip(x); - const auto y_clipped = y_range.clip(y); - return { x_clipped, y_clipped }; + static constexpr range_t x_range{0, 240 - 1}; + static constexpr range_t y_range{0, 320 - 1}; + + const int32_t x = (a * p.x + b * p.y + c) / k; + const int32_t y = (d * p.x + e * p.y + f) / k; + const auto x_clipped = x_range.clip(x); + const auto y_clipped = y_range.clip(y); + return {x_clipped, y_clipped}; } void Manager::feed(const Frame& frame) { - // touch_debounce.feed(touch_raw); - const auto touch_raw = frame.touch; - //const auto touch_stable = touch_debounce.state(); - const auto touch_stable = frame.touch; - bool touch_down_pressure = false; - bool touch_up_pressure = false; - - // Only feed coordinate averaging if there's a touch. - // TODO: Separate threshold to gate coordinates for filtering? - if( touch_raw ) { - const auto metrics = calculate_metrics(frame); - - constexpr float r_touch_down_threshold = 3200.0f; - constexpr float r_touch_up_threshold = r_touch_down_threshold * 2.0f; - - touch_down_pressure = (metrics.r < r_touch_down_threshold); - touch_up_pressure = (metrics.r < r_touch_up_threshold); - - if( touch_down_pressure ) { - filter_x.feed(metrics.x * 1024); - filter_y.feed(metrics.y * 1024); - } - } else { - filter_x.reset(); - filter_y.reset(); - } - - switch(state) { - case State::NoTouch: - if( touch_stable && touch_down_pressure && !persistent_memory::disable_touchscreen()) { - if( point_stable() ) { - state = State::TouchDetected; - touch_started(); - } - } - break; - - case State::TouchDetected: - if( touch_stable && touch_up_pressure ) { - touch_moved(); - } else { - state = State::NoTouch; - touch_ended(); - } - break; - - default: - state = State::NoTouch; - break; - } + // touch_debounce.feed(touch_raw); + const auto touch_raw = frame.touch; + // const auto touch_stable = touch_debounce.state(); + const auto touch_stable = frame.touch; + bool touch_down_pressure = false; + bool touch_up_pressure = false; + + // Only feed coordinate averaging if there's a touch. + // TODO: Separate threshold to gate coordinates for filtering? + if (touch_raw) { + const auto metrics = calculate_metrics(frame); + + constexpr float r_touch_down_threshold = 3200.0f; + constexpr float r_touch_up_threshold = r_touch_down_threshold * 2.0f; + + touch_down_pressure = (metrics.r < r_touch_down_threshold); + touch_up_pressure = (metrics.r < r_touch_up_threshold); + + if (touch_down_pressure) { + filter_x.feed(metrics.x * 1024); + filter_y.feed(metrics.y * 1024); + } + } else { + filter_x.reset(); + filter_y.reset(); + } + + switch (state) { + case State::NoTouch: + if (touch_stable && touch_down_pressure && !persistent_memory::disable_touchscreen()) { + if (point_stable()) { + state = State::TouchDetected; + touch_started(); + } + } + break; + + case State::TouchDetected: + if (touch_stable && touch_up_pressure) { + touch_moved(); + } else { + state = State::NoTouch; + touch_ended(); + } + break; + + default: + state = State::NoTouch; + break; + } } ui::Point Manager::filtered_point() const { - return persistent_memory::touch_calibration().translate({ filter_x.value(), filter_y.value() }); + return persistent_memory::touch_calibration().translate({filter_x.value(), filter_y.value()}); } } /* namespace touch */ diff --git a/firmware/application/touch.hpp b/firmware/application/touch.hpp index 4aa18297e..e6ee9f9bf 100644 --- a/firmware/application/touch.hpp +++ b/firmware/application/touch.hpp @@ -41,222 +41,213 @@ constexpr sample_t sample_max = 1023; constexpr sample_t touch_threshold = sample_max / 5; struct Samples { - sample_t xp; - sample_t xn; - sample_t yp; - sample_t yn; - - constexpr Samples( - ) : Samples { 0 } - { - } - - constexpr Samples( - uint32_t v - ) : xp { static_cast(v) }, - xn { static_cast(v) }, - yp { static_cast(v) }, - yn { static_cast(v) } - { - } - - constexpr Samples( - uint32_t xp, - uint32_t xn, - uint32_t yp, - uint32_t yn - ) : xp { static_cast(xp) }, - xn { static_cast(xn) }, - yp { static_cast(yp) }, - yn { static_cast(yn) } - { - } - - Samples& operator +=(const Samples& r) { - xp += r.xp; - xn += r.xn; - yp += r.yp; - yn += r.yn; - return *this; - } - - Samples operator/(const unsigned int r) const { - return { - static_cast(xp / r), - static_cast(xn / r), - static_cast(yp / r), - static_cast(yn / r) - }; - } - - Samples operator>>(const size_t n) const { - return { - static_cast(xp >> n), - static_cast(xn >> n), - static_cast(yp >> n), - static_cast(yn >> n) - }; - } + sample_t xp; + sample_t xn; + sample_t yp; + sample_t yn; + + constexpr Samples() + : Samples{0} { + } + + constexpr Samples( + uint32_t v) + : xp{static_cast(v)}, + xn{static_cast(v)}, + yp{static_cast(v)}, + yn{static_cast(v)} { + } + + constexpr Samples( + uint32_t xp, + uint32_t xn, + uint32_t yp, + uint32_t yn) + : xp{static_cast(xp)}, + xn{static_cast(xn)}, + yp{static_cast(yp)}, + yn{static_cast(yn)} { + } + + Samples& operator+=(const Samples& r) { + xp += r.xp; + xn += r.xn; + yp += r.yp; + yn += r.yn; + return *this; + } + + Samples operator/(const unsigned int r) const { + return { + static_cast(xp / r), + static_cast(xn / r), + static_cast(yp / r), + static_cast(yn / r)}; + } + + Samples operator>>(const size_t n) const { + return { + static_cast(xp >> n), + static_cast(xn >> n), + static_cast(yp >> n), + static_cast(yn >> n)}; + } }; struct Frame { - Samples pressure { }; - Samples x { }; - Samples y { }; - bool touch { false }; + Samples pressure{}; + Samples x{}; + Samples y{}; + bool touch{false}; }; struct Metrics { - const float x; - const float y; - const float r; + const float x; + const float y; + const float r; }; Metrics calculate_metrics(const Frame& frame); struct DigitizerPoint { - int32_t x; - int32_t y; + int32_t x; + int32_t y; }; struct Calibration { - /* Touch screen calibration matrix, based on article by Carlos E. Vidales: - * http://www.embedded.com/design/system-integration/4023968/How-To-Calibrate-Touch-Screens - */ - - constexpr Calibration( - const std::array& s, - const std::array& d - ) : k { (s[0].x - s[2].x) * (s[1].y - s[2].y) - (s[1].x - s[2].x) * (s[0].y - s[2].y) }, - a { (d[0].x() - d[2].x()) * (s[1].y - s[2].y) - (d[1].x() - d[2].x()) * (s[0].y - s[2].y) }, - b { (s[0].x - s[2].x) * (d[1].x() - d[2].x()) - (d[0].x() - d[2].x()) * (s[1].x - s[2].x) }, - c { s[0].y * (s[2].x * d[1].x() - s[1].x * d[2].x()) + s[1].y * (s[0].x * d[2].x() - s[2].x * d[0].x()) + s[2].y * (s[1].x * d[0].x() - s[0].x * d[1].x()) }, - d { (d[0].y() - d[2].y()) * (s[1].y - s[2].y) - (d[1].y() - d[2].y()) * (s[0].y - s[2].y) }, - e { (s[0].x - s[2].x) * (d[1].y() - d[2].y()) - (d[0].y() - d[2].y()) * (s[1].x - s[2].x) }, - f { s[0].y * (s[2].x * d[1].y() - s[1].x * d[2].y()) + s[1].y * (s[0].x * d[2].y() - s[2].x * d[0].y()) + s[2].y * (s[1].x * d[0].y() - s[0].x * d[1].y()) } - { - } - - constexpr Calibration() : - Calibration( - /* Values derived from one PortaPack H1 unit. */ - { { { 256, 731 }, { 880, 432 }, { 568, 146 } } }, - { { { 32, 48 }, { 208, 168 }, { 120, 288 } } } - ) - { - } - - ui::Point translate(const DigitizerPoint& p) const; - -private: - int32_t k; - int32_t a; - int32_t b; - int32_t c; - int32_t d; - int32_t e; - int32_t f; + /* Touch screen calibration matrix, based on article by Carlos E. Vidales: + * http://www.embedded.com/design/system-integration/4023968/How-To-Calibrate-Touch-Screens + */ + + constexpr Calibration( + const std::array& s, + const std::array& d) + : k{(s[0].x - s[2].x) * (s[1].y - s[2].y) - (s[1].x - s[2].x) * (s[0].y - s[2].y)}, + a{(d[0].x() - d[2].x()) * (s[1].y - s[2].y) - (d[1].x() - d[2].x()) * (s[0].y - s[2].y)}, + b{(s[0].x - s[2].x) * (d[1].x() - d[2].x()) - (d[0].x() - d[2].x()) * (s[1].x - s[2].x)}, + c{s[0].y * (s[2].x * d[1].x() - s[1].x * d[2].x()) + s[1].y * (s[0].x * d[2].x() - s[2].x * d[0].x()) + s[2].y * (s[1].x * d[0].x() - s[0].x * d[1].x())}, + d{(d[0].y() - d[2].y()) * (s[1].y - s[2].y) - (d[1].y() - d[2].y()) * (s[0].y - s[2].y)}, + e{(s[0].x - s[2].x) * (d[1].y() - d[2].y()) - (d[0].y() - d[2].y()) * (s[1].x - s[2].x)}, + f{s[0].y * (s[2].x * d[1].y() - s[1].x * d[2].y()) + s[1].y * (s[0].x * d[2].y() - s[2].x * d[0].y()) + s[2].y * (s[1].x * d[0].y() - s[0].x * d[1].y())} { + } + + constexpr Calibration() + : Calibration( + /* Values derived from one PortaPack H1 unit. */ + {{{256, 731}, {880, 432}, {568, 146}}}, + {{{32, 48}, {208, 168}, {120, 288}}}) { + } + + ui::Point translate(const DigitizerPoint& p) const; + + private: + int32_t k; + int32_t a; + int32_t b; + int32_t c; + int32_t d; + int32_t e; + int32_t f; }; -template +template class Filter { -public: - constexpr Filter() = default; - - void reset() { - history.fill(0); - history_history = 0; - accumulator = 0; - n = 0; - } - - void feed(const sample_t value) { - accumulator = accumulator + value - history[n]; - history[n] = value; - n = (n + 1) % history.size(); - - history_history = (history_history << 1) | 1U; - } - - int32_t value() const { - return accumulator / N; - } - - bool stable(const uint32_t bound) const { - if( history_valid() ) { - const auto minmax = std::minmax_element(history.cbegin(), history.cend()); - const auto min = *minmax.first; - const auto max = *minmax.second; - const uint32_t delta = max - min; - return (delta < bound); - } else { - return false; - } - } - -private: - static constexpr uint32_t history_history_mask { (1U << N) - 1 }; - - std::array history { }; - uint32_t history_history { 0 }; - int32_t accumulator { 0 }; - size_t n { 0 }; - - bool history_valid() const { - return (history_history & history_history_mask) == history_history_mask; - } + public: + constexpr Filter() = default; + + void reset() { + history.fill(0); + history_history = 0; + accumulator = 0; + n = 0; + } + + void feed(const sample_t value) { + accumulator = accumulator + value - history[n]; + history[n] = value; + n = (n + 1) % history.size(); + + history_history = (history_history << 1) | 1U; + } + + int32_t value() const { + return accumulator / N; + } + + bool stable(const uint32_t bound) const { + if (history_valid()) { + const auto minmax = std::minmax_element(history.cbegin(), history.cend()); + const auto min = *minmax.first; + const auto max = *minmax.second; + const uint32_t delta = max - min; + return (delta < bound); + } else { + return false; + } + } + + private: + static constexpr uint32_t history_history_mask{(1U << N) - 1}; + + std::array history{}; + uint32_t history_history{0}; + int32_t accumulator{0}; + size_t n{0}; + + bool history_valid() const { + return (history_history & history_history_mask) == history_history_mask; + } }; class Manager { -public: - std::function on_event { }; + public: + std::function on_event{}; - void feed(const Frame& frame); + void feed(const Frame& frame); -private: - enum State { - NoTouch, - TouchDetected, - }; + private: + enum State { + NoTouch, + TouchDetected, + }; - static constexpr size_t touch_count_threshold { 3 }; - static constexpr uint32_t touch_stable_bound { 8 }; + static constexpr size_t touch_count_threshold{3}; + static constexpr uint32_t touch_stable_bound{8}; - // Ensure filter length is equal or less than touch_count_threshold, - // or coordinates from the last touch will be in the initial averages. - Filter filter_x { }; - Filter filter_y { }; + // Ensure filter length is equal or less than touch_count_threshold, + // or coordinates from the last touch will be in the initial averages. + Filter filter_x{}; + Filter filter_y{}; - //Debounce touch_debounce; + // Debounce touch_debounce; - State state { State::NoTouch }; + State state{State::NoTouch}; - bool point_stable() const { - return filter_x.stable(touch_stable_bound) - && filter_y.stable(touch_stable_bound); - } + bool point_stable() const { + return filter_x.stable(touch_stable_bound) && filter_y.stable(touch_stable_bound); + } - ui::Point filtered_point() const; + ui::Point filtered_point() const; - void touch_started() { - fire_event(ui::TouchEvent::Type::Start); - } + void touch_started() { + fire_event(ui::TouchEvent::Type::Start); + } - void touch_moved() { - fire_event(ui::TouchEvent::Type::Move); - } + void touch_moved() { + fire_event(ui::TouchEvent::Type::Move); + } - void touch_ended() { - fire_event(ui::TouchEvent::Type::End); - } + void touch_ended() { + fire_event(ui::TouchEvent::Type::End); + } - void fire_event(ui::TouchEvent::Type type) { - if( on_event ) { - on_event({ filtered_point(), type }); - } - } + void fire_event(ui::TouchEvent::Type type) { + if (on_event) { + on_event({filtered_point(), type}); + } + } }; } /* namespace touch */ -#endif/*__TOUCH_H__*/ +#endif /*__TOUCH_H__*/ diff --git a/firmware/application/transmitter_model.cpp b/firmware/application/transmitter_model.cpp index 1d5d402bd..47d394522 100644 --- a/firmware/application/transmitter_model.cpp +++ b/firmware/application/transmitter_model.cpp @@ -36,154 +36,154 @@ using namespace portapack; #include "audio.hpp" rf::Frequency TransmitterModel::tuning_frequency() const { - return persistent_memory::tuned_frequency(); + return persistent_memory::tuned_frequency(); } void TransmitterModel::set_tuning_frequency(rf::Frequency f) { - persistent_memory::set_tuned_frequency(f); - update_tuning_frequency(); + persistent_memory::set_tuned_frequency(f); + update_tuning_frequency(); } void TransmitterModel::set_antenna_bias() { - update_antenna_bias(); + update_antenna_bias(); } bool TransmitterModel::rf_amp() const { - return rf_amp_; + return rf_amp_; } void TransmitterModel::set_rf_amp(bool enabled) { - rf_amp_ = enabled; - update_rf_amp(); + rf_amp_ = enabled; + update_rf_amp(); } int32_t TransmitterModel::lna() const { - return lna_gain_db_; + return lna_gain_db_; } void TransmitterModel::set_lna(int32_t v_db) { - lna_gain_db_ = v_db; - update_lna(); + lna_gain_db_ = v_db; + update_lna(); } uint32_t TransmitterModel::baseband_bandwidth() const { - return baseband_bandwidth_; + return baseband_bandwidth_; } void TransmitterModel::set_baseband_bandwidth(uint32_t v) { - baseband_bandwidth_ = v; - update_baseband_bandwidth(); + baseband_bandwidth_ = v; + update_baseband_bandwidth(); } int32_t TransmitterModel::vga() const { - return vga_gain_db_; + return vga_gain_db_; } void TransmitterModel::set_vga(int32_t v_db) { - vga_gain_db_ = v_db; - update_vga(); + vga_gain_db_ = v_db; + update_vga(); } uint32_t TransmitterModel::channel_bandwidth() const { - return channel_bandwidth_; + return channel_bandwidth_; } void TransmitterModel::set_channel_bandwidth(uint32_t v) { - channel_bandwidth_ = v; + channel_bandwidth_ = v; } uint32_t TransmitterModel::sampling_rate() const { - return sampling_rate_; + return sampling_rate_; } void TransmitterModel::set_sampling_rate(uint32_t v) { - sampling_rate_ = v; - update_sampling_rate(); + sampling_rate_ = v; + update_sampling_rate(); } int32_t TransmitterModel::tx_gain() const { - return tx_gain_db_; + return tx_gain_db_; } void TransmitterModel::set_tx_gain(int32_t v_db) { - tx_gain_db_ = v_db; - update_tx_gain(); + tx_gain_db_ = v_db; + update_tx_gain(); } void TransmitterModel::on_tick_second() { - if (portapack::persistent_memory::stealth_mode()) - led_tx.toggle(); + if (portapack::persistent_memory::stealth_mode()) + led_tx.toggle(); } void TransmitterModel::enable() { - enabled_ = true; - radio::set_direction(rf::Direction::Transmit); - update_tuning_frequency(); - update_antenna_bias(); - update_rf_amp(); - update_lna(); - update_vga(); - update_baseband_bandwidth(); - update_sampling_rate(); - - led_tx.on(); - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; - if (portapack::persistent_memory::stealth_mode()) { - DisplaySleepMessage message; - EventDispatcher::send_message(message); - } + enabled_ = true; + radio::set_direction(rf::Direction::Transmit); + update_tuning_frequency(); + update_antenna_bias(); + update_rf_amp(); + update_lna(); + update_vga(); + update_baseband_bandwidth(); + update_sampling_rate(); + + led_tx.on(); + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; + if (portapack::persistent_memory::stealth_mode()) { + DisplaySleepMessage message; + EventDispatcher::send_message(message); + } } void TransmitterModel::disable() { - enabled_ = false; - radio::set_antenna_bias(false); + enabled_ = false; + radio::set_antenna_bias(false); - // TODO: Responsibility for enabling/disabling the radio is muddy. - // Some happens in ReceiverModel, some inside radio namespace. - radio::disable(); - - rtc_time::signal_tick_second -= signal_token_tick_second; - led_tx.off(); + // TODO: Responsibility for enabling/disabling the radio is muddy. + // Some happens in ReceiverModel, some inside radio namespace. + radio::disable(); + + rtc_time::signal_tick_second -= signal_token_tick_second; + led_tx.off(); } void TransmitterModel::update_tuning_frequency() { - radio::set_tuning_frequency(persistent_memory::tuned_frequency()); + radio::set_tuning_frequency(persistent_memory::tuned_frequency()); } void TransmitterModel::update_antenna_bias() { - if (enabled_) - radio::set_antenna_bias(portapack::get_antenna_bias()); + if (enabled_) + radio::set_antenna_bias(portapack::get_antenna_bias()); } void TransmitterModel::update_rf_amp() { - radio::set_rf_amp(rf_amp_); + radio::set_rf_amp(rf_amp_); } void TransmitterModel::update_lna() { - radio::set_lna_gain(lna_gain_db_); + radio::set_lna_gain(lna_gain_db_); } void TransmitterModel::update_baseband_bandwidth() { - radio::set_baseband_filter_bandwidth(baseband_bandwidth_); + radio::set_baseband_filter_bandwidth(baseband_bandwidth_); } void TransmitterModel::update_vga() { - radio::set_vga_gain(vga_gain_db_); + radio::set_vga_gain(vga_gain_db_); } void TransmitterModel::update_tx_gain() { - radio::set_tx_gain(tx_gain_db_); + radio::set_tx_gain(tx_gain_db_); } void TransmitterModel::update_sampling_rate() { - // TODO: Move more low-level radio control stuff to M4. It'll enable tighter - // synchronization for things like wideband (sweeping) spectrum analysis, and - // protocols that need quick RX/TX turn-around. - - // Disabling baseband while changing sampling rates seems like a good idea... - - radio::set_baseband_rate(sampling_rate()); - update_tuning_frequency(); + // TODO: Move more low-level radio control stuff to M4. It'll enable tighter + // synchronization for things like wideband (sweeping) spectrum analysis, and + // protocols that need quick RX/TX turn-around. + + // Disabling baseband while changing sampling rates seems like a good idea... + + radio::set_baseband_rate(sampling_rate()); + update_tuning_frequency(); } diff --git a/firmware/application/transmitter_model.hpp b/firmware/application/transmitter_model.hpp index d67150688..3debb8c77 100644 --- a/firmware/application/transmitter_model.hpp +++ b/firmware/application/transmitter_model.hpp @@ -34,56 +34,56 @@ #include "signal.hpp" class TransmitterModel { -public: - rf::Frequency tuning_frequency() const; - void set_tuning_frequency(rf::Frequency f); - - void set_antenna_bias(); - - bool rf_amp() const; - void set_rf_amp(bool enabled); - - int32_t lna() const; - void set_lna(int32_t v_db); - - uint32_t baseband_bandwidth() const; - void set_baseband_bandwidth(uint32_t v); - - int32_t vga() const; - void set_vga(int32_t v_db); - - int32_t tx_gain() const; - void set_tx_gain(int32_t v_db); - - uint32_t channel_bandwidth() const; - void set_channel_bandwidth(uint32_t v); - - uint32_t sampling_rate() const; - void set_sampling_rate(uint32_t v); - - void enable(); - void disable(); - -private: - bool enabled_ { false }; - bool rf_amp_ { false }; - int32_t lna_gain_db_ { 0 }; - uint32_t channel_bandwidth_ { 1 }; - uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum }; - int32_t vga_gain_db_ { 8 }; - int32_t tx_gain_db_ { 47 }; - uint32_t sampling_rate_ { 3072000 }; - SignalToken signal_token_tick_second { }; - - void update_tuning_frequency(); - void update_antenna_bias(); - void update_rf_amp(); - void update_lna(); - void update_baseband_bandwidth(); - void update_vga(); - void update_tx_gain(); - void update_sampling_rate(); - void on_tick_second(); + public: + rf::Frequency tuning_frequency() const; + void set_tuning_frequency(rf::Frequency f); + + void set_antenna_bias(); + + bool rf_amp() const; + void set_rf_amp(bool enabled); + + int32_t lna() const; + void set_lna(int32_t v_db); + + uint32_t baseband_bandwidth() const; + void set_baseband_bandwidth(uint32_t v); + + int32_t vga() const; + void set_vga(int32_t v_db); + + int32_t tx_gain() const; + void set_tx_gain(int32_t v_db); + + uint32_t channel_bandwidth() const; + void set_channel_bandwidth(uint32_t v); + + uint32_t sampling_rate() const; + void set_sampling_rate(uint32_t v); + + void enable(); + void disable(); + + private: + bool enabled_{false}; + bool rf_amp_{false}; + int32_t lna_gain_db_{0}; + uint32_t channel_bandwidth_{1}; + uint32_t baseband_bandwidth_{max2837::filter::bandwidth_minimum}; + int32_t vga_gain_db_{8}; + int32_t tx_gain_db_{47}; + uint32_t sampling_rate_{3072000}; + SignalToken signal_token_tick_second{}; + + void update_tuning_frequency(); + void update_antenna_bias(); + void update_rf_amp(); + void update_lna(); + void update_baseband_bandwidth(); + void update_vga(); + void update_tx_gain(); + void update_sampling_rate(); + void on_tick_second(); }; -#endif/*__TRANSMITTER_MODEL_H__*/ +#endif /*__TRANSMITTER_MODEL_H__*/ diff --git a/firmware/application/tuning.cpp b/firmware/application/tuning.cpp index cfd434927..5024e8cb0 100644 --- a/firmware/application/tuning.cpp +++ b/firmware/application/tuning.cpp @@ -29,54 +29,52 @@ namespace config { namespace { constexpr rf::Frequency low_band_second_lo_frequency(const rf::Frequency target_frequency) { - return 2650000000 - (target_frequency / 7); + return 2650000000 - (target_frequency / 7); } constexpr rf::Frequency high_band_second_lo_regions_2_and_3(const rf::Frequency target_frequency) { - return (target_frequency < 5100000000) - ? (2350000000 + ((target_frequency - 3600000000) / 5)) - : (2500000000 + ((target_frequency - 5100000000) / 9)) - ; + return (target_frequency < 5100000000) + ? (2350000000 + ((target_frequency - 3600000000) / 5)) + : (2500000000 + ((target_frequency - 5100000000) / 9)); } constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target_frequency) { - return (target_frequency < 3600000000) - ? (2170000000 + (((target_frequency - 2740000000) * 57) / 86)) - : high_band_second_lo_regions_2_and_3(target_frequency) - ; + return (target_frequency < 3600000000) + ? (2170000000 + (((target_frequency - 2740000000) * 57) / 86)) + : high_band_second_lo_regions_2_and_3(target_frequency); } Config low_band(const rf::Frequency target_frequency) { - const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency); - const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency; - const bool mixer_invert = true; - return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, mixer_invert }; + const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency); + const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency; + const bool mixer_invert = true; + return {first_lo_frequency, second_lo_frequency, rf::path::Band::Low, mixer_invert}; } Config mid_band(const rf::Frequency target_frequency) { - return { 0, target_frequency, rf::path::Band::Mid, false }; + return {0, target_frequency, rf::path::Band::Mid, false}; } Config high_band(const rf::Frequency target_frequency) { - const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency); - const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency; - const bool mixer_invert = false; - return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, mixer_invert }; + const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency); + const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency; + const bool mixer_invert = false; + return {first_lo_frequency, second_lo_frequency, rf::path::Band::High, mixer_invert}; } } /* namespace */ Config create(const rf::Frequency target_frequency) { - /* TODO: This is some lame code. */ - if( rf::path::band_low.contains(target_frequency) ) { - return low_band(target_frequency); - } else if( rf::path::band_mid.contains(target_frequency) ) { - return mid_band(target_frequency); - } else if( rf::path::band_high.contains(target_frequency) ) { - return high_band(target_frequency); - } else { - return { }; - } + /* TODO: This is some lame code. */ + if (rf::path::band_low.contains(target_frequency)) { + return low_band(target_frequency); + } else if (rf::path::band_mid.contains(target_frequency)) { + return mid_band(target_frequency); + } else if (rf::path::band_high.contains(target_frequency)) { + return high_band(target_frequency); + } else { + return {}; + } } } /* namespace config */ diff --git a/firmware/application/tuning.hpp b/firmware/application/tuning.hpp index a590fa888..b16b6c5a0 100644 --- a/firmware/application/tuning.hpp +++ b/firmware/application/tuning.hpp @@ -28,35 +28,33 @@ namespace tuning { namespace config { struct Config { - /* Empty config to denote an error, in lieu of throwing an exception. */ - constexpr Config( - ) : first_lo_frequency(0), - second_lo_frequency(0), - rf_path_band(rf::path::Band::Mid), - mixer_invert(false) - { - } + /* Empty config to denote an error, in lieu of throwing an exception. */ + constexpr Config() + : first_lo_frequency(0), + second_lo_frequency(0), + rf_path_band(rf::path::Band::Mid), + mixer_invert(false) { + } - constexpr Config( - rf::Frequency first_lo_frequency, - rf::Frequency second_lo_frequency, - rf::path::Band rf_path_band, - bool mixer_invert - ) : first_lo_frequency(first_lo_frequency), - second_lo_frequency(second_lo_frequency), - rf_path_band(rf_path_band), - mixer_invert(mixer_invert) - { - } + constexpr Config( + rf::Frequency first_lo_frequency, + rf::Frequency second_lo_frequency, + rf::path::Band rf_path_band, + bool mixer_invert) + : first_lo_frequency(first_lo_frequency), + second_lo_frequency(second_lo_frequency), + rf_path_band(rf_path_band), + mixer_invert(mixer_invert) { + } - bool is_valid() const { - return (second_lo_frequency != 0); - } + bool is_valid() const { + return (second_lo_frequency != 0); + } - const rf::Frequency first_lo_frequency; - const rf::Frequency second_lo_frequency; - const rf::path::Band rf_path_band; - const bool mixer_invert; + const rf::Frequency first_lo_frequency; + const rf::Frequency second_lo_frequency; + const rf::path::Band rf_path_band; + const bool mixer_invert; }; Config create(const rf::Frequency target_frequency); @@ -64,4 +62,4 @@ Config create(const rf::Frequency target_frequency); } /* namespace config */ } /* namespace tuning */ -#endif/*__TUNING_H__*/ +#endif /*__TUNING_H__*/ diff --git a/firmware/application/ui/ui_alphanum.cpp b/firmware/application/ui/ui_alphanum.cpp index f9161fc51..024125de9 100644 --- a/firmware/application/ui/ui_alphanum.cpp +++ b/firmware/application/ui/ui_alphanum.cpp @@ -31,94 +31,87 @@ namespace ui { AlphanumView::AlphanumView( - NavigationView& nav, - std::string& str, - size_t max_length -) : TextEntryView(nav, str, max_length) -{ - size_t n; - - add_children({ - &button_mode, - &text_raw, - &field_raw - }); - - const auto button_fn = [this](Button& button) { - this->on_button(button); - }; - - n = 0; - for (auto& button : buttons) { - button.id = n; - button.on_highlight = [this](Button& button) { - focused_button = button.id; - }; - button.on_select = button_fn; - button.set_parent_rect({ - static_cast((n % 5) * (240 / 5)), - static_cast((n / 5) * 38 + 24), - 240 / 5, 38 - }); - add_child(&button); - n++; - } - - set_mode(mode); - - button_mode.on_select = [this](Button&) { - set_mode(mode + 1); - }; - - field_raw.set_value('0'); - field_raw.on_select = [this](NumberField&) { - char_add(field_raw.value()); - }; + NavigationView& nav, + std::string& str, + size_t max_length) + : TextEntryView(nav, str, max_length) { + size_t n; + + add_children({&button_mode, + &text_raw, + &field_raw}); + + const auto button_fn = [this](Button& button) { + this->on_button(button); + }; + + n = 0; + for (auto& button : buttons) { + button.id = n; + button.on_highlight = [this](Button& button) { + focused_button = button.id; + }; + button.on_select = button_fn; + button.set_parent_rect({static_cast((n % 5) * (240 / 5)), + static_cast((n / 5) * 38 + 24), + 240 / 5, 38}); + add_child(&button); + n++; + } + + set_mode(mode); + + button_mode.on_select = [this](Button&) { + set_mode(mode + 1); + }; + + field_raw.set_value('0'); + field_raw.on_select = [this](NumberField&) { + char_add(field_raw.value()); + }; } void AlphanumView::set_mode(const uint32_t new_mode) { - size_t n = 0; - - if (new_mode < 3) - mode = new_mode; - else - mode = 0; - - const char * key_list = key_sets[mode].second; - - for (auto& button : buttons) { - const std::string label { - key_list[n] - }; - button.set_text(label); - n++; - } - - if (mode < 2) - button_mode.set_text(key_sets[mode + 1].first); - else - button_mode.set_text(key_sets[0].first); + size_t n = 0; + + if (new_mode < 3) + mode = new_mode; + else + mode = 0; + + const char* key_list = key_sets[mode].second; + + for (auto& button : buttons) { + const std::string label{ + key_list[n]}; + button.set_text(label); + n++; + } + + if (mode < 2) + button_mode.set_text(key_sets[mode + 1].first); + else + button_mode.set_text(key_sets[0].first); } void AlphanumView::on_button(Button& button) { - const auto c = button.text()[0]; - - if (c == '<') - char_delete(); - else - char_add(c); + const auto c = button.text()[0]; + + if (c == '<') + char_delete(); + else + char_add(c); } bool AlphanumView::on_encoder(const EncoderEvent delta) { - focused_button += delta; - if (focused_button < 0) { - focused_button = buttons.size() - 1; - } - else if (focused_button >= (int16_t)buttons.size()) { - focused_button = 0; - } - buttons[focused_button].focus(); - return true; + focused_button += delta; + if (focused_button < 0) { + focused_button = buttons.size() - 1; + } else if (focused_button >= (int16_t)buttons.size()) { + focused_button = 0; + } + buttons[focused_button].focus(); + return true; } -} +} // namespace ui diff --git a/firmware/application/ui/ui_alphanum.hpp b/firmware/application/ui/ui_alphanum.hpp index d61ee7fe7..debde5205 100644 --- a/firmware/application/ui/ui_alphanum.hpp +++ b/firmware/application/ui/ui_alphanum.hpp @@ -32,58 +32,53 @@ namespace ui { class AlphanumView : public TextEntryView { -public: - AlphanumView(NavigationView& nav, std::string& str, size_t max_length); - - AlphanumView(const AlphanumView&) = delete; - AlphanumView(AlphanumView&&) = delete; - AlphanumView& operator=(const AlphanumView&) = delete; - AlphanumView& operator=(AlphanumView&&) = delete; - - bool on_encoder(const EncoderEvent delta) override; - -private: - const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<"; - const char * const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<"; - const char * const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<"; - - const std::pair key_sets[3] = { - { "Upper", keys_upper }, - { "Lower", keys_lower }, - { "Digit", keys_digit } - }; - - int16_t focused_button = 0; - uint32_t mode = 0; // Uppercase - - void set_mode(const uint32_t new_mode); - void on_button(Button& button); - - std::array buttons { }; - - Button button_mode { - { 21 * 8, 33 * 8, 8 * 8, 32 }, - "" - }; - - Text text_raw { - { 1 * 8, 33 * 8, 4 * 8, 16 }, - "Raw:" - }; - NumberField field_raw { - { 5 * 8, 33 * 8 }, - 3, - { 1, 255 }, - 1, - '0' - }; - - Button button_ok { - { 10 * 8, 33 * 8, 9 * 8, 32 }, - "OK" - }; + public: + AlphanumView(NavigationView& nav, std::string& str, size_t max_length); + + AlphanumView(const AlphanumView&) = delete; + AlphanumView(AlphanumView&&) = delete; + AlphanumView& operator=(const AlphanumView&) = delete; + AlphanumView& operator=(AlphanumView&&) = delete; + + bool on_encoder(const EncoderEvent delta) override; + + private: + const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<"; + const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<"; + const char* const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<"; + + const std::pair key_sets[3] = { + {"Upper", keys_upper}, + {"Lower", keys_lower}, + {"Digit", keys_digit}}; + + int16_t focused_button = 0; + uint32_t mode = 0; // Uppercase + + void set_mode(const uint32_t new_mode); + void on_button(Button& button); + + std::array buttons{}; + + Button button_mode{ + {21 * 8, 33 * 8, 8 * 8, 32}, + ""}; + + Text text_raw{ + {1 * 8, 33 * 8, 4 * 8, 16}, + "Raw:"}; + NumberField field_raw{ + {5 * 8, 33 * 8}, + 3, + {1, 255}, + 1, + '0'}; + + Button button_ok{ + {10 * 8, 33 * 8, 9 * 8, 32}, + "OK"}; }; } /* namespace ui */ -#endif/*__ALPHANUM_H__*/ +#endif /*__ALPHANUM_H__*/ diff --git a/firmware/application/ui/ui_audio.cpp b/firmware/application/ui/ui_audio.cpp index 1f51c3a39..eae89d6e3 100644 --- a/firmware/application/ui/ui_audio.cpp +++ b/firmware/application/ui/ui_audio.cpp @@ -28,45 +28,41 @@ namespace ui { void Audio::paint(Painter& painter) { - const auto r = screen_rect(); + const auto r = screen_rect(); - constexpr int db_min = -96; - constexpr int db_max = 0; - constexpr int db_delta = db_max - db_min; - const range_t x_rms_range { 0, r.width() - 1 }; - const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta); - const range_t x_max_range { x_rms + 1, r.width() }; - const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); + constexpr int db_min = -96; + constexpr int db_max = 0; + constexpr int db_delta = db_max - db_min; + const range_t x_rms_range{0, r.width() - 1}; + const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta); + const range_t x_max_range{x_rms + 1, r.width()}; + const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); - const Rect r0 { r.left(), r.top(), x_rms, r.height() }; - painter.fill_rectangle( - r0, - Color::green() - ); + const Rect r0{r.left(), r.top(), x_rms, r.height()}; + painter.fill_rectangle( + r0, + Color::green()); - const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() }; - painter.fill_rectangle( - r1, - Color::black() - ); + const Rect r1{r.left() + x_rms, r.top(), 1, r.height()}; + painter.fill_rectangle( + r1, + Color::black()); - const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() }; - painter.fill_rectangle( - r2, - Color::red() - ); + const Rect r2{r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height()}; + painter.fill_rectangle( + r2, + Color::red()); - const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() }; - painter.fill_rectangle( - r3, - Color::black() - ); + const Rect r3{r.left() + x_max, r.top(), r.width() - x_max, r.height()}; + painter.fill_rectangle( + r3, + Color::black()); } void Audio::on_statistics_update(const AudioStatistics& statistics) { - rms_db_ = statistics.rms_db; - max_db_ = statistics.max_db; - set_dirty(); + rms_db_ = statistics.rms_db; + max_db_ = statistics.max_db; + set_dirty(); } } /* namespace ui */ diff --git a/firmware/application/ui/ui_audio.hpp b/firmware/application/ui/ui_audio.hpp index 23bc3cc98..6f84abd8b 100644 --- a/firmware/application/ui/ui_audio.hpp +++ b/firmware/application/ui/ui_audio.hpp @@ -33,31 +33,29 @@ namespace ui { class Audio : public Widget { -public: - Audio( - const Rect parent_rect - ) : Widget { parent_rect }, - rms_db_ { -120 }, - max_db_ { -120 } - { - } - - void paint(Painter& painter) override; - -private: - int32_t rms_db_; - int32_t max_db_; - - MessageHandlerRegistration message_handler_statistics { - Message::ID::AudioStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - - void on_statistics_update(const AudioStatistics& statistics); + public: + Audio( + const Rect parent_rect) + : Widget{parent_rect}, + rms_db_{-120}, + max_db_{-120} { + } + + void paint(Painter& painter) override; + + private: + int32_t rms_db_; + int32_t max_db_; + + MessageHandlerRegistration message_handler_statistics{ + Message::ID::AudioStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + + void on_statistics_update(const AudioStatistics& statistics); }; -} +} // namespace ui -#endif/*__UI_AUDIO_H__*/ +#endif /*__UI_AUDIO_H__*/ diff --git a/firmware/application/ui/ui_btngrid.cpp b/firmware/application/ui/ui_btngrid.cpp index d3df9f9a5..d43e41b81 100644 --- a/firmware/application/ui/ui_btngrid.cpp +++ b/firmware/application/ui/ui_btngrid.cpp @@ -29,176 +29,170 @@ namespace ui { /* BtnGridView **************************************************************/ BtnGridView::BtnGridView( - Rect new_parent_rect, - bool keep_highlight -) : keep_highlight { keep_highlight } -{ - set_parent_rect(new_parent_rect); + Rect new_parent_rect, + bool keep_highlight) + : keep_highlight{keep_highlight} { + set_parent_rect(new_parent_rect); - set_focusable(true); + set_focusable(true); - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; - add_child(&arrow_more); - arrow_more.set_focusable(false); - arrow_more.set_foreground(Color::black()); + add_child(&arrow_more); + arrow_more.set_focusable(false); + arrow_more.set_foreground(Color::black()); } BtnGridView::~BtnGridView() { - rtc_time::signal_tick_second -= signal_token_tick_second; + rtc_time::signal_tick_second -= signal_token_tick_second; - for (auto item : menu_item_views) { - delete item; - } + for (auto item : menu_item_views) { + delete item; + } } void BtnGridView::set_max_rows(int rows) { - rows_ = rows; + rows_ = rows; } int BtnGridView::rows() { - return rows_; + return rows_; } void BtnGridView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); + View::set_parent_rect(new_parent_rect); - displayed_max = (parent_rect().size().height() / button_h); - arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * button_h), 8, 8 } ); - displayed_max *= rows_; + displayed_max = (parent_rect().size().height() / button_h); + arrow_more.set_parent_rect({228, (Coord)(displayed_max * button_h), 8, 8}); + displayed_max *= rows_; - // TODO: Clean this up :( - if (menu_item_views.size()) { + // TODO: Clean this up :( + if (menu_item_views.size()) { + for (auto item : menu_item_views) { + remove_child(item); + delete item; + } + menu_item_views.clear(); + } - for (auto item : menu_item_views) { - remove_child(item); - delete item; - } - menu_item_views.clear(); - } + button_w = 240 / rows_; + for (size_t c = 0; c < displayed_max; c++) { + auto item = new NewButton{}; + menu_item_views.push_back(item); + add_child(item); - button_w = 240 / rows_; - for (size_t c = 0; c < displayed_max; c++) { - auto item = new NewButton { }; - menu_item_views.push_back(item); - add_child(item); + item->set_parent_rect({(int)(c % rows_) * button_w, + (int)(c / rows_) * button_h, + button_w, button_h}); + } - item->set_parent_rect({ - (int)(c % rows_) * button_w, - (int)(c / rows_) * button_h, - button_w, button_h - }); - } - - update_items(); + update_items(); } void BtnGridView::set_arrow_enabled(bool new_value) { - if(new_value){ - add_child(&arrow_more); - } - else{ - remove_child(&arrow_more); - } + if (new_value) { + add_child(&arrow_more); + } else { + remove_child(&arrow_more); + } }; void BtnGridView::on_tick_second() { - if (more && blink) - arrow_more.set_foreground(Color::white()); - else - arrow_more.set_foreground(Color::black()); + if (more && blink) + arrow_more.set_foreground(Color::white()); + else + arrow_more.set_foreground(Color::black()); - blink = !blink; + blink = !blink; - arrow_more.set_dirty(); + arrow_more.set_dirty(); } void BtnGridView::clear() { - menu_items.clear(); + menu_items.clear(); } void BtnGridView::add_items(std::initializer_list new_items) { - for (auto item : new_items) { - menu_items.push_back(item); - } - update_items(); + for (auto item : new_items) { + menu_items.push_back(item); + } + update_items(); } void BtnGridView::update_items() { - size_t i = 0; - - if ((menu_items.size()) > (displayed_max + offset)) { - more = true; - blink = true; - } else - more = false; - - for (NewButton* item : menu_item_views) { - if ((i + offset) >= menu_items.size()) { - item->hidden(true); - item->set_text(" "); - item->set_bitmap(nullptr); - item->on_select = [](){}; - item->set_dirty(); - } - else { - // Assign item data to NewButtons according to offset - item->hidden(false); - item->set_text(menu_items[i + offset].text); - item->set_bitmap(menu_items[i + offset].bitmap); - item->set_color(menu_items[i + offset].color); - item->on_select = menu_items[i + offset].on_select; - item->set_dirty(); - } - - i++; - } + size_t i = 0; + + if ((menu_items.size()) > (displayed_max + offset)) { + more = true; + blink = true; + } else + more = false; + + for (NewButton* item : menu_item_views) { + if ((i + offset) >= menu_items.size()) { + item->hidden(true); + item->set_text(" "); + item->set_bitmap(nullptr); + item->on_select = []() {}; + item->set_dirty(); + } else { + // Assign item data to NewButtons according to offset + item->hidden(false); + item->set_text(menu_items[i + offset].text); + item->set_bitmap(menu_items[i + offset].bitmap); + item->set_color(menu_items[i + offset].color); + item->on_select = menu_items[i + offset].on_select; + item->set_dirty(); + } + + i++; + } } NewButton* BtnGridView::item_view(size_t index) const { - return menu_item_views[index]; + return menu_item_views[index]; } bool BtnGridView::set_highlighted(int32_t new_value) { - int32_t item_count = (int32_t)menu_items.size(); + int32_t item_count = (int32_t)menu_items.size(); - if (new_value < 0) - return false; + if (new_value < 0) + return false; - if (new_value >= item_count) - new_value = item_count - 1; + if (new_value >= item_count) + new_value = item_count - 1; - if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) { - // Shift BtnGridView up - highlighted_item = new_value; - offset = new_value - displayed_max + rows_; - update_items(); - set_dirty(); - } else if ((uint32_t)new_value < offset) { - // Shift BtnGridView down - highlighted_item = new_value; - offset = (new_value / rows_) * rows_; - update_items(); - set_dirty(); - } else { - // Just update highlight - highlighted_item = new_value; - } + if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) { + // Shift BtnGridView up + highlighted_item = new_value; + offset = new_value - displayed_max + rows_; + update_items(); + set_dirty(); + } else if ((uint32_t)new_value < offset) { + // Shift BtnGridView down + highlighted_item = new_value; + offset = (new_value / rows_) * rows_; + update_items(); + set_dirty(); + } else { + // Just update highlight + highlighted_item = new_value; + } - if (visible()) - item_view(highlighted_item - offset)->focus(); + if (visible()) + item_view(highlighted_item - offset)->focus(); - return true; + return true; } uint32_t BtnGridView::highlighted_index() { - return highlighted_item; + return highlighted_item; } void BtnGridView::on_focus() { - item_view(highlighted_item - offset)->focus(); + item_view(highlighted_item - offset)->focus(); } void BtnGridView::on_blur() { @@ -209,32 +203,32 @@ void BtnGridView::on_blur() { } bool BtnGridView::on_key(const KeyEvent key) { - switch(key) { - case KeyEvent::Up: - return set_highlighted(highlighted_item - rows_); + switch (key) { + case KeyEvent::Up: + return set_highlighted(highlighted_item - rows_); - case KeyEvent::Down: - return set_highlighted(highlighted_item + rows_); + case KeyEvent::Down: + return set_highlighted(highlighted_item + rows_); - case KeyEvent::Right: - return set_highlighted(highlighted_item + 1); + case KeyEvent::Right: + return set_highlighted(highlighted_item + 1); - case KeyEvent::Left: - return set_highlighted(highlighted_item - 1); + case KeyEvent::Left: + return set_highlighted(highlighted_item - 1); - case KeyEvent::Select: - if( menu_items[highlighted_item].on_select ) { - menu_items[highlighted_item].on_select(); - } - return true; + case KeyEvent::Select: + if (menu_items[highlighted_item].on_select) { + menu_items[highlighted_item].on_select(); + } + return true; - default: - return false; - } + default: + return false; + } } bool BtnGridView::on_encoder(const EncoderEvent event) { - return set_highlighted(highlighted_item + event); + return set_highlighted(highlighted_item + event); } } /* namespace ui */ diff --git a/firmware/application/ui/ui_btngrid.hpp b/firmware/application/ui/ui_btngrid.hpp index 5f922f78b..9d62e85a0 100644 --- a/firmware/application/ui/ui_btngrid.hpp +++ b/firmware/application/ui/ui_btngrid.hpp @@ -37,64 +37,63 @@ namespace ui { struct GridItem { - std::string text; - ui::Color color; - const Bitmap* bitmap; - std::function on_select; + std::string text; + ui::Color color; + const Bitmap* bitmap; + std::function on_select; - // TODO: Prevent default-constructed GridItems. + // TODO: Prevent default-constructed GridItems. }; class BtnGridView : public View { -public: - BtnGridView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false); - - ~BtnGridView(); - - void add_items(std::initializer_list new_items); - void set_max_rows(int rows); - int rows(); - void clear(); - - NewButton* item_view(size_t index) const; - - bool set_highlighted(int32_t new_value); - uint32_t highlighted_index(); - - void set_parent_rect(const Rect new_parent_rect) override; - void set_arrow_enabled(bool new_value); - void on_focus() override; - void on_blur() override; - bool on_key(const KeyEvent event) override; - bool on_encoder(const EncoderEvent event) override; - -private: - int rows_ { 3 }; - void update_items(); - void on_tick_second(); - - bool keep_highlight { false }; - - SignalToken signal_token_tick_second { }; - std::vector menu_items { }; - std::vector menu_item_views { }; - - Image arrow_more { - { 228, 320 - 8, 8, 8 }, - &bitmap_more, - Color::white(), - Color::black() - }; - - int button_w = 240 / rows_; - static constexpr int button_h = 48; - bool blink = false; - bool more = false; - size_t displayed_max { 0 }; - size_t highlighted_item { 0 }; - size_t offset { 0 }; + public: + BtnGridView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false); + + ~BtnGridView(); + + void add_items(std::initializer_list new_items); + void set_max_rows(int rows); + int rows(); + void clear(); + + NewButton* item_view(size_t index) const; + + bool set_highlighted(int32_t new_value); + uint32_t highlighted_index(); + + void set_parent_rect(const Rect new_parent_rect) override; + void set_arrow_enabled(bool new_value); + void on_focus() override; + void on_blur() override; + bool on_key(const KeyEvent event) override; + bool on_encoder(const EncoderEvent event) override; + + private: + int rows_{3}; + void update_items(); + void on_tick_second(); + + bool keep_highlight{false}; + + SignalToken signal_token_tick_second{}; + std::vector menu_items{}; + std::vector menu_item_views{}; + + Image arrow_more{ + {228, 320 - 8, 8, 8}, + &bitmap_more, + Color::white(), + Color::black()}; + + int button_w = 240 / rows_; + static constexpr int button_h = 48; + bool blink = false; + bool more = false; + size_t displayed_max{0}; + size_t highlighted_item{0}; + size_t offset{0}; }; } /* namespace ui */ -#endif/*__UI_BTNGRID_H__*/ +#endif /*__UI_BTNGRID_H__*/ diff --git a/firmware/application/ui/ui_channel.cpp b/firmware/application/ui/ui_channel.cpp index 227d71fe5..d6a2cfb47 100644 --- a/firmware/application/ui/ui_channel.cpp +++ b/firmware/application/ui/ui_channel.cpp @@ -28,36 +28,33 @@ namespace ui { void Channel::paint(Painter& painter) { - const auto r = screen_rect(); - - constexpr int db_min = -96; - constexpr int db_max = 0; - constexpr int db_delta = db_max - db_min; - const range_t x_max_range { 0, r.width() - 1 }; - const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); - - const Rect r0 { r.left(), r.top(), x_max, r.height() }; - painter.fill_rectangle( - r0, - Color::blue() - ); - - const Rect r1 { r.left() + x_max, r.top(), 1, r.height() }; - painter.fill_rectangle( - r1, - Color::white() - ); - - const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() }; - painter.fill_rectangle( - r2, - Color::black() - ); + const auto r = screen_rect(); + + constexpr int db_min = -96; + constexpr int db_max = 0; + constexpr int db_delta = db_max - db_min; + const range_t x_max_range{0, r.width() - 1}; + const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); + + const Rect r0{r.left(), r.top(), x_max, r.height()}; + painter.fill_rectangle( + r0, + Color::blue()); + + const Rect r1{r.left() + x_max, r.top(), 1, r.height()}; + painter.fill_rectangle( + r1, + Color::white()); + + const Rect r2{r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height()}; + painter.fill_rectangle( + r2, + Color::black()); } void Channel::on_statistics_update(const ChannelStatistics& statistics) { - max_db_ = statistics.max_db; - set_dirty(); + max_db_ = statistics.max_db; + set_dirty(); } } /* namespace ui */ diff --git a/firmware/application/ui/ui_channel.hpp b/firmware/application/ui/ui_channel.hpp index bf8e832e6..b53c28641 100644 --- a/firmware/application/ui/ui_channel.hpp +++ b/firmware/application/ui/ui_channel.hpp @@ -35,29 +35,27 @@ namespace ui { class Channel : public Widget { -public: - Channel( - const Rect parent_rect - ) : Widget { parent_rect }, - max_db_ { -120 } - { - } - - void paint(Painter& painter) override; - -private: - int32_t max_db_; - - MessageHandlerRegistration message_handler_stats { - Message::ID::ChannelStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - - void on_statistics_update(const ChannelStatistics& statistics); + public: + Channel( + const Rect parent_rect) + : Widget{parent_rect}, + max_db_{-120} { + } + + void paint(Painter& painter) override; + + private: + int32_t max_db_; + + MessageHandlerRegistration message_handler_stats{ + Message::ID::ChannelStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + + void on_statistics_update(const ChannelStatistics& statistics); }; -} +} // namespace ui -#endif/*__UI_CHANNEL_H__*/ +#endif /*__UI_CHANNEL_H__*/ diff --git a/firmware/application/ui/ui_font_fixed_8x16.cpp b/firmware/application/ui/ui_font_fixed_8x16.cpp index 15f97931b..c6f3b74a6 100644 --- a/firmware/application/ui/ui_font_fixed_8x16.cpp +++ b/firmware/application/ui/ui_font_fixed_8x16.cpp @@ -30,932 +30,4140 @@ namespace { const uint8_t fixed_8x16_glyph_data[] = { -// Font based on a modded https://www.1001freefonts.com/8-bit-operator.font - -// Index: 0 (0x00) Char: 0x0020 (' ') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 1 (0x01) Char: 0x0021 ('!') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 2 (0x02) Char: 0x0022 ('"') -0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 3 (0x03) Char: 0x0023 ('#') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66, -0x66, 0x66, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 4 (0x04) Char: 0x0024 ('$') -0x00, 0x00, 0x18, 0x18, 0x3C, 0x66, 0x66, 0x0C, -0x18, 0x30, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00, - -// Index: 5 (0x05) Char: 0x0025 ('%') -0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, -0x18, 0x0C, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, - -// Index: 6 (0x06) Char: 0x0026 ('&') -0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x1C, -0x0E, 0x7B, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00, - -// Index: 7 (0x07) Char: 0x0027 (''') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 8 (0x08) Char: 0x0028 ('(') -0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, -0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, - -// Index: 9 (0x09) Char: 0x0029 (')') -0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, -0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, - -// Index: 10 (0x0A) Char: 0x002A ('*') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1C, -0x7F, 0x1C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 11 (0x0B) Char: 0x002B ('+') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 12 (0x0C) Char: 0x002C (',') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00, - -// Index: 13 (0x0D) Char: 0x002D ('-') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 14 (0x0E) Char: 0x002E ('.') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 15 (0x0F) Char: 0x002F ('/') -0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x30, 0x30, -0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x00, 0x00, - -// Index: 16 (0x10) Char: 0x0030 ('0') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x76, -0x7E, 0x6E, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 17 (0x11) Char: 0x0031 ('1') -0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 18 (0x12) Char: 0x0032 ('2') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60, -0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 19 (0x13) Char: 0x0033 ('3') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60, -0x38, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 20 (0x14) Char: 0x0034 ('4') -0x00, 0x00, 0x00, 0x00, 0x60, 0x70, 0x78, 0x6C, -0x66, 0x66, 0x7E, 0x60, 0x60, 0x00, 0x00, 0x00, - -// Index: 21 (0x15) Char: 0x0035 ('5') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x3E, -0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 22 (0x16) Char: 0x0036 ('6') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x3E, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 23 (0x17) Char: 0x0037 ('7') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30, -0x30, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 24 (0x18) Char: 0x0038 ('8') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 25 (0x19) Char: 0x0039 ('9') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x7C, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 26 (0x1A) Char: 0x003A (':') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 27 (0x1B) Char: 0x003B (';') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00, - -// Index: 28 (0x1C) Char: 0x003C ('<') -0x00, 0x00, 0x00, 0x00, 0x20, 0x30, 0x18, 0x0C, -0x06, 0x0C, 0x18, 0x30, 0x20, 0x00, 0x00, 0x00, - -// Index: 29 (0x1D) Char: 0x003D ('=') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, -0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 30 (0x1E) Char: 0x003E ('>') -0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, -0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0x00, 0x00, - -// Index: 31 (0x1F) Char: 0x003F ('?') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60, -0x30, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 32 (0x20) Char: 0x0040 ('@') -0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xF3, 0xDB, -0xDB, 0xDB, 0xF3, 0x03, 0x7E, 0x00, 0x00, 0x00, - -// Index: 33 (0x21) Char: 0x0041 ('A') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 34 (0x22) Char: 0x0042 ('B') -0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, - -// Index: 35 (0x23) Char: 0x0043 ('C') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 36 (0x24) Char: 0x0044 ('D') -0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, - -// Index: 37 (0x25) Char: 0x0045 ('E') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 38 (0x26) Char: 0x0046 ('F') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, - -// Index: 39 (0x27) Char: 0x0047 ('G') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x76, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 40 (0x28) Char: 0x0048 ('H') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, -0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 41 (0x29) Char: 0x0049 ('I') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 42 (0x2A) Char: 0x004A ('J') -0x00, 0x00, 0x00, 0x00, 0x70, 0x60, 0x60, 0x60, -0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 43 (0x2B) Char: 0x004B ('K') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x36, -0x1E, 0x36, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 44 (0x2C) Char: 0x004C ('L') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, -0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 45 (0x2D) Char: 0x004D ('M') -0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x77, 0x7F, -0x6B, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, - -// Index: 46 (0x2E) Char: 0x004E ('N') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x6E, 0x7E, -0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 47 (0x2F) Char: 0x004F ('O') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 48 (0x30) Char: 0x0050 ('P') -0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, - -// Index: 49 (0x31) Char: 0x0051 ('Q') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x30, 0x60, 0x00, - -// Index: 50 (0x32) Char: 0x0052 ('R') -0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 51 (0x33) Char: 0x0053 ('S') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x0C, -0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 52 (0x34) Char: 0x0054 ('T') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 53 (0x35) Char: 0x0055 ('U') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 54 (0x36) Char: 0x0056 ('V') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, - -// Index: 55 (0x37) Char: 0x0057 ('W') -0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6B, 0x6B, -0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00, - -// Index: 56 (0x38) Char: 0x0058 ('X') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, -0x18, 0x3C, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 57 (0x39) Char: 0x0059 ('Y') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, -0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 58 (0x3A) Char: 0x005A ('Z') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30, -0x18, 0x0C, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 59 (0x3B) Char: 0x005B ('[') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, - -// Index: 60 (0x3C) Char: 0x005C ('\') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x0C, 0x0C, -0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00, - -// Index: 61 (0x3D) Char: 0x005D (']') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, -0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, - -// Index: 62 (0x3E) Char: 0x005E ('^') -0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 63 (0x3F) Char: 0x005F ('_') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, - -// Index: 64 (0x40) Char: 0x0060 ('`') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 65 (0x41) Char: 0x0061 ('a') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 66 (0x42) Char: 0x0062 ('b') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, - -// Index: 67 (0x43) Char: 0x0063 ('c') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 68 (0x44) Char: 0x0064 ('d') -0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 69 (0x45) Char: 0x0065 ('e') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 70 (0x46) Char: 0x0066 ('f') -0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E, -0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, - -// Index: 71 (0x47) Char: 0x0067 ('g') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x33, -0x33, 0x1E, 0x03, 0x1E, 0x33, 0x33, 0x1E, 0x00, - -// Index: 72 (0x48) Char: 0x0068 ('h') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 73 (0x49) Char: 0x0069 ('i') -0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x1C, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 74 (0x4A) Char: 0x006A ('j') -0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, -0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, - -// Index: 75 (0x4B) Char: 0x006B ('k') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x66, 0x66, -0x36, 0x1E, 0x36, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 76 (0x4C) Char: 0x006C ('l') -0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 77 (0x4D) Char: 0x006D ('m') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xDB, -0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, - -// Index: 78 (0x4E) Char: 0x006E ('n') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 79 (0x4F) Char: 0x006F ('o') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 80 (0x50) Char: 0x0070 ('p') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, - -// Index: 81 (0x51) Char: 0x0071 ('q') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, - -// Index: 82 (0x52) Char: 0x0072 ('r') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, -0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, - -// Index: 83 (0x53) Char: 0x0073 ('s') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 84 (0x54) Char: 0x0074 ('t') -0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x3E, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x38, 0x00, 0x00, 0x00, - -// Index: 85 (0x55) Char: 0x0075 ('u') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 86 (0x56) Char: 0x0076 ('v') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, - -// Index: 87 (0x57) Char: 0x0077 ('w') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6B, -0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00, - -// Index: 88 (0x58) Char: 0x0078 ('x') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, -0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 89 (0x59) Char: 0x0079 ('y') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, - -// Index: 90 (0x5A) Char: 0x007A ('z') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, -0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 91 (0x5B) Char: 0x007B ('{') -0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, -0x0E, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, - -// Index: 92 (0x5C) Char: 0x007C ('|') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 93 (0x5D) Char: 0x007D ('}') -0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, -0x70, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, - -// Index: 94 (0x5E) Char: 0x007E ('~') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, -0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 95 (0x5F) Char: 0x007F ('') -0x00, 0x00, 0x7C, 0xC6, 0x92, 0x92, 0x82, 0xBA, -0xFE, 0xEE, 0xD6, 0xD6, 0xEE, 0xFE, 0x7C, 0x00, - -// Index: 96 (0x60) Char: 0x00A0 (' ') -0x00, 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, -0xFE, 0xBA, 0x82, 0xAA, 0x92, 0xC6, 0x7C, 0x00, - -// Index: 97 (0x61) Char: 0x00A1 ('¡') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, - -// Index: 98 (0x62) Char: 0x00A2 ('¢') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3C, 0x66, -0x06, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 99 (0x63) Char: 0x00A3 ('£') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x1F, 0x06, 0x06, 0x66, 0x3F, 0x00, 0x00, 0x00, - -// Index: 100 (0x64) Char: 0x00A4 ('¤') -0x00, 0x00, 0x00, 0x22, 0x1C, 0x36, 0x22, 0x36, -0x1C, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 101 (0x65) Char: 0x00A5 ('¥') -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, -0x3C, 0x7E, 0x18, 0x7E, 0x18, 0x00, 0x00, 0x00, - -// Index: 102 (0x66) Char: 0x00A6 ('¦') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, -0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 103 (0x67) Char: 0x00A7 ('§') -0x00, 0x00, 0x00, 0x3C, 0x46, 0x0E, 0x1A, 0x36, -0x6C, 0x58, 0x70, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 104 (0x68) Char: 0x00A8 ('¨') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 105 (0x69) Char: 0x00A9 ('©') -0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x99, -0x85, 0x85, 0x99, 0x42, 0x3C, 0x00, 0x00, 0x00, - -// Index: 106 (0x6A) Char: 0x00AA ('ª') -0x00, 0x00, 0x1C, 0x20, 0x3C, 0x36, 0x3C, 0x00, -0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 107 (0x6B) Char: 0x00AB ('«') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x66, -0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 108 (0x6C) Char: 0x00AC ('¬') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x7E, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 109 (0x6D) Char: 0x00AD ('­') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 110 (0x6E) Char: 0x00AE ('®') -0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x9D, -0xA5, 0x9D, 0xA5, 0x42, 0x3C, 0x00, 0x00, 0x00, - -// Index: 111 (0x6F) Char: 0x00AF ('¯') -0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 112 (0x70) Char: 0x00B0 ('°') -0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x36, 0x1C, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 113 (0x71) Char: 0x00B1 ('±') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x00, 0x00, - -// Index: 114 (0x72) Char: 0x00B2 ('²') -0x00, 0x04, 0x0A, 0x08, 0x04, 0x02, 0x0E, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 115 (0x73) Char: 0x00B3 ('³') -0x00, 0x06, 0x08, 0x06, 0x08, 0x08, 0x06, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 116 (0x74) Char: 0x00B4 ('´') -0x00, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 117 (0x75) Char: 0x00B5 ('µ') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, - -// Index: 118 (0x76) Char: 0x00B6 ('¶') -0x00, 0x00, 0x00, 0x00, 0x7E, 0x6F, 0x6F, 0x6F, -0x6E, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, - -// Index: 119 (0x77) Char: 0x00B7 ('·') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, -0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 120 (0x78) Char: 0x00B8 ('¸') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x00, - -// Index: 121 (0x79) Char: 0x00B9 ('¹') -0x00, 0x04, 0x06, 0x04, 0x04, 0x04, 0x0E, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 122 (0x7A) Char: 0x00BA ('º') -0x00, 0x0E, 0x0A, 0x0E, 0x00, 0x0E, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 123 (0x7B) Char: 0x00BB ('»') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, -0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 124 (0x7C) Char: 0x00BC ('¼') -0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0xA0, -0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00, - -// Index: 125 (0x7D) Char: 0x00BD ('½') -0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0x40, -0xA8, 0x84, 0x42, 0x20, 0xE0, 0x00, 0x00, 0x00, - -// Index: 126 (0x7E) Char: 0x00BE ('¾') -0x00, 0x06, 0x08, 0x86, 0x48, 0x28, 0x06, 0xA0, -0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00, - -// Index: 127 (0x7F) Char: 0x00BF ('¿') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x00, 0x18, 0x0C, 0x06, 0x66, 0x66, 0x3C, 0x00, - -// Index: 128 (0x80) Char: 0x00C0 ('À') -0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 129 (0x81) Char: 0x00C1 ('Á') -0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 130 (0x82) Char: 0x00C2 ('Â') -0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 131 (0x83) Char: 0x00C3 ('Ã') -0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33, -0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, - -// Index: 132 (0x84) Char: 0x00C4 ('Ä') -0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 133 (0x85) Char: 0x00C5 ('Å') -0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 134 (0x86) Char: 0x00C6 ('Æ') -0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33, -0xF3, 0x3F, 0x33, 0x33, 0xF3, 0x00, 0x00, 0x00, - -// Index: 135 (0x87) Char: 0x00C7 ('Ç') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C, - -// Index: 136 (0x88) Char: 0x00C8 ('È') -0x00, 0x0C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 137 (0x89) Char: 0x00C9 ('É') -0x00, 0x30, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 138 (0x8A) Char: 0x00CA ('Ê') -0x18, 0x3C, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 139 (0x8B) Char: 0x00CB ('Ë') -0x00, 0x66, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 140 (0x8C) Char: 0x00CC ('Ì') -0x00, 0x0C, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 141 (0x8D) Char: 0x00CD ('Í') -0x00, 0x30, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 142 (0x8E) Char: 0x00CE ('Î') -0x0C, 0x1E, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, - -// Index: 143 (0x8F) Char: 0x00CF ('Ï') -0x00, 0x32, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, - -// Index: 144 (0x90) Char: 0x00D0 ('Ð') -0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x6E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, - -// Index: 145 (0x91) Char: 0x00D1 ('Ñ') -0x00, 0x6E, 0x3B, 0x00, 0x33, 0x33, 0x37, 0x3F, -0x3B, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, - -// Index: 146 (0x92) Char: 0x00D2 ('Ò') -0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 147 (0x93) Char: 0x00D3 ('Ó') -0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 148 (0x94) Char: 0x00D4 ('Ô') -0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 149 (0x95) Char: 0x00D5 ('Õ') -0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33, -0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00, - -// Index: 150 (0x96) Char: 0x00D6 ('Ö') -0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 151 (0x97) Char: 0x00D7 ('×') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, -0x3C, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, - -// Index: 152 (0x98) Char: 0x00D8 ('Ø') -0x00, 0x00, 0x00, 0x00, 0x5E, 0x73, 0x33, 0x3B, -0x3F, 0x37, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00, - -// Index: 153 (0x99) Char: 0x00D9 ('Ù') -0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 154 (0x9A) Char: 0x00DA ('Ú') -0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 155 (0x9B) Char: 0x00DB ('Û') -0x18, 0x3C, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 156 (0x9C) Char: 0x00DC ('Ü') -0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 157 (0x9D) Char: 0x00DD ('Ý') -0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, -0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 158 (0x9E) Char: 0x00DE ('Þ') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66, -0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, 0x00, 0x00, - -// Index: 159 (0x9F) Char: 0x00DF ('ß') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, -0x36, 0x66, 0x66, 0x66, 0x36, 0x00, 0x00, 0x00, - -// Index: 160 (0xA0) Char: 0x00E0 ('à') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 161 (0xA1) Char: 0x00E1 ('á') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 162 (0xA2) Char: 0x00E2 ('â') -0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 163 (0xA3) Char: 0x00E3 ('ã') -0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, -0x30, 0x3E, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00, - -// Index: 164 (0xA4) Char: 0x00E4 ('ä') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 165 (0xA5) Char: 0x00E5 ('å') -0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66, -0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 166 (0xA6) Char: 0x00E6 ('æ') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, -0x30, 0xFE, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00, - -// Index: 167 (0xA7) Char: 0x00E7 ('ç') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, -0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C, - -// Index: 168 (0xA8) Char: 0x00E8 ('è') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 169 (0xA9) Char: 0x00E9 ('é') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 170 (0xAA) Char: 0x00EA ('ê') -0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 171 (0xAB) Char: 0x00EB ('ë') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 172 (0xAC) Char: 0x00EC ('ì') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x1C, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 173 (0xAD) Char: 0x00ED ('í') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x1C, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 174 (0xAE) Char: 0x00EE ('î') -0x00, 0x00, 0x0C, 0x1E, 0x32, 0x00, 0x0E, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, - -// Index: 175 (0xAF) Char: 0x00EF ('ï') -0x00, 0x00, 0x00, 0x32, 0x32, 0x00, 0x0E, 0x0C, -0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, - -// Index: 176 (0xB0) Char: 0x00F0 ('ð') -0x00, 0x00, 0x00, 0x00, 0x30, 0x7C, 0x30, 0x3E, -0x33, 0x33, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00, - -// Index: 177 (0xB1) Char: 0x00F1 ('ñ') -0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1F, 0x33, -0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, - -// Index: 178 (0xB2) Char: 0x00F2 ('ò') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 179 (0xB3) Char: 0x00F3 ('ó') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 180 (0xB4) Char: 0x00F4 ('ô') -0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 181 (0xB5) Char: 0x00F5 ('õ') -0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, -0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00, - -// Index: 182 (0xB6) Char: 0x00F6 ('ö') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 183 (0xB7) Char: 0x00F7 ('÷') -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, -0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - -// Index: 184 (0xB8) Char: 0x00F8 ('ø') -0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7E, 0x33, -0x3B, 0x3F, 0x37, 0x33, 0x1F, 0x00, 0x00, 0x00, - -// Index: 185 (0xB9) Char: 0x00F9 ('ù') -0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 186 (0xBA) Char: 0x00FA ('ú') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 187 (0xBB) Char: 0x00FB ('û') -0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 188 (0xBC) Char: 0x00FC ('ü') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 189 (0xBD) Char: 0x00FD ('ý') -0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, - -// Index: 190 (0xBE) Char: 0x00FE ('þ') -0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, - -// Index: 191 (0xBF) Char: 0x00FF ('ÿ') -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, - -// Index: 192 (0xC0) Char: 0x010C ('C') -0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x06, -0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 193 (0xC1) Char: 0x010D ('c') -0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 194 (0xC2) Char: 0x010E ('D') -0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, - -// Index: 195 (0xC3) Char: 0x010F ('d') -0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 196 (0xC4) Char: 0x011A ('E') -0x66, 0x3C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06, -0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00, - -// Index: 197 (0xC5) Char: 0x011B ('e') -0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, -0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 198 (0xC6) Char: 0x0131 ('i') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, -0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, - -// Index: 199 (0xC7) Char: 0x0147 ('N') -0x66, 0x3C, 0x18, 0x00, 0x66, 0x66, 0x6E, 0x7E, -0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 200 (0xC8) Char: 0x0148 ('n') -0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, -0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 201 (0xC9) Char: 0x0152 ('Œ') -0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33, -0xF3, 0x33, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00, - -// Index: 202 (0xCA) Char: 0x0153 ('œ') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, -0x33, 0xF3, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00, - -// Index: 203 (0xCB) Char: 0x0158 ('R') -0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66, -0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 204 (0xCC) Char: 0x0159 ('r') -0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, -0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, - -// Index: 205 (0xCD) Char: 0x0160 ('Š') -0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x0C, -0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 206 (0xCE) Char: 0x0161 ('š') -0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, -0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 207 (0xCF) Char: 0x0164 ('T') -0x66, 0x3C, 0x18, 0x00, 0x7E, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 208 (0xD0) Char: 0x0165 ('t') -0x00, 0x00, 0x00, 0x00, 0x86, 0x86, 0x1F, 0x86, -0x06, 0x06, 0x06, 0x06, 0x1C, 0x00, 0x00, 0x00, - -// Index: 209 (0xD1) Char: 0x016E ('U') -0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 210 (0xD2) Char: 0x016F ('u') -0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66, -0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, - -// Index: 211 (0xD3) Char: 0x0178 ('Ÿ') -0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, -0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 212 (0xD4) Char: 0x017D ('Ž') -0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30, 0x30, 0x18, -0x0C, 0x06, 0x02, 0x02, 0x3E, 0x00, 0x00, 0x00, - -// Index: 213 (0xD5) Char: 0x017E ('ž') -0x00, 0x00, 0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30, -0x18, 0x0C, 0x06, 0x02, 0x3E, 0x00, 0x00, 0x00, - -// Index: 214 (0xD6) Char: 0x0192 ('ƒ') -0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E, -0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x06, 0x00, - -// Index: 215 (0xD7) Char: 0x2014 ('—') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 216 (0xD8) Char: 0x2018 ('‘') -0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 217 (0xD9) Char: 0x2019 ('’') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 218 (0xDA) Char: 0x201A ('‚') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00, - -// Index: 219 (0xDB) Char: 0x201C ('“') -0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 220 (0xDC) Char: 0x201D ('”') -0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 221 (0xDD) Char: 0x201E ('„') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12, 0x00, - -// Index: 222 (0xDE) Char: 0x2020 ('†') -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - -// Index: 223 (0xDF) Char: 0x2022 ('•') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, -0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 224 (0xE0) Char: 0x2026 ('…') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, - -// Index: 225 (0xE1) Char: 0x2039 ('‹') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, -0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 226 (0xE2) Char: 0x203A ('›') -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, -0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, - -// Index: 227 (0xE3) Char: 0x20AC ('€') -0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x1F, -0x06, 0x1F, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, - -// Index: 228 (0xE4) Char: 0x2122 ('™') -0x00, 0x00, 0x00, 0x00, 0xDF, 0xAA, 0x8A, 0x8A, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Font based on a modded https://www.1001freefonts.com/8-bit-operator.font + + // Index: 0 (0x00) Char: 0x0020 (' ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 1 (0x01) Char: 0x0021 ('!') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 2 (0x02) Char: 0x0022 ('"') + 0x00, + 0x00, + 0x00, + 0x00, + 0x36, + 0x36, + 0x24, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 3 (0x03) Char: 0x0023 ('#') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0xFF, + 0x66, + 0x66, + 0x66, + 0xFF, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 4 (0x04) Char: 0x0024 ('$') + 0x00, + 0x00, + 0x18, + 0x18, + 0x3C, + 0x66, + 0x66, + 0x0C, + 0x18, + 0x30, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x18, + 0x00, + + // Index: 5 (0x05) Char: 0x0025 ('%') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x43, + 0x63, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x63, + 0x61, + 0x00, + 0x00, + 0x00, + + // Index: 6 (0x06) Char: 0x0026 ('&') + 0x00, + 0x00, + 0x00, + 0x00, + 0x1C, + 0x36, + 0x36, + 0x1C, + 0x0E, + 0x7B, + 0x33, + 0x33, + 0x6E, + 0x00, + 0x00, + 0x00, + + // Index: 7 (0x07) Char: 0x0027 (''') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x10, + 0x08, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 8 (0x08) Char: 0x0028 ('(') + 0x00, + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x18, + 0x30, + 0x00, + 0x00, + 0x00, + + // Index: 9 (0x09) Char: 0x0029 (')') + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x30, + 0x30, + 0x30, + 0x30, + 0x30, + 0x18, + 0x0C, + 0x00, + 0x00, + 0x00, + + // Index: 10 (0x0A) Char: 0x002A ('*') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x36, + 0x1C, + 0x7F, + 0x1C, + 0x36, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 11 (0x0B) Char: 0x002B ('+') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x7E, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 12 (0x0C) Char: 0x002C (',') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x10, + 0x08, + 0x00, + + // Index: 13 (0x0D) Char: 0x002D ('-') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 14 (0x0E) Char: 0x002E ('.') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 15 (0x0F) Char: 0x002F ('/') + 0x00, + 0x00, + 0x00, + 0x00, + 0x60, + 0x60, + 0x30, + 0x30, + 0x18, + 0x18, + 0x0C, + 0x0C, + 0x06, + 0x06, + 0x00, + 0x00, + + // Index: 16 (0x10) Char: 0x0030 ('0') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x76, + 0x7E, + 0x6E, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 17 (0x11) Char: 0x0031 ('1') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 18 (0x12) Char: 0x0032 ('2') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x60, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 19 (0x13) Char: 0x0033 ('3') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x60, + 0x38, + 0x60, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 20 (0x14) Char: 0x0034 ('4') + 0x00, + 0x00, + 0x00, + 0x00, + 0x60, + 0x70, + 0x78, + 0x6C, + 0x66, + 0x66, + 0x7E, + 0x60, + 0x60, + 0x00, + 0x00, + 0x00, + + // Index: 21 (0x15) Char: 0x0035 ('5') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x3E, + 0x60, + 0x60, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 22 (0x16) Char: 0x0036 ('6') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 23 (0x17) Char: 0x0037 ('7') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x60, + 0x60, + 0x30, + 0x30, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 24 (0x18) Char: 0x0038 ('8') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 25 (0x19) Char: 0x0039 ('9') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x60, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 26 (0x1A) Char: 0x003A (':') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 27 (0x1B) Char: 0x003B (';') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x10, + 0x08, + 0x00, + + // Index: 28 (0x1C) Char: 0x003C ('<') + 0x00, + 0x00, + 0x00, + 0x00, + 0x20, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x0C, + 0x18, + 0x30, + 0x20, + 0x00, + 0x00, + 0x00, + + // Index: 29 (0x1D) Char: 0x003D ('=') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x00, + 0x3E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 30 (0x1E) Char: 0x003E ('>') + 0x00, + 0x00, + 0x00, + 0x00, + 0x02, + 0x06, + 0x0C, + 0x18, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x02, + 0x00, + 0x00, + 0x00, + + // Index: 31 (0x1F) Char: 0x003F ('?') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x60, + 0x30, + 0x18, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 32 (0x20) Char: 0x0040 ('@') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0xC3, + 0xF3, + 0xDB, + 0xDB, + 0xDB, + 0xF3, + 0x03, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 33 (0x21) Char: 0x0041 ('A') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 34 (0x22) Char: 0x0042 ('B') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 35 (0x23) Char: 0x0043 ('C') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x06, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 36 (0x24) Char: 0x0044 ('D') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 37 (0x25) Char: 0x0045 ('E') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 38 (0x26) Char: 0x0046 ('F') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x06, + 0x00, + 0x00, + 0x00, + + // Index: 39 (0x27) Char: 0x0047 ('G') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x76, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 40 (0x28) Char: 0x0048 ('H') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 41 (0x29) Char: 0x0049 ('I') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 42 (0x2A) Char: 0x004A ('J') + 0x00, + 0x00, + 0x00, + 0x00, + 0x70, + 0x60, + 0x60, + 0x60, + 0x60, + 0x60, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 43 (0x2B) Char: 0x004B ('K') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x36, + 0x1E, + 0x36, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 44 (0x2C) Char: 0x004C ('L') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x06, + 0x06, + 0x06, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 45 (0x2D) Char: 0x004D ('M') + 0x00, + 0x00, + 0x00, + 0x00, + 0x41, + 0x63, + 0x77, + 0x7F, + 0x6B, + 0x63, + 0x63, + 0x63, + 0x63, + 0x00, + 0x00, + 0x00, + + // Index: 46 (0x2E) Char: 0x004E ('N') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x6E, + 0x7E, + 0x76, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 47 (0x2F) Char: 0x004F ('O') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 48 (0x30) Char: 0x0050 ('P') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x06, + 0x00, + 0x00, + 0x00, + + // Index: 49 (0x31) Char: 0x0051 ('Q') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x30, + 0x60, + 0x00, + + // Index: 50 (0x32) Char: 0x0052 ('R') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 51 (0x33) Char: 0x0053 ('S') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x0C, + 0x18, + 0x30, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 52 (0x34) Char: 0x0054 ('T') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 53 (0x35) Char: 0x0055 ('U') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 54 (0x36) Char: 0x0056 ('V') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 55 (0x37) Char: 0x0057 ('W') + 0x00, + 0x00, + 0x00, + 0x00, + 0x63, + 0x63, + 0x6B, + 0x6B, + 0x6B, + 0x6B, + 0x6B, + 0x36, + 0x36, + 0x00, + 0x00, + 0x00, + + // Index: 56 (0x38) Char: 0x0058 ('X') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 57 (0x39) Char: 0x0059 ('Y') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 58 (0x3A) Char: 0x005A ('Z') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x60, + 0x60, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 59 (0x3B) Char: 0x005B ('[') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 60 (0x3C) Char: 0x005C ('\') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x0C, + 0x0C, + 0x18, + 0x18, + 0x30, + 0x30, + 0x60, + 0x60, + 0x00, + 0x00, + + // Index: 61 (0x3D) Char: 0x005D (']') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x30, + 0x30, + 0x30, + 0x30, + 0x30, + 0x30, + 0x30, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 62 (0x3E) Char: 0x005E ('^') + 0x00, + 0x00, + 0x00, + 0x08, + 0x1C, + 0x36, + 0x63, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 63 (0x3F) Char: 0x005F ('_') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x00, + + // Index: 64 (0x40) Char: 0x0060 ('`') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 65 (0x41) Char: 0x0061 ('a') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 66 (0x42) Char: 0x0062 ('b') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 67 (0x43) Char: 0x0063 ('c') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 68 (0x44) Char: 0x0064 ('d') + 0x00, + 0x00, + 0x00, + 0x00, + 0x60, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 69 (0x45) Char: 0x0065 ('e') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 70 (0x46) Char: 0x0066 ('f') + 0x00, + 0x00, + 0x00, + 0x00, + 0x38, + 0x0C, + 0x0C, + 0x3E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x00, + 0x00, + 0x00, + + // Index: 71 (0x47) Char: 0x0067 ('g') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x33, + 0x33, + 0x1E, + 0x03, + 0x1E, + 0x33, + 0x33, + 0x1E, + 0x00, + + // Index: 72 (0x48) Char: 0x0068 ('h') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 73 (0x49) Char: 0x0069 ('i') + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 74 (0x4A) Char: 0x006A ('j') + 0x00, + 0x00, + 0x00, + 0x60, + 0x60, + 0x00, + 0x70, + 0x60, + 0x60, + 0x60, + 0x60, + 0x60, + 0x60, + 0x66, + 0x3C, + 0x00, + + // Index: 75 (0x4B) Char: 0x006B ('k') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x66, + 0x66, + 0x36, + 0x1E, + 0x36, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 76 (0x4C) Char: 0x006C ('l') + 0x00, + 0x00, + 0x00, + 0x00, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 77 (0x4D) Char: 0x006D ('m') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x6F, + 0xDB, + 0xDB, + 0xDB, + 0xDB, + 0xDB, + 0xDB, + 0x00, + 0x00, + 0x00, + + // Index: 78 (0x4E) Char: 0x006E ('n') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 79 (0x4F) Char: 0x006F ('o') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 80 (0x50) Char: 0x0070 ('p') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x06, + 0x06, + 0x00, + + // Index: 81 (0x51) Char: 0x0071 ('q') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x60, + 0x60, + 0x00, + + // Index: 82 (0x52) Char: 0x0072 ('r') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x06, + 0x06, + 0x06, + 0x06, + 0x00, + 0x00, + 0x00, + + // Index: 83 (0x53) Char: 0x0073 ('s') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x06, + 0x3C, + 0x60, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 84 (0x54) Char: 0x0074 ('t') + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x0C, + 0x3E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x38, + 0x00, + 0x00, + 0x00, + + // Index: 85 (0x55) Char: 0x0075 ('u') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 86 (0x56) Char: 0x0076 ('v') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 87 (0x57) Char: 0x0077 ('w') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x63, + 0x6B, + 0x6B, + 0x6B, + 0x6B, + 0x36, + 0x36, + 0x00, + 0x00, + 0x00, + + // Index: 88 (0x58) Char: 0x0078 ('x') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x3C, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 89 (0x59) Char: 0x0079 ('y') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x60, + 0x66, + 0x3C, + 0x00, + + // Index: 90 (0x5A) Char: 0x007A ('z') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x60, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 91 (0x5B) Char: 0x007B ('{') + 0x00, + 0x00, + 0x00, + 0x00, + 0x70, + 0x18, + 0x18, + 0x18, + 0x0E, + 0x18, + 0x18, + 0x18, + 0x70, + 0x00, + 0x00, + 0x00, + + // Index: 92 (0x5C) Char: 0x007C ('|') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 93 (0x5D) Char: 0x007D ('}') + 0x00, + 0x00, + 0x00, + 0x00, + 0x0E, + 0x18, + 0x18, + 0x18, + 0x70, + 0x18, + 0x18, + 0x18, + 0x0E, + 0x00, + 0x00, + 0x00, + + // Index: 94 (0x5E) Char: 0x007E ('~') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 95 (0x5F) Char: 0x007F ('') + 0x00, + 0x00, + 0x7C, + 0xC6, + 0x92, + 0x92, + 0x82, + 0xBA, + 0xFE, + 0xEE, + 0xD6, + 0xD6, + 0xEE, + 0xFE, + 0x7C, + 0x00, + + // Index: 96 (0x60) Char: 0x00A0 (' ') + 0x00, + 0x00, + 0x7C, + 0xFE, + 0xEE, + 0xEE, + 0xEE, + 0xEE, + 0xFE, + 0xBA, + 0x82, + 0xAA, + 0x92, + 0xC6, + 0x7C, + 0x00, + + // Index: 97 (0x61) Char: 0x00A1 ('¡') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + + // Index: 98 (0x62) Char: 0x00A2 ('¢') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x3C, + 0x66, + 0x06, + 0x66, + 0x3C, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 99 (0x63) Char: 0x00A3 ('£') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x1F, + 0x06, + 0x06, + 0x66, + 0x3F, + 0x00, + 0x00, + 0x00, + + // Index: 100 (0x64) Char: 0x00A4 ('¤') + 0x00, + 0x00, + 0x00, + 0x22, + 0x1C, + 0x36, + 0x22, + 0x36, + 0x1C, + 0x22, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 101 (0x65) Char: 0x00A5 ('¥') + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x7E, + 0x18, + 0x7E, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 102 (0x66) Char: 0x00A6 ('¦') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 103 (0x67) Char: 0x00A7 ('§') + 0x00, + 0x00, + 0x00, + 0x3C, + 0x46, + 0x0E, + 0x1A, + 0x36, + 0x6C, + 0x58, + 0x70, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 104 (0x68) Char: 0x00A8 ('¨') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 105 (0x69) Char: 0x00A9 ('©') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x42, + 0x99, + 0x85, + 0x85, + 0x99, + 0x42, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 106 (0x6A) Char: 0x00AA ('ª') + 0x00, + 0x00, + 0x1C, + 0x20, + 0x3C, + 0x36, + 0x3C, + 0x00, + 0x3E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 107 (0x6B) Char: 0x00AB ('«') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xCC, + 0x66, + 0x33, + 0x66, + 0xCC, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 108 (0x6C) Char: 0x00AC ('¬') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x60, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 109 (0x6D) Char: 0x00AD ('­') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 110 (0x6E) Char: 0x00AE ('®') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x42, + 0x9D, + 0xA5, + 0x9D, + 0xA5, + 0x42, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 111 (0x6F) Char: 0x00AF ('¯') + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 112 (0x70) Char: 0x00B0 ('°') + 0x00, + 0x00, + 0x00, + 0x1C, + 0x36, + 0x36, + 0x36, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 113 (0x71) Char: 0x00B1 ('±') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x7E, + 0x18, + 0x18, + 0x00, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 114 (0x72) Char: 0x00B2 ('²') + 0x00, + 0x04, + 0x0A, + 0x08, + 0x04, + 0x02, + 0x0E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 115 (0x73) Char: 0x00B3 ('³') + 0x00, + 0x06, + 0x08, + 0x06, + 0x08, + 0x08, + 0x06, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 116 (0x74) Char: 0x00B4 ('´') + 0x00, + 0x00, + 0x00, + 0x18, + 0x0C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 117 (0x75) Char: 0x00B5 ('µ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x06, + 0x06, + 0x00, + + // Index: 118 (0x76) Char: 0x00B6 ('¶') + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x6F, + 0x6F, + 0x6F, + 0x6E, + 0x6C, + 0x6C, + 0x6C, + 0x6C, + 0x00, + 0x00, + 0x00, + + // Index: 119 (0x77) Char: 0x00B7 ('·') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 120 (0x78) Char: 0x00B8 ('¸') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x0C, + 0x00, + + // Index: 121 (0x79) Char: 0x00B9 ('¹') + 0x00, + 0x04, + 0x06, + 0x04, + 0x04, + 0x04, + 0x0E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 122 (0x7A) Char: 0x00BA ('º') + 0x00, + 0x0E, + 0x0A, + 0x0E, + 0x00, + 0x0E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 123 (0x7B) Char: 0x00BB ('»') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x33, + 0x66, + 0xCC, + 0x66, + 0x33, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 124 (0x7C) Char: 0x00BC ('¼') + 0x00, + 0x04, + 0x06, + 0x84, + 0x44, + 0x24, + 0x0E, + 0xA0, + 0xA8, + 0xA4, + 0xE2, + 0x80, + 0x80, + 0x00, + 0x00, + 0x00, + + // Index: 125 (0x7D) Char: 0x00BD ('½') + 0x00, + 0x04, + 0x06, + 0x84, + 0x44, + 0x24, + 0x0E, + 0x40, + 0xA8, + 0x84, + 0x42, + 0x20, + 0xE0, + 0x00, + 0x00, + 0x00, + + // Index: 126 (0x7E) Char: 0x00BE ('¾') + 0x00, + 0x06, + 0x08, + 0x86, + 0x48, + 0x28, + 0x06, + 0xA0, + 0xA8, + 0xA4, + 0xE2, + 0x80, + 0x80, + 0x00, + 0x00, + 0x00, + + // Index: 127 (0x7F) Char: 0x00BF ('¿') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x18, + 0x0C, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + + // Index: 128 (0x80) Char: 0x00C0 ('À') + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 129 (0x81) Char: 0x00C1 ('Á') + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 130 (0x82) Char: 0x00C2 ('Â') + 0x18, + 0x3C, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 131 (0x83) Char: 0x00C3 ('Ã') + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x1E, + 0x33, + 0x33, + 0x33, + 0x33, + 0x3F, + 0x33, + 0x33, + 0x33, + 0x00, + 0x00, + 0x00, + + // Index: 132 (0x84) Char: 0x00C4 ('Ä') + 0x00, + 0x66, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 133 (0x85) Char: 0x00C5 ('Å') + 0x3C, + 0x66, + 0x3C, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7E, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 134 (0x86) Char: 0x00C6 ('Æ') + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x33, + 0x33, + 0x33, + 0xF3, + 0x3F, + 0x33, + 0x33, + 0xF3, + 0x00, + 0x00, + 0x00, + + // Index: 135 (0x87) Char: 0x00C7 ('Ç') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x06, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x18, + 0x0C, + + // Index: 136 (0x88) Char: 0x00C8 ('È') + 0x00, + 0x0C, + 0x18, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 137 (0x89) Char: 0x00C9 ('É') + 0x00, + 0x30, + 0x18, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 138 (0x8A) Char: 0x00CA ('Ê') + 0x18, + 0x3C, + 0x66, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 139 (0x8B) Char: 0x00CB ('Ë') + 0x00, + 0x66, + 0x66, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 140 (0x8C) Char: 0x00CC ('Ì') + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 141 (0x8D) Char: 0x00CD ('Í') + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 142 (0x8E) Char: 0x00CE ('Î') + 0x0C, + 0x1E, + 0x32, + 0x00, + 0x1E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 143 (0x8F) Char: 0x00CF ('Ï') + 0x00, + 0x32, + 0x32, + 0x00, + 0x1E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 144 (0x90) Char: 0x00D0 ('Ð') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x6E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 145 (0x91) Char: 0x00D1 ('Ñ') + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x33, + 0x33, + 0x37, + 0x3F, + 0x3B, + 0x33, + 0x33, + 0x33, + 0x33, + 0x00, + 0x00, + 0x00, + + // Index: 146 (0x92) Char: 0x00D2 ('Ò') + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 147 (0x93) Char: 0x00D3 ('Ó') + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 148 (0x94) Char: 0x00D4 ('Ô') + 0x18, + 0x3C, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 149 (0x95) Char: 0x00D5 ('Õ') + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x1E, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 150 (0x96) Char: 0x00D6 ('Ö') + 0x00, + 0x66, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 151 (0x97) Char: 0x00D7 ('×') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x3C, + 0x66, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 152 (0x98) Char: 0x00D8 ('Ø') + 0x00, + 0x00, + 0x00, + 0x00, + 0x5E, + 0x73, + 0x33, + 0x3B, + 0x3F, + 0x37, + 0x33, + 0x33, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 153 (0x99) Char: 0x00D9 ('Ù') + 0x00, + 0x0C, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 154 (0x9A) Char: 0x00DA ('Ú') + 0x00, + 0x30, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 155 (0x9B) Char: 0x00DB ('Û') + 0x18, + 0x3C, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 156 (0x9C) Char: 0x00DC ('Ü') + 0x00, + 0x66, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 157 (0x9D) Char: 0x00DD ('Ý') + 0x00, + 0x30, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 158 (0x9E) Char: 0x00DE ('Þ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x06, + 0x06, + 0x00, + 0x00, + 0x00, + + // Index: 159 (0x9F) Char: 0x00DF ('ß') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x36, + 0x66, + 0x66, + 0x66, + 0x36, + 0x00, + 0x00, + 0x00, + + // Index: 160 (0xA0) Char: 0x00E0 ('à') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 161 (0xA1) Char: 0x00E1 ('á') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 162 (0xA2) Char: 0x00E2 ('â') + 0x00, + 0x00, + 0x18, + 0x3C, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 163 (0xA3) Char: 0x00E3 ('ã') + 0x00, + 0x00, + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x1E, + 0x33, + 0x30, + 0x3E, + 0x33, + 0x33, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 164 (0xA4) Char: 0x00E4 ('ä') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 165 (0xA5) Char: 0x00E5 ('å') + 0x00, + 0x00, + 0x3C, + 0x66, + 0x3C, + 0x00, + 0x3C, + 0x66, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 166 (0xA6) Char: 0x00E6 ('æ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x33, + 0x30, + 0xFE, + 0x33, + 0x33, + 0xFE, + 0x00, + 0x00, + 0x00, + + // Index: 167 (0xA7) Char: 0x00E7 ('ç') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x18, + 0x0C, + + // Index: 168 (0xA8) Char: 0x00E8 ('è') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 169 (0xA9) Char: 0x00E9 ('é') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 170 (0xAA) Char: 0x00EA ('ê') + 0x00, + 0x00, + 0x18, + 0x3C, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 171 (0xAB) Char: 0x00EB ('ë') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 172 (0xAC) Char: 0x00EC ('ì') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 173 (0xAD) Char: 0x00ED ('í') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 174 (0xAE) Char: 0x00EE ('î') + 0x00, + 0x00, + 0x0C, + 0x1E, + 0x32, + 0x00, + 0x0E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 175 (0xAF) Char: 0x00EF ('ï') + 0x00, + 0x00, + 0x00, + 0x32, + 0x32, + 0x00, + 0x0E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 176 (0xB0) Char: 0x00F0 ('ð') + 0x00, + 0x00, + 0x00, + 0x00, + 0x30, + 0x7C, + 0x30, + 0x3E, + 0x33, + 0x33, + 0x33, + 0x33, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 177 (0xB1) Char: 0x00F1 ('ñ') + 0x00, + 0x00, + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x1F, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x00, + 0x00, + 0x00, + + // Index: 178 (0xB2) Char: 0x00F2 ('ò') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 179 (0xB3) Char: 0x00F3 ('ó') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 180 (0xB4) Char: 0x00F4 ('ô') + 0x00, + 0x00, + 0x18, + 0x3C, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 181 (0xB5) Char: 0x00F5 ('õ') + 0x00, + 0x00, + 0x00, + 0x6E, + 0x3B, + 0x00, + 0x1E, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x1E, + 0x00, + 0x00, + 0x00, + + // Index: 182 (0xB6) Char: 0x00F6 ('ö') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 183 (0xB7) Char: 0x00F7 ('÷') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x7E, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 184 (0xB8) Char: 0x00F8 ('ø') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x7E, + 0x33, + 0x3B, + 0x3F, + 0x37, + 0x33, + 0x1F, + 0x00, + 0x00, + 0x00, + + // Index: 185 (0xB9) Char: 0x00F9 ('ù') + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 186 (0xBA) Char: 0x00FA ('ú') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 187 (0xBB) Char: 0x00FB ('û') + 0x00, + 0x00, + 0x18, + 0x3C, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 188 (0xBC) Char: 0x00FC ('ü') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 189 (0xBD) Char: 0x00FD ('ý') + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x60, + 0x66, + 0x3C, + 0x00, + + // Index: 190 (0xBE) Char: 0x00FE ('þ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, + 0x06, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x06, + 0x06, + 0x00, + + // Index: 191 (0xBF) Char: 0x00FF ('ÿ') + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x60, + 0x66, + 0x3C, + 0x00, + + // Index: 192 (0xC0) Char: 0x010C ('C') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x06, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 193 (0xC1) Char: 0x010D ('c') + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x06, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 194 (0xC2) Char: 0x010E ('D') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 195 (0xC3) Char: 0x010F ('d') + 0x00, + 0x00, + 0x00, + 0x00, + 0x60, + 0x60, + 0x7C, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 196 (0xC4) Char: 0x011A ('E') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x7E, + 0x06, + 0x06, + 0x06, + 0x3E, + 0x06, + 0x06, + 0x06, + 0x7E, + 0x00, + 0x00, + 0x00, + + // Index: 197 (0xC5) Char: 0x011B ('e') + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x7E, + 0x06, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 198 (0xC6) Char: 0x0131 ('i') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x1C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 199 (0xC7) Char: 0x0147 ('N') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x66, + 0x66, + 0x6E, + 0x7E, + 0x76, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 200 (0xC8) Char: 0x0148 ('n') + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 201 (0xC9) Char: 0x0152 ('Œ') + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x33, + 0x33, + 0x33, + 0xF3, + 0x33, + 0x33, + 0x33, + 0xFE, + 0x00, + 0x00, + 0x00, + + // Index: 202 (0xCA) Char: 0x0153 ('œ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFE, + 0x33, + 0x33, + 0xF3, + 0x33, + 0x33, + 0xFE, + 0x00, + 0x00, + 0x00, + + // Index: 203 (0xCB) Char: 0x0158 ('R') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x3E, + 0x66, + 0x66, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 204 (0xCC) Char: 0x0159 ('r') + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3E, + 0x66, + 0x66, + 0x06, + 0x06, + 0x06, + 0x06, + 0x00, + 0x00, + 0x00, + + // Index: 205 (0xCD) Char: 0x0160 ('Š') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x0C, + 0x18, + 0x30, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 206 (0xCE) Char: 0x0161 ('š') + 0x00, + 0x00, + 0x66, + 0x3C, + 0x18, + 0x00, + 0x3C, + 0x66, + 0x06, + 0x3C, + 0x60, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 207 (0xCF) Char: 0x0164 ('T') + 0x66, + 0x3C, + 0x18, + 0x00, + 0x7E, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 208 (0xD0) Char: 0x0165 ('t') + 0x00, + 0x00, + 0x00, + 0x00, + 0x86, + 0x86, + 0x1F, + 0x86, + 0x06, + 0x06, + 0x06, + 0x06, + 0x1C, + 0x00, + 0x00, + 0x00, + + // Index: 209 (0xD1) Char: 0x016E ('U') + 0x3C, + 0x66, + 0x3C, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 210 (0xD2) Char: 0x016F ('u') + 0x00, + 0x00, + 0x3C, + 0x66, + 0x3C, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x7C, + 0x00, + 0x00, + 0x00, + + // Index: 211 (0xD3) Char: 0x0178 ('Ÿ') + 0x00, + 0x66, + 0x66, + 0x00, + 0x66, + 0x66, + 0x66, + 0x66, + 0x3C, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 212 (0xD4) Char: 0x017D ('Ž') + 0x32, + 0x1E, + 0x0C, + 0x00, + 0x3E, + 0x30, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x02, + 0x02, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 213 (0xD5) Char: 0x017E ('ž') + 0x00, + 0x00, + 0x32, + 0x1E, + 0x0C, + 0x00, + 0x3E, + 0x30, + 0x18, + 0x0C, + 0x06, + 0x02, + 0x3E, + 0x00, + 0x00, + 0x00, + + // Index: 214 (0xD6) Char: 0x0192 ('ƒ') + 0x00, + 0x00, + 0x00, + 0x00, + 0x38, + 0x0C, + 0x0C, + 0x3E, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x0C, + 0x06, + 0x00, + + // Index: 215 (0xD7) Char: 0x2014 ('—') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 216 (0xD8) Char: 0x2018 ('‘') + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, + 0x08, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 217 (0xD9) Char: 0x2019 ('’') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x10, + 0x08, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 218 (0xDA) Char: 0x201A ('‚') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x10, + 0x08, + 0x00, + + // Index: 219 (0xDB) Char: 0x201C ('“') + 0x00, + 0x00, + 0x00, + 0x00, + 0x24, + 0x12, + 0x36, + 0x36, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 220 (0xDC) Char: 0x201D ('”') + 0x00, + 0x00, + 0x00, + 0x00, + 0x36, + 0x36, + 0x24, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 221 (0xDD) Char: 0x201E ('„') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x36, + 0x36, + 0x24, + 0x12, + 0x00, + + // Index: 222 (0xDE) Char: 0x2020 ('†') + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x7E, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + + // Index: 223 (0xDF) Char: 0x2022 ('•') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x18, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 224 (0xE0) Char: 0x2026 ('…') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x66, + 0x66, + 0x00, + 0x00, + 0x00, + + // Index: 225 (0xE1) Char: 0x2039 ('‹') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x30, + 0x18, + 0x0C, + 0x18, + 0x30, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 226 (0xE2) Char: 0x203A ('›') + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0C, + 0x18, + 0x30, + 0x18, + 0x0C, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + // Index: 227 (0xE3) Char: 0x20AC ('€') + 0x00, + 0x00, + 0x00, + 0x00, + 0x3C, + 0x66, + 0x66, + 0x1F, + 0x06, + 0x1F, + 0x66, + 0x66, + 0x3C, + 0x00, + 0x00, + 0x00, + + // Index: 228 (0xE4) Char: 0x2122 ('™') + 0x00, + 0x00, + 0x00, + 0x00, + 0xDF, + 0xAA, + 0x8A, + 0x8A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, }; } -const ui::Font fixed_8x16 { - 8, 16, - fixed_8x16_glyph_data, - 0x20, 223, +const ui::Font fixed_8x16{ + 8, + 16, + fixed_8x16_glyph_data, + 0x20, + 223, }; } /* namespace font */ diff --git a/firmware/application/ui/ui_font_fixed_8x16.hpp b/firmware/application/ui/ui_font_fixed_8x16.hpp index 32bf53731..70c9130d4 100644 --- a/firmware/application/ui/ui_font_fixed_8x16.hpp +++ b/firmware/application/ui/ui_font_fixed_8x16.hpp @@ -30,6 +30,6 @@ namespace font { extern const ui::Font fixed_8x16; } /* namespace font */ -} /* namspace ui */ +} // namespace ui -#endif/*__UI_FONT_FIXED_8X16_H__*/ +#endif /*__UI_FONT_FIXED_8X16_H__*/ diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index e2a30aa90..b422ed29b 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -35,428 +35,410 @@ using namespace portapack; namespace ui { GeoPos::GeoPos( - const Point pos, - const alt_unit altitude_unit -) : altitude_unit_(altitude_unit) { - - set_parent_rect({pos, { 30 * 8, 3 * 16 }}); - - add_children({ - &labels_position, - &field_altitude, - &text_alt_unit, - &field_lat_degrees, - &field_lat_minutes, - &field_lat_seconds, - &text_lat_decimal, - &field_lon_degrees, - &field_lon_minutes, - &field_lon_seconds, - &text_lon_decimal - }); - - // Defaults - set_altitude(0); - set_lat(0); - set_lon(0); - - const auto changed_fn = [this](int32_t) { - float lat_value = lat(); - float lon_value = lon(); - - text_lat_decimal.set(to_string_decimal(lat_value, 5)); - text_lon_decimal.set(to_string_decimal(lon_value, 5)); - - if (on_change && report_change) - on_change(altitude(), lat_value, lon_value); - }; - - field_altitude.on_change = changed_fn; - field_lat_degrees.on_change = changed_fn; - field_lat_minutes.on_change = changed_fn; - field_lat_seconds.on_change = changed_fn; - field_lon_degrees.on_change = changed_fn; - field_lon_minutes.on_change = changed_fn; - field_lon_seconds.on_change = changed_fn; - - text_alt_unit.set(altitude_unit_ ? "m" : "ft"); + const Point pos, + const alt_unit altitude_unit) + : altitude_unit_(altitude_unit) { + set_parent_rect({pos, {30 * 8, 3 * 16}}); + + add_children({&labels_position, + &field_altitude, + &text_alt_unit, + &field_lat_degrees, + &field_lat_minutes, + &field_lat_seconds, + &text_lat_decimal, + &field_lon_degrees, + &field_lon_minutes, + &field_lon_seconds, + &text_lon_decimal}); + + // Defaults + set_altitude(0); + set_lat(0); + set_lon(0); + + const auto changed_fn = [this](int32_t) { + float lat_value = lat(); + float lon_value = lon(); + + text_lat_decimal.set(to_string_decimal(lat_value, 5)); + text_lon_decimal.set(to_string_decimal(lon_value, 5)); + + if (on_change && report_change) + on_change(altitude(), lat_value, lon_value); + }; + + field_altitude.on_change = changed_fn; + field_lat_degrees.on_change = changed_fn; + field_lat_minutes.on_change = changed_fn; + field_lat_seconds.on_change = changed_fn; + field_lon_degrees.on_change = changed_fn; + field_lon_minutes.on_change = changed_fn; + field_lon_seconds.on_change = changed_fn; + + text_alt_unit.set(altitude_unit_ ? "m" : "ft"); } void GeoPos::set_read_only(bool v) { - for(auto child : children_) - child->set_focusable(!v); + for (auto child : children_) + child->set_focusable(!v); } // Stupid hack to avoid an event loop void GeoPos::set_report_change(bool v) { - report_change = v; + report_change = v; } void GeoPos::focus() { - field_altitude.focus(); + field_altitude.focus(); } void GeoPos::set_altitude(int32_t altitude) { - field_altitude.set_value(altitude); + field_altitude.set_value(altitude); } void GeoPos::set_lat(float lat) { - field_lat_degrees.set_value(lat); - field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60); - field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60); + field_lat_degrees.set_value(lat); + field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60); + field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60); } void GeoPos::set_lon(float lon) { - field_lon_degrees.set_value(lon); - field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60); - field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60); + field_lon_degrees.set_value(lon); + field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60); + field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60); } float GeoPos::lat() { - if (field_lat_degrees.value() < 0) { - return -1 * ( -1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0)); - } else { - return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0); - } + if (field_lat_degrees.value() < 0) { + return -1 * (-1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0)); + } else { + return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0); + } }; float GeoPos::lon() { - if (field_lon_degrees.value() < 0) { - return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0)); - } else { - return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0); - } + if (field_lon_degrees.value() < 0) { + return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0)); + } else { + return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0); + } }; int32_t GeoPos::altitude() { - return field_altitude.value(); + return field_altitude.value(); }; GeoMap::GeoMap( - Rect parent_rect -) : Widget { parent_rect }, markerListLen(0) -{ - //set_focusable(true); + Rect parent_rect) + : Widget{parent_rect}, markerListLen(0) { + // set_focusable(true); } void GeoMap::paint(Painter& painter) { - u_int16_t line; - std::array map_line_buffer; - const auto r = screen_rect(); - - // Ony redraw map if it moved by at least 1 pixel - // or the markers list was updated - int x_diff = abs(x_pos-prev_x_pos); - int y_diff = abs(y_pos-prev_y_pos); - if (markerListUpdated || (x_diff>=3) || (y_diff>=3)) { - for (line = 0; line < r.height(); line++) { - map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1)); - map_file.read(map_line_buffer.data(), r.width() << 1); - display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer); - } - prev_x_pos = x_pos; - prev_y_pos = y_pos; - - // Draw the other markers - for ( int i=0; i=0) && (x10) && (y=32) { // Dont draw text if it would overlap top - // Text and symbol - draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta() ); - } else { - // Only symbol - draw_bearing( itemPoint, item.angle, 10, Color::blue()); - } - } - markerListUpdated = false; - } // Draw the other markers - } - - //Draw the marker in the center - draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black() ); + u_int16_t line; + std::array map_line_buffer; + const auto r = screen_rect(); + + // Ony redraw map if it moved by at least 1 pixel + // or the markers list was updated + int x_diff = abs(x_pos - prev_x_pos); + int y_diff = abs(y_pos - prev_y_pos); + if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) { + for (line = 0; line < r.height(); line++) { + map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1)); + map_file.read(map_line_buffer.data(), r.width() << 1); + display.draw_pixels({0, r.top() + line, r.width(), 1}, map_line_buffer); + } + prev_x_pos = x_pos; + prev_y_pos = y_pos; + + // Draw the other markers + for (int i = 0; i < markerListLen; ++i) { + GeoMarker& item = markerList[i]; + double lat_rad = sin(item.lat * pi / 180); + int x = (map_width * (item.lon + 180) / 360) - x_pos; + int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI + if ((x >= 0) && (x < r.width()) && + (y > 10) && (y < r.height())) // Dont draw within symbol size of top + { + ui::Point itemPoint(x, y + r.top()); + if (y >= 32) { // Dont draw text if it would overlap top + // Text and symbol + draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta()); + } else { + // Only symbol + draw_bearing(itemPoint, item.angle, 10, Color::blue()); + } + } + markerListUpdated = false; + } // Draw the other markers + } + + // Draw the marker in the center + draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black()); } bool GeoMap::on_touch(const TouchEvent event) { - if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) { - set_highlighted(true); - if (on_move) { - Point p = event.point - screen_rect().center(); - on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio); - return true; - } - } - return false; + if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) { + set_highlighted(true); + if (on_move) { + Point p = event.point - screen_rect().center(); + on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio); + return true; + } + } + return false; } void GeoMap::move(const float lon, const float lat) { - lon_ = lon; - lat_ = lat; - - Rect map_rect = screen_rect(); - - // Using WGS 84/Pseudo-Mercator projection - x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2); - - // Latitude calculation based on https://stackoverflow.com/a/10401734/2278659 - double lat_rad = sin(lat * pi / 180); - y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI - - // Cap position - if (x_pos > (map_width - map_rect.width())) - x_pos = map_width - map_rect.width(); - if (y_pos > (map_height + map_rect.height())) - y_pos = map_height - map_rect.height(); + lon_ = lon; + lat_ = lat; + + Rect map_rect = screen_rect(); + + // Using WGS 84/Pseudo-Mercator projection + x_pos = map_width * (lon_ + 180) / 360 - (map_rect.width() / 2); + + // Latitude calculation based on https://stackoverflow.com/a/10401734/2278659 + double lat_rad = sin(lat * pi / 180); + y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI + + // Cap position + if (x_pos > (map_width - map_rect.width())) + x_pos = map_width - map_rect.width(); + if (y_pos > (map_height + map_rect.height())) + y_pos = map_height - map_rect.height(); } bool GeoMap::init() { - auto result = map_file.open("ADSB/world_map.bin"); - if (result.is_valid()) - return false; - - map_file.read(&map_width, 2); - map_file.read(&map_height, 2); - - map_center_x = map_width >> 1; - map_center_y = map_height >> 1; - - lon_ratio = 180.0 / map_center_x; - lat_ratio = -90.0 / map_center_y; - - map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat - map_world_lon = map_width / (2 * pi); + auto result = map_file.open("ADSB/world_map.bin"); + if (result.is_valid()) + return false; + + map_file.read(&map_width, 2); + map_file.read(&map_height, 2); + + map_center_x = map_width >> 1; + map_center_y = map_height >> 1; + + lon_ratio = 180.0 / map_center_x; + lat_ratio = -90.0 / map_center_y; + + map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat + map_world_lon = map_width / (2 * pi); map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom))); - return true; + return true; } void GeoMap::set_mode(GeoMapMode mode) { - mode_ = mode; + mode_ = mode; } void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) { - Point arrow_a, arrow_b, arrow_c; - - for (size_t thickness = 0; thickness < 3; thickness++) { - arrow_a = fast_polar_to_point((int)angle, size) + origin; - arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin; - arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin; - - display.draw_line(arrow_a, arrow_b, color); - display.draw_line(arrow_b, arrow_c, color); - display.draw_line(arrow_c, arrow_a, color); - - size--; - } -} + Point arrow_a, arrow_b, arrow_c; + + for (size_t thickness = 0; thickness < 3; thickness++) { + arrow_a = fast_polar_to_point((int)angle, size) + origin; + arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin; + arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin; + + display.draw_line(arrow_a, arrow_b, color); + display.draw_line(arrow_b, arrow_c, color); + display.draw_line(arrow_c, arrow_a, color); -void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, - const Color color, const Color fontColor, const Color backColor ) -{ - int tagOffset = 10; - if (mode_ == PROMPT) { - // Cross - display.fill_rectangle({ itemPoint - Point(16, 1), { 32, 2 } }, color); - display.fill_rectangle({ itemPoint - Point(1, 16), { 2, 32 } }, color); - tagOffset = 16; - } else if (angle_ < 360){ - //if we have a valid angle draw bearing - draw_bearing( itemPoint, itemAngle, 10, color); - tagOffset = 10; - } - else { - //draw a small cross - display.fill_rectangle({ itemPoint - Point(8, 1), { 16, 2 } }, color); - display.fill_rectangle({ itemPoint - Point(1, 8), { 2, 16 } }, color); - tagOffset = 8; - } - //center tag above point - if(itemTag.find_first_not_of(' ') != itemTag.npos){ //only draw tag if we have something other than spaces - painter.draw_string( itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset ), - style().font, fontColor, backColor, itemTag); - } + size--; + } } -void GeoMap::clear_markers() -{ - markerListLen = 0; +void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color, const Color fontColor, const Color backColor) { + int tagOffset = 10; + if (mode_ == PROMPT) { + // Cross + display.fill_rectangle({itemPoint - Point(16, 1), {32, 2}}, color); + display.fill_rectangle({itemPoint - Point(1, 16), {2, 32}}, color); + tagOffset = 16; + } else if (angle_ < 360) { + // if we have a valid angle draw bearing + draw_bearing(itemPoint, itemAngle, 10, color); + tagOffset = 10; + } else { + // draw a small cross + display.fill_rectangle({itemPoint - Point(8, 1), {16, 2}}, color); + display.fill_rectangle({itemPoint - Point(1, 8), {2, 16}}, color); + tagOffset = 8; + } + // center tag above point + if (itemTag.find_first_not_of(' ') != itemTag.npos) { // only draw tag if we have something other than spaces + painter.draw_string(itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset), + style().font, fontColor, backColor, itemTag); + } } -MapMarkerStored GeoMap::store_marker(GeoMarker & marker) -{ - MapMarkerStored ret; - - // Check if it could be on screen - // Only checking one direction to reduce CPU - const auto r = screen_rect(); - double lat_rad = sin(marker.lat * pi / 180); - int x = (map_width * (marker.lon+180)/360) - x_pos; - int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI - if (false==((x>=0) && (x10) && (y= 0) && (x < r.width()) && (y > 10) && (y < r.height()))) // Dont draw within symbol size of top + { + ret = MARKER_NOT_STORED; + } else if (markerListLen < NumMarkerListElements) { + markerList[markerListLen] = marker; + markerListLen++; + markerListUpdated = true; + ret = MARKER_STORED; + } else { + ret = MARKER_LIST_FULL; + } + return ret; +} void GeoMapView::focus() { - geopos.focus(); - - if (!map_opened) - nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr); + geopos.focus(); + + if (!map_opened) + nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr); } void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) { - lat_ = lat; - lon_ = lon; - altitude_ = altitude; - - // Stupid hack to avoid an event loop - geopos.set_report_change(false); - geopos.set_lat(lat_); - geopos.set_lon(lon_); - geopos.set_altitude(altitude_); - geopos.set_report_change(true); - - geomap.set_angle(angle); - geomap.move(lon_, lat_); - geomap.set_dirty(); + lat_ = lat; + lon_ = lon; + altitude_ = altitude; + + // Stupid hack to avoid an event loop + geopos.set_report_change(false); + geopos.set_lat(lat_); + geopos.set_lon(lon_); + geopos.set_altitude(altitude_); + geopos.set_report_change(true); + + geomap.set_angle(angle); + geomap.move(lon_, lat_); + geomap.set_dirty(); } void GeoMapView::update_tag(const std::string tag) { - geomap.set_tag(tag); + geomap.set_tag(tag); } - + void GeoMapView::setup() { - add_child(&geomap); - - geopos.set_altitude(altitude_); - geopos.set_lat(lat_); - geopos.set_lon(lon_); - - geopos.on_change = [this](int32_t altitude, float lat, float lon) { - altitude_ = altitude; - lat_ = lat; - lon_ = lon; - geomap.move(lon_, lat_); - geomap.set_dirty(); - }; - - geomap.on_move = [this](float move_x, float move_y) { - lon_ += move_x; - lat_ += move_y; - - // Stupid hack to avoid an event loop - geopos.set_report_change(false); - geopos.set_lon(lon_); - geopos.set_lat(lat_); - geopos.set_report_change(true); - - geomap.move(lon_, lat_); - geomap.set_dirty(); - }; + add_child(&geomap); + + geopos.set_altitude(altitude_); + geopos.set_lat(lat_); + geopos.set_lon(lon_); + + geopos.on_change = [this](int32_t altitude, float lat, float lon) { + altitude_ = altitude; + lat_ = lat; + lon_ = lon; + geomap.move(lon_, lat_); + geomap.set_dirty(); + }; + + geomap.on_move = [this](float move_x, float move_y) { + lon_ += move_x; + lat_ += move_y; + + // Stupid hack to avoid an event loop + geopos.set_report_change(false); + geopos.set_lon(lon_); + geopos.set_lat(lat_); + geopos.set_report_change(true); + + geomap.move(lon_, lat_); + geomap.set_dirty(); + }; } - GeoMapView::~GeoMapView() { - if (on_close_) - on_close_(); + if (on_close_) + on_close_(); } // Display mode GeoMapView::GeoMapView( - NavigationView& nav, - const std::string& tag, - int32_t altitude, - GeoPos::alt_unit altitude_unit, - float lat, - float lon, - uint16_t angle, - const std::function on_close -) : nav_ (nav), - altitude_ (altitude), - altitude_unit_ (altitude_unit), - lat_ (lat), - lon_ (lon), - angle_ (angle), - on_close_(on_close) -{ - mode_ = DISPLAY; - - add_child(&geopos); - - map_opened = geomap.init(); - if (!map_opened) return; - - setup(); - - geomap.set_mode(mode_); - geomap.set_tag(tag); - geomap.set_angle(angle); - geomap.move(lon_, lat_); - - geopos.set_read_only(true); + NavigationView& nav, + const std::string& tag, + int32_t altitude, + GeoPos::alt_unit altitude_unit, + float lat, + float lon, + uint16_t angle, + const std::function on_close) + : nav_(nav), + altitude_(altitude), + altitude_unit_(altitude_unit), + lat_(lat), + lon_(lon), + angle_(angle), + on_close_(on_close) { + mode_ = DISPLAY; + + add_child(&geopos); + + map_opened = geomap.init(); + if (!map_opened) return; + + setup(); + + geomap.set_mode(mode_); + geomap.set_tag(tag); + geomap.set_angle(angle); + geomap.move(lon_, lat_); + + geopos.set_read_only(true); } // Prompt mode GeoMapView::GeoMapView( - NavigationView& nav, - int32_t altitude, - GeoPos::alt_unit altitude_unit, - float lat, - float lon, - const std::function on_done -) : nav_ (nav), - altitude_ (altitude), - altitude_unit_ (altitude_unit), - lat_ (lat), - lon_ (lon) -{ - mode_ = PROMPT; - - add_child(&geopos); - - map_opened = geomap.init(); - if (!map_opened) return; - - setup(); - add_child(&button_ok); - - geomap.set_mode(mode_); - geomap.move(lon_, lat_); - - button_ok.on_select = [this, on_done, &nav](Button&) { - if (on_done) - on_done(altitude_, lat_, lon_); - nav.pop(); - }; + NavigationView& nav, + int32_t altitude, + GeoPos::alt_unit altitude_unit, + float lat, + float lon, + const std::function on_done) + : nav_(nav), + altitude_(altitude), + altitude_unit_(altitude_unit), + lat_(lat), + lon_(lon) { + mode_ = PROMPT; + + add_child(&geopos); + + map_opened = geomap.init(); + if (!map_opened) return; + + setup(); + add_child(&button_ok); + + geomap.set_mode(mode_); + geomap.move(lon_, lat_); + + button_ok.on_select = [this, on_done, &nav](Button&) { + if (on_done) + on_done(altitude_, lat_, lon_); + nav.pop(); + }; } -void GeoMapView::clear_markers() -{ - geomap.clear_markers(); +void GeoMapView::clear_markers() { + geomap.clear_markers(); } -MapMarkerStored GeoMapView::store_marker(GeoMarker & marker) -{ - return geomap.store_marker(marker); +MapMarkerStored GeoMapView::store_marker(GeoMarker& marker) { + return geomap.store_marker(marker); } } /* namespace ui */ diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp index 09094dc4f..86d7d9ed9 100644 --- a/firmware/application/ui/ui_geomap.hpp +++ b/firmware/application/ui/ui_geomap.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -33,230 +33,236 @@ namespace ui { enum GeoMapMode { - DISPLAY, - PROMPT + DISPLAY, + PROMPT }; struct GeoMarker { - public: - float lat {0}; - float lon {0}; - uint16_t angle {0}; - std::string tag {""}; - - GeoMarker & operator=(GeoMarker & rhs){ - lat = rhs.lat; - lon = rhs.lon; - angle = rhs.angle; - tag = rhs.tag; - - return *this; - } -}; + public: + float lat{0}; + float lon{0}; + uint16_t angle{0}; + std::string tag{""}; + GeoMarker& operator=(GeoMarker& rhs) { + lat = rhs.lat; + lon = rhs.lon; + angle = rhs.angle; + tag = rhs.tag; + + return *this; + } +}; class GeoPos : public View { -public: - enum alt_unit { - FEET = 0, - METERS - }; - - std::function on_change { }; - - GeoPos(const Point pos, const alt_unit altitude_unit); - - void focus() override; - - void set_read_only(bool v); - void set_altitude(int32_t altitude); - void set_lat(float lat); - void set_lon(float lon); - int32_t altitude(); - float lat(); - float lon(); - - void set_report_change(bool v); - -private: - bool read_only { false }; - bool report_change { true }; - alt_unit altitude_unit_ { }; - - Labels labels_position { - { { 1 * 8, 0 * 16 }, "Alt:", Color::light_grey() }, - { { 1 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font - { { 1 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() }, - }; - - NumberField field_altitude { - { 6 * 8, 0 * 16 }, - 5, - { -1000, 50000 }, - 250, - ' ' - }; - Text text_alt_unit { - { 12 * 8, 0 * 16, 2 * 8, 16 }, - "" - }; - - NumberField field_lat_degrees { - { 5 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' ' - }; - NumberField field_lat_minutes { - { 10 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - NumberField field_lat_seconds { - { 13 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - Text text_lat_decimal { - { 17 * 8, 1 * 16, 13 * 8, 1 * 16 }, - "" - }; - - NumberField field_lon_degrees { - { 5 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' ' - }; - NumberField field_lon_minutes { - { 10 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - NumberField field_lon_seconds { - { 13 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - Text text_lon_decimal { - { 17 * 8, 2 * 16, 13 * 8, 1 * 16 }, - "" - }; + public: + enum alt_unit { + FEET = 0, + METERS + }; + + std::function on_change{}; + + GeoPos(const Point pos, const alt_unit altitude_unit); + + void focus() override; + + void set_read_only(bool v); + void set_altitude(int32_t altitude); + void set_lat(float lat); + void set_lon(float lon); + int32_t altitude(); + float lat(); + float lon(); + + void set_report_change(bool v); + + private: + bool read_only{false}; + bool report_change{true}; + alt_unit altitude_unit_{}; + + Labels labels_position{ + {{1 * 8, 0 * 16}, "Alt:", Color::light_grey()}, + {{1 * 8, 1 * 16}, "Lat: * ' \"", Color::light_grey()}, // No ° symbol in 8x16 font + {{1 * 8, 2 * 16}, "Lon: * ' \"", Color::light_grey()}, + }; + + NumberField field_altitude{ + {6 * 8, 0 * 16}, + 5, + {-1000, 50000}, + 250, + ' '}; + Text text_alt_unit{ + {12 * 8, 0 * 16, 2 * 8, 16}, + ""}; + + NumberField field_lat_degrees{ + {5 * 8, 1 * 16}, + 4, + {-90, 90}, + 1, + ' '}; + NumberField field_lat_minutes{ + {10 * 8, 1 * 16}, + 2, + {0, 59}, + 1, + ' '}; + NumberField field_lat_seconds{ + {13 * 8, 1 * 16}, + 2, + {0, 59}, + 1, + ' '}; + Text text_lat_decimal{ + {17 * 8, 1 * 16, 13 * 8, 1 * 16}, + ""}; + + NumberField field_lon_degrees{ + {5 * 8, 2 * 16}, + 4, + {-180, 180}, + 1, + ' '}; + NumberField field_lon_minutes{ + {10 * 8, 2 * 16}, + 2, + {0, 59}, + 1, + ' '}; + NumberField field_lon_seconds{ + {13 * 8, 2 * 16}, + 2, + {0, 59}, + 1, + ' '}; + Text text_lon_decimal{ + {17 * 8, 2 * 16, 13 * 8, 1 * 16}, + ""}; }; enum MapMarkerStored { - MARKER_NOT_STORED, - MARKER_STORED, - MARKER_LIST_FULL + MARKER_NOT_STORED, + MARKER_STORED, + MARKER_LIST_FULL }; class GeoMap : public Widget { -public: - std::function on_move { }; - - GeoMap(Rect parent_rect); - - void paint(Painter& painter) override; - - bool on_touch(const TouchEvent event) override; - - bool init(); - void set_mode(GeoMapMode mode); - void move(const float lon, const float lat); - void set_tag(std::string new_tag) { - tag_ = new_tag; - } - - void set_angle(uint16_t new_angle){ - angle_ = new_angle; - } - - static const int NumMarkerListElements = 30; - - void clear_markers(); - MapMarkerStored store_marker(GeoMarker & marker); - -private: - void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); - void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, - const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black() ); - - GeoMapMode mode_ { }; - File map_file { }; - uint16_t map_width { }, map_height { }; - int32_t map_center_x { }, map_center_y { }; - float lon_ratio { }, lat_ratio { }; - double map_bottom { }; - double map_world_lon { }; - double map_offset { }; - - int32_t x_pos { }, y_pos { }; - int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF }; - float lat_ { }; - float lon_ { }; - uint16_t angle_ { }; - std::string tag_ { }; - - int markerListLen {0}; - GeoMarker markerList[NumMarkerListElements]; - bool markerListUpdated {false}; + public: + std::function on_move{}; + + GeoMap(Rect parent_rect); + + void paint(Painter& painter) override; + + bool on_touch(const TouchEvent event) override; + + bool init(); + void set_mode(GeoMapMode mode); + void move(const float lon, const float lat); + void set_tag(std::string new_tag) { + tag_ = new_tag; + } + + void set_angle(uint16_t new_angle) { + angle_ = new_angle; + } + + static const int NumMarkerListElements = 30; + + void clear_markers(); + MapMarkerStored store_marker(GeoMarker& marker); + + private: + void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); + void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black()); + + GeoMapMode mode_{}; + File map_file{}; + uint16_t map_width{}, map_height{}; + int32_t map_center_x{}, map_center_y{}; + float lon_ratio{}, lat_ratio{}; + double map_bottom{}; + double map_world_lon{}; + double map_offset{}; + + int32_t x_pos{}, y_pos{}; + int32_t prev_x_pos{0xFFFF}, prev_y_pos{0xFFFF}; + float lat_{}; + float lon_{}; + uint16_t angle_{}; + std::string tag_{}; + + int markerListLen{0}; + GeoMarker markerList[NumMarkerListElements]; + bool markerListUpdated{false}; }; class GeoMapView : public View { -public: - GeoMapView( - NavigationView& nav, - const std::string& tag, - int32_t altitude, - GeoPos::alt_unit altitude_unit, - float lat, - float lon, - uint16_t angle, - const std::function on_close = nullptr - ); - GeoMapView(NavigationView& nav, - int32_t altitude, - GeoPos::alt_unit altitude_unit, - float lat, - float lon, - const std::function on_done - ); - ~GeoMapView(); - - GeoMapView(const GeoMapView&) = delete; - GeoMapView(GeoMapView&&) = delete; - GeoMapView& operator=(const GeoMapView&) = delete; - GeoMapView& operator=(GeoMapView&&) = delete; - - void focus() override; - - void update_position(float lat, float lon, uint16_t angle, int32_t altitude); - - std::string title() const override { return "Map view"; }; - - void clear_markers(); - MapMarkerStored store_marker(GeoMarker & marker); - - void update_tag(const std::string tag); - - -private: - NavigationView& nav_; - - void setup(); - - const std::function on_done { }; - - const Dim banner_height = 3 * 16; - GeoMapMode mode_ { }; - int32_t altitude_ { }; - GeoPos::alt_unit altitude_unit_ { }; - float lat_ { }; - float lon_ { }; - uint16_t angle_ { }; - std::function on_close_ { nullptr }; - - bool map_opened { }; - - GeoPos geopos { - { 0, 0 }, - altitude_unit_ - }; - - GeoMap geomap { - { 0, banner_height, 240, 320 - 16 - banner_height } - }; - - Button button_ok { - { 20 * 8, 8, 8 * 8, 2 * 16 }, - "OK" - }; + public: + GeoMapView( + NavigationView& nav, + const std::string& tag, + int32_t altitude, + GeoPos::alt_unit altitude_unit, + float lat, + float lon, + uint16_t angle, + const std::function on_close = nullptr); + GeoMapView(NavigationView& nav, + int32_t altitude, + GeoPos::alt_unit altitude_unit, + float lat, + float lon, + const std::function on_done); + ~GeoMapView(); + + GeoMapView(const GeoMapView&) = delete; + GeoMapView(GeoMapView&&) = delete; + GeoMapView& operator=(const GeoMapView&) = delete; + GeoMapView& operator=(GeoMapView&&) = delete; + + void focus() override; + + void update_position(float lat, float lon, uint16_t angle, int32_t altitude); + + std::string title() const override { return "Map view"; }; + + void clear_markers(); + MapMarkerStored store_marker(GeoMarker& marker); + + void update_tag(const std::string tag); + + private: + NavigationView& nav_; + + void setup(); + + const std::function on_done{}; + + const Dim banner_height = 3 * 16; + GeoMapMode mode_{}; + int32_t altitude_{}; + GeoPos::alt_unit altitude_unit_{}; + float lat_{}; + float lon_{}; + uint16_t angle_{}; + std::function on_close_{nullptr}; + + bool map_opened{}; + + GeoPos geopos{ + {0, 0}, + altitude_unit_}; + + GeoMap geomap{ + {0, banner_height, 240, 320 - 16 - banner_height}}; + + Button button_ok{ + {20 * 8, 8, 8 * 8, 2 * 16}, + "OK"}; }; } /* namespace ui */ diff --git a/firmware/application/ui/ui_menu.cpp b/firmware/application/ui/ui_menu.cpp index 3a307c174..945a1ee4b 100644 --- a/firmware/application/ui/ui_menu.cpp +++ b/firmware/application/ui/ui_menu.cpp @@ -28,264 +28,256 @@ namespace ui { /* MenuItemView **********************************************************/ void MenuItemView::set_item(MenuItem* item_) { - item = item_; + item = item_; } void MenuItemView::highlight() { - set_highlighted(true); - set_dirty(); + set_highlighted(true); + set_dirty(); } void MenuItemView::unhighlight() { - set_highlighted(false); - set_dirty(); + set_highlighted(false); + set_dirty(); } void MenuItemView::paint(Painter& painter) { - Coord offset_x { }; - - if (!item) return; - - const auto r = screen_rect(); - - const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style(); - - const auto font_height = paint_style.font.line_height(); - - ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color; - ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background; - - if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground; - - painter.fill_rectangle( - r, - final_bg_color - ); - - if (item->bitmap) { - painter.draw_bitmap( - { r.location().x() + 4, r.location().y() + 4 }, - *item->bitmap, - final_item_color, - final_bg_color - ); - offset_x = 26; - } else - offset_x = 0; - - Style text_style { - .font = paint_style.font, - .background = final_bg_color, - .foreground = final_item_color - }; - - painter.draw_string( - { r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2 }, - text_style, - item->text - ); + Coord offset_x{}; + + if (!item) return; + + const auto r = screen_rect(); + + const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style(); + + const auto font_height = paint_style.font.line_height(); + + ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color; + ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background; + + if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground; + + painter.fill_rectangle( + r, + final_bg_color); + + if (item->bitmap) { + painter.draw_bitmap( + {r.location().x() + 4, r.location().y() + 4}, + *item->bitmap, + final_item_color, + final_bg_color); + offset_x = 26; + } else + offset_x = 0; + + Style text_style{ + .font = paint_style.font, + .background = final_bg_color, + .foreground = final_item_color}; + + painter.draw_string( + {r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2}, + text_style, + item->text); } /* MenuView **************************************************************/ MenuView::MenuView( - Rect new_parent_rect, - bool keep_highlight -) : keep_highlight { keep_highlight } -{ - set_parent_rect(new_parent_rect); - - set_focusable(true); - - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; - - add_child(&arrow_more); - arrow_more.set_focusable(false); - arrow_more.set_foreground(Color::black()); + Rect new_parent_rect, + bool keep_highlight) + : keep_highlight{keep_highlight} { + set_parent_rect(new_parent_rect); + + set_focusable(true); + + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; + + add_child(&arrow_more); + arrow_more.set_focusable(false); + arrow_more.set_foreground(Color::black()); } MenuView::~MenuView() { - rtc_time::signal_tick_second -= signal_token_tick_second; - - for (auto item : menu_item_views) { - delete item; - } + rtc_time::signal_tick_second -= signal_token_tick_second; + + for (auto item : menu_item_views) { + delete item; + } } void MenuView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - - displayed_max = (parent_rect().size().height() / item_height); - arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * item_height), 8, 8 } ); - - // TODO: Clean this up :( - if (menu_item_views.size()) { - - for (auto item : menu_item_views) { - remove_child(item); - delete item; - } - - menu_item_views.clear(); - } - - for (size_t c = 0; c < displayed_max; c++) { - auto item = new MenuItemView { keep_highlight }; - menu_item_views.push_back(item); - add_child(item); - - Coord y_pos = c * item_height; - item->set_parent_rect({ - { 0, y_pos }, - { size().width(), (Coord)item_height } - }); - } - - update_items(); + View::set_parent_rect(new_parent_rect); + + displayed_max = (parent_rect().size().height() / item_height); + arrow_more.set_parent_rect({228, (Coord)(displayed_max * item_height), 8, 8}); + + // TODO: Clean this up :( + if (menu_item_views.size()) { + for (auto item : menu_item_views) { + remove_child(item); + delete item; + } + + menu_item_views.clear(); + } + + for (size_t c = 0; c < displayed_max; c++) { + auto item = new MenuItemView{keep_highlight}; + menu_item_views.push_back(item); + add_child(item); + + Coord y_pos = c * item_height; + item->set_parent_rect({{0, y_pos}, + {size().width(), (Coord)item_height}}); + } + + update_items(); } void MenuView::on_tick_second() { - if (more && blink) - arrow_more.set_foreground(Color::white()); - else - arrow_more.set_foreground(Color::black()); - - blink = !blink; - - arrow_more.set_dirty(); + if (more && blink) + arrow_more.set_foreground(Color::white()); + else + arrow_more.set_foreground(Color::black()); + + blink = !blink; + + arrow_more.set_dirty(); } void MenuView::clear() { - for (auto item : menu_item_views) { - item->set_item(nullptr); - } - - menu_items.clear(); - highlighted_item = 0; - offset = 0; + for (auto item : menu_item_views) { + item->set_item(nullptr); + } + + menu_items.clear(); + highlighted_item = 0; + offset = 0; } size_t MenuView::item_count() const { - return menu_items.size(); + return menu_items.size(); } void MenuView::add_item(MenuItem new_item) { - menu_items.push_back(new_item); - - update_items(); + menu_items.push_back(new_item); + + update_items(); } void MenuView::add_items(std::initializer_list new_items) { - for (auto item : new_items) { - add_item(item); - } + for (auto item : new_items) { + add_item(item); + } } void MenuView::update_items() { - size_t i = 0; - - if (menu_items.size() > displayed_max + offset) { - more = true; - blink = true; - } else - more = false; - - for (auto item : menu_item_views) { - if (i >= menu_items.size()) break; - - // Assign item data to MenuItemViews according to offset - item->set_item(&menu_items[i + offset]); - item->set_dirty(); - - if (highlighted_item == (i + offset)) { - item->highlight(); - } else - item->unhighlight(); - - i++; - } + size_t i = 0; + + if (menu_items.size() > displayed_max + offset) { + more = true; + blink = true; + } else + more = false; + + for (auto item : menu_item_views) { + if (i >= menu_items.size()) break; + + // Assign item data to MenuItemViews according to offset + item->set_item(&menu_items[i + offset]); + item->set_dirty(); + + if (highlighted_item == (i + offset)) { + item->highlight(); + } else + item->unhighlight(); + + i++; + } } MenuItemView* MenuView::item_view(size_t index) const { - return menu_item_views[index]; + return menu_item_views[index]; } bool MenuView::set_highlighted(int32_t new_value) { - int32_t item_count = (int32_t)menu_items.size(); - - if (new_value < 0 || item_count == 0) - return false; - - if (new_value >= item_count) - new_value = item_count - 1; - - if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) { - // Shift MenuView up - highlighted_item = new_value; - offset = new_value - displayed_max + 1; - update_items(); - } else if ((uint32_t)new_value < offset) { - // Shift MenuView down - highlighted_item = new_value; - offset = new_value; - update_items(); - } else { - // Just update highlight - item_view(highlighted_item - offset)->unhighlight(); - highlighted_item = new_value; - item_view(highlighted_item - offset)->highlight(); - } - - if (on_highlight) - on_highlight(); - - return true; + int32_t item_count = (int32_t)menu_items.size(); + + if (new_value < 0 || item_count == 0) + return false; + + if (new_value >= item_count) + new_value = item_count - 1; + + if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) { + // Shift MenuView up + highlighted_item = new_value; + offset = new_value - displayed_max + 1; + update_items(); + } else if ((uint32_t)new_value < offset) { + // Shift MenuView down + highlighted_item = new_value; + offset = new_value; + update_items(); + } else { + // Just update highlight + item_view(highlighted_item - offset)->unhighlight(); + highlighted_item = new_value; + item_view(highlighted_item - offset)->highlight(); + } + + if (on_highlight) + on_highlight(); + + return true; } uint32_t MenuView::highlighted_index() const { - return highlighted_item; + return highlighted_item; } void MenuView::on_focus() { - item_view(highlighted_item - offset)->highlight(); + item_view(highlighted_item - offset)->highlight(); } void MenuView::on_blur() { - if (!keep_highlight) - item_view(highlighted_item - offset)->unhighlight(); + if (!keep_highlight) + item_view(highlighted_item - offset)->unhighlight(); } bool MenuView::on_key(const KeyEvent key) { - switch(key) { - case KeyEvent::Up: - return set_highlighted(highlighted_item - 1); - - case KeyEvent::Down: - return set_highlighted(highlighted_item + 1); - - case KeyEvent::Select: - case KeyEvent::Right: - if( menu_items[highlighted_item].on_select ) { - menu_items[highlighted_item].on_select(key); - } - return true; - - case KeyEvent::Left: - if( on_left ) { - on_left(); - } - return true; - - default: - return false; - } + switch (key) { + case KeyEvent::Up: + return set_highlighted(highlighted_item - 1); + + case KeyEvent::Down: + return set_highlighted(highlighted_item + 1); + + case KeyEvent::Select: + case KeyEvent::Right: + if (menu_items[highlighted_item].on_select) { + menu_items[highlighted_item].on_select(key); + } + return true; + + case KeyEvent::Left: + if (on_left) { + on_left(); + } + return true; + + default: + return false; + } } bool MenuView::on_encoder(const EncoderEvent event) { - set_highlighted(highlighted_item + event); - return true; + set_highlighted(highlighted_item + event); + return true; } /* TODO: This could be handled by default behavior, if the UI stack were to @@ -294,15 +286,15 @@ bool MenuView::on_encoder(const EncoderEvent event) { */ /* bool MenuView::on_touch(const TouchEvent event) { - size_t i = 0; - for(const auto child : children_) { - if( child->screen_rect().contains(event.point) ) { - return set_highlighted(i); - } - i++; - } - - return false; + size_t i = 0; + for(const auto child : children_) { + if( child->screen_rect().contains(event.point) ) { + return set_highlighted(i); + } + i++; + } + + return false; } */ } /* namespace ui */ diff --git a/firmware/application/ui/ui_menu.hpp b/firmware/application/ui/ui_menu.hpp index bc6d2bc49..98591ab76 100644 --- a/firmware/application/ui/ui_menu.hpp +++ b/firmware/application/ui/ui_menu.hpp @@ -36,91 +36,89 @@ namespace ui { struct MenuItem { - std::string text; - ui::Color color; - const Bitmap* bitmap; - std::function on_select; - - // TODO: Prevent default-constructed MenuItems. - // I managed to construct a menu with three extra, unspecified menu items - // in the array that were default constructed... + std::string text; + ui::Color color; + const Bitmap* bitmap; + std::function on_select; + + // TODO: Prevent default-constructed MenuItems. + // I managed to construct a menu with three extra, unspecified menu items + // in the array that were default constructed... }; class MenuItemView : public Widget { -public: - MenuItemView( - bool keep_highlight - ) : keep_highlight { keep_highlight } - { - } - - MenuItemView(const MenuItemView&) = delete; - MenuItemView(MenuItemView&&) = delete; - MenuItemView& operator=(const MenuItemView&) = delete; - MenuItemView& operator=(MenuItemView&&) = delete; - - void paint(Painter& painter) override; - - void set_item(MenuItem* item_); - - void highlight(); - void unhighlight(); - -private: - MenuItem* item { nullptr }; - bool keep_highlight = false; + public: + MenuItemView( + bool keep_highlight) + : keep_highlight{keep_highlight} { + } + + MenuItemView(const MenuItemView&) = delete; + MenuItemView(MenuItemView&&) = delete; + MenuItemView& operator=(const MenuItemView&) = delete; + MenuItemView& operator=(MenuItemView&&) = delete; + + void paint(Painter& painter) override; + + void set_item(MenuItem* item_); + + void highlight(); + void unhighlight(); + + private: + MenuItem* item{nullptr}; + bool keep_highlight = false; }; class MenuView : public View { -public: - std::function on_left { }; - std::function on_highlight { nullptr }; - - MenuView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false); - - ~MenuView(); - - void add_item(MenuItem new_item); - void add_items(std::initializer_list new_items); - void clear(); - size_t item_count() const; - - MenuItemView* item_view(size_t index) const; - - bool set_highlighted(int32_t new_value); - uint32_t highlighted_index() const; - - void set_parent_rect(const Rect new_parent_rect) override; - void on_focus() override; - void on_blur() override; - bool on_key(const KeyEvent event) override; - bool on_encoder(const EncoderEvent event) override; - -private: - void update_items(); - void on_tick_second(); - - bool keep_highlight { false }; - - SignalToken signal_token_tick_second { }; - std::vector menu_items { }; - std::vector menu_item_views { }; - - Image arrow_more { - { 228, 320 - 8, 8, 8 }, - &bitmap_more, - Color::white(), - Color::black() - }; - - const size_t item_height = 24; - bool blink = false; - bool more = false; - size_t displayed_max { 0 }; - size_t highlighted_item { 0 }; - size_t offset { 0 }; + public: + std::function on_left{}; + std::function on_highlight{nullptr}; + + MenuView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false); + + ~MenuView(); + + void add_item(MenuItem new_item); + void add_items(std::initializer_list new_items); + void clear(); + size_t item_count() const; + + MenuItemView* item_view(size_t index) const; + + bool set_highlighted(int32_t new_value); + uint32_t highlighted_index() const; + + void set_parent_rect(const Rect new_parent_rect) override; + void on_focus() override; + void on_blur() override; + bool on_key(const KeyEvent event) override; + bool on_encoder(const EncoderEvent event) override; + + private: + void update_items(); + void on_tick_second(); + + bool keep_highlight{false}; + + SignalToken signal_token_tick_second{}; + std::vector menu_items{}; + std::vector menu_item_views{}; + + Image arrow_more{ + {228, 320 - 8, 8, 8}, + &bitmap_more, + Color::white(), + Color::black()}; + + const size_t item_height = 24; + bool blink = false; + bool more = false; + size_t displayed_max{0}; + size_t highlighted_item{0}; + size_t offset{0}; }; } /* namespace ui */ -#endif/*__UI_MENU_H__*/ +#endif /*__UI_MENU_H__*/ diff --git a/firmware/application/ui/ui_qrcode.cpp b/firmware/application/ui/ui_qrcode.cpp index 736ef441e..9d55d0b9d 100644 --- a/firmware/application/ui/ui_qrcode.cpp +++ b/firmware/application/ui/ui_qrcode.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -33,116 +33,97 @@ using namespace portapack; #include "string_format.hpp" #include "complex.hpp" - namespace ui { QRCodeImage::QRCodeImage( - Rect parent_rect -) : Widget { parent_rect } -{ - + Rect parent_rect) + : Widget{parent_rect} { } -QRCodeImage::~QRCodeImage( ) -{ - +QRCodeImage::~QRCodeImage() { } -QRCodeImage::QRCodeImage(const QRCodeImage&Image) : Widget { } -{ +QRCodeImage::QRCodeImage(const QRCodeImage& Image) + : Widget{} { (void)Image; } -QRCodeImage & QRCodeImage::operator=(const QRCodeImage&Image) -{ +QRCodeImage& QRCodeImage::operator=(const QRCodeImage& Image) { (void)Image; return *this; } void QRCodeImage::paint(Painter& painter) { + (void)painter; - (void)painter ; - - // The structure to manage the QR code - QRCode qrcode; - - - //Either small or large QR code can be shown.. + // The structure to manage the QR code + QRCode qrcode; - if(portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code - int qr_version = 2; + // Either small or large QR code can be shown.. - // Allocate a chunk of memory to store the QR code - uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; + if (portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code + int qr_version = 2; - qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); + // Allocate a chunk of memory to store the QR code + uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; + qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); - display.fill_rectangle(Rect(10, 30, 220, 220), Color::white()); + display.fill_rectangle(Rect(10, 30, 220, 220), Color::white()); - for (uint8_t y = 0; y < qrcode.size; y++) { - for (uint8_t x = 0; x < qrcode.size; x++) { - if (qrcode_getModule(&qrcode, x, y)) { - display.fill_rectangle(Rect(20+(x*8), 40+(y*8), 8, 8), Color::black()); + for (uint8_t y = 0; y < qrcode.size; y++) { + for (uint8_t x = 0; x < qrcode.size; x++) { + if (qrcode_getModule(&qrcode, x, y)) { + display.fill_rectangle(Rect(20 + (x * 8), 40 + (y * 8), 8, 8), Color::black()); + } + } + } - } - } - } + } - } + else { // show small QR code + int qr_version = 10; - else { // show small QR code - int qr_version = 10; + // Allocate a chunk of memory to store the QR code + uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; - // Allocate a chunk of memory to store the QR code - uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; + qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); - qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); + display.fill_rectangle(Rect(92, 97, 63, 63), Color::white()); - - display.fill_rectangle(Rect(92, 97, 63, 63), Color::white()); - - for (uint8_t y = 0; y < qrcode.size; y++) { - for (uint8_t x = 0; x < qrcode.size; x++) { - if (qrcode_getModule(&qrcode, x, y)) { - display.draw_pixel(Point(95+x,100+y), Color::black()); - - } - } - } - - } + for (uint8_t y = 0; y < qrcode.size; y++) { + for (uint8_t x = 0; x < qrcode.size; x++) { + if (qrcode_getModule(&qrcode, x, y)) { + display.draw_pixel(Point(95 + x, 100 + y), Color::black()); + } + } + } + } } void QRCodeView::focus() { - button_close.focus(); + button_close.focus(); } QRCodeView::~QRCodeView() { - if (on_close_) - on_close_(); + if (on_close_) + on_close_(); } QRCodeView::QRCodeView( - NavigationView& nav, - const char * qr_text, - const std::function on_close -) : nav_ (nav), - on_close_(on_close) -{ - - - add_children({ - &qr_code, - &button_close}); - //text_qr.set(qr_text); - qr_code.set_text(qr_text); - - button_close.on_select = [&nav](Button&){ - nav.pop(); - }; + NavigationView& nav, + const char* qr_text, + const std::function on_close) + : nav_(nav), + on_close_(on_close) { + add_children({&qr_code, + &button_close}); + // text_qr.set(qr_text); + qr_code.set_text(qr_text); + + button_close.on_select = [&nav](Button&) { + nav.pop(); + }; } - - } /* namespace ui */ diff --git a/firmware/application/ui/ui_qrcode.hpp b/firmware/application/ui/ui_qrcode.hpp index d30b7715a..d8d3fbdca 100644 --- a/firmware/application/ui/ui_qrcode.hpp +++ b/firmware/application/ui/ui_qrcode.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -33,59 +33,53 @@ namespace ui { class QRCodeImage : public Widget { -public: - - QRCodeImage(Rect parent_rect); - void set_text(const char * qr_text) { - qr_text_ = qr_text; - } - void paint(Painter& painter) override; - // for -weffc++ to be killed - ~QRCodeImage(); // destructor - QRCodeImage(const QRCodeImage&Image); - QRCodeImage & operator=(const QRCodeImage &Image); // assignment - -private: - const char * qr_text_ = NULL ; + public: + QRCodeImage(Rect parent_rect); + void set_text(const char* qr_text) { + qr_text_ = qr_text; + } + void paint(Painter& painter) override; + // for -weffc++ to be killed + ~QRCodeImage(); // destructor + QRCodeImage(const QRCodeImage& Image); + QRCodeImage& operator=(const QRCodeImage& Image); // assignment + + private: + const char* qr_text_ = NULL; }; class QRCodeView : public View { -public: - QRCodeView( - NavigationView& nav, - const char * qr_text, - const std::function on_close = nullptr - ); - ~QRCodeView(); - - QRCodeView(const QRCodeView&) = delete; - QRCodeView(QRCodeView&&) = delete; - QRCodeView& operator=(const QRCodeView&) = delete; - QRCodeView& operator=(QRCodeView&&) = delete; - - std::string title() const override { return "QR code"; }; - void focus() override; - -private: - NavigationView& nav_; - - - std::function on_close_ { nullptr }; - - - QRCodeImage qr_code { - { 50, 100, 100, 100 } - }; - - //Text text_qr { - // { 0 * 8, 10 * 16, 32 * 8, 1 * 8 }, - // "-" - //}; - - Button button_close { - { 9 * 8, 31 * 8, 12 * 8, 3 * 16 }, - "Back" - }; + public: + QRCodeView( + NavigationView& nav, + const char* qr_text, + const std::function on_close = nullptr); + ~QRCodeView(); + + QRCodeView(const QRCodeView&) = delete; + QRCodeView(QRCodeView&&) = delete; + QRCodeView& operator=(const QRCodeView&) = delete; + QRCodeView& operator=(QRCodeView&&) = delete; + + std::string title() const override { return "QR code"; }; + void focus() override; + + private: + NavigationView& nav_; + + std::function on_close_{nullptr}; + + QRCodeImage qr_code{ + {50, 100, 100, 100}}; + + // Text text_qr { + // { 0 * 8, 10 * 16, 32 * 8, 1 * 8 }, + // "-" + // }; + + Button button_close{ + {9 * 8, 31 * 8, 12 * 8, 3 * 16}, + "Back"}; }; } /* namespace ui */ diff --git a/firmware/application/ui/ui_receiver.cpp b/firmware/application/ui/ui_receiver.cpp index a05b743f0..6ad4d9079 100644 --- a/firmware/application/ui/ui_receiver.cpp +++ b/firmware/application/ui/ui_receiver.cpp @@ -35,362 +35,350 @@ namespace ui { /* FrequencyField ********************************************************/ FrequencyField::FrequencyField( - const Point parent_pos -) : Widget { { parent_pos, { 8 * 10, 16 } } }, - length_ { 11 }, - range(rf::tuning_range) -{ - set_focusable(true); + const Point parent_pos) + : Widget{{parent_pos, {8 * 10, 16}}}, + length_{11}, + range(rf::tuning_range) { + set_focusable(true); } rf::Frequency FrequencyField::value() const { - return value_; + return value_; } void FrequencyField::set_value(rf::Frequency new_value) { - new_value = clamp_value(new_value); - - if( new_value != value_ ) { - value_ = new_value; - if( on_change ) { - on_change(value_); - } - set_dirty(); - } + new_value = clamp_value(new_value); + + if (new_value != value_) { + value_ = new_value; + if (on_change) { + on_change(value_); + } + set_dirty(); + } } void FrequencyField::set_step(rf::Frequency new_value) { - step = new_value; - // TODO: Quantize current frequency to a step of the new size? + step = new_value; + // TODO: Quantize current frequency to a step of the new size? } void FrequencyField::paint(Painter& painter) { - const std::string str_value = to_string_short_freq(value_); + const std::string str_value = to_string_short_freq(value_); - const auto paint_style = has_focus() ? style().invert() : style(); + const auto paint_style = has_focus() ? style().invert() : style(); - painter.draw_string( - screen_pos(), - paint_style, - str_value - ); + painter.draw_string( + screen_pos(), + paint_style, + str_value); } bool FrequencyField::on_key(const ui::KeyEvent event) { - if( event == ui::KeyEvent::Select ) { - if( on_edit ) { - on_edit(); - return true; - } - } - return false; + if (event == ui::KeyEvent::Select) { + if (on_edit) { + on_edit(); + return true; + } + } + return false; } bool FrequencyField::on_encoder(const EncoderEvent delta) { - if (step == 0) { // 'Auto' mode.' - auto ms = RTT2MS(halGetCounterValue()); - auto delta_ms = last_ms_ <= ms ? ms - last_ms_ : ms; - last_ms_ = ms; - - // The goal is to map 'scale' to a range of about 10 to 10M. - // The faster the encoder is rotated, the larger the step. - // Linear doesn't feel right. Hyperbolic felt better. - // To get these magic numbers, I graphed the function until the - // curve shape seemed about right then tested on device. - delta_ms = std::min(145ull, delta_ms) + 5; // Prevent DIV/0 - int64_t scale = 200'000'000 / (0.001'55 * pow(delta_ms, 5.45)) + 8; - set_value(value() + (delta * scale)); - } else { - set_value(value() + (delta * step)); - } - return true; + if (step == 0) { // 'Auto' mode.' + auto ms = RTT2MS(halGetCounterValue()); + auto delta_ms = last_ms_ <= ms ? ms - last_ms_ : ms; + last_ms_ = ms; + + // The goal is to map 'scale' to a range of about 10 to 10M. + // The faster the encoder is rotated, the larger the step. + // Linear doesn't feel right. Hyperbolic felt better. + // To get these magic numbers, I graphed the function until the + // curve shape seemed about right then tested on device. + delta_ms = std::min(145ull, delta_ms) + 5; // Prevent DIV/0 + int64_t scale = 200'000'000 / (0.001'55 * pow(delta_ms, 5.45)) + 8; + set_value(value() + (delta * scale)); + } else { + set_value(value() + (delta * step)); + } + return true; } bool FrequencyField::on_touch(const TouchEvent event) { - if( event.type == TouchEvent::Type::Start ) { - focus(); - } - return true; + if (event.type == TouchEvent::Type::Start) { + focus(); + } + return true; } void FrequencyField::on_focus() { - if( on_show_options ) { - on_show_options(); - } + if (on_show_options) { + on_show_options(); + } } rf::Frequency FrequencyField::clamp_value(rf::Frequency value) { - return range.clip(value); + return range.clip(value); } /* FrequencyKeypadView ***************************************************/ bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) { - focused_button += delta; - if (focused_button < 0) { - focused_button = buttons.size() - 1; - } - else if (focused_button >= (int16_t)buttons.size()) { - focused_button = 0; - } - buttons[focused_button].focus(); - return true; + focused_button += delta; + if (focused_button < 0) { + focused_button = buttons.size() - 1; + } else if (focused_button >= (int16_t)buttons.size()) { + focused_button = 0; + } + buttons[focused_button].focus(); + return true; } FrequencyKeypadView::FrequencyKeypadView( - NavigationView& nav, - const rf::Frequency value -) { - add_child(&text_value); - - const auto button_fn = [this](Button& button) { - this->on_button(button); - }; - - const char* const key_caps = "123456789<0."; - - int n = 0; - for(auto& button : buttons) { - add_child(&button); - const std::string label { - key_caps[n] - }; - button.id = n; - button.on_highlight = [this](Button& button) { - focused_button = button.id; - }; - button.on_select = button_fn; - button.set_parent_rect({ - (n % 3) * button_w, - (n / 3) * button_h + 24, - button_w, button_h - }); - button.set_text(label); - n++; - } - - add_children({ - &button_save, - &button_load, - &button_close - }); - - button_save.on_select = [this, &nav](Button&) { - nav.push(this->value()); - }; - button_load.on_select = [this, &nav](Button&) { - auto load_view = nav.push(); - load_view->on_frequency_loaded = [this](rf::Frequency value) { - set_value(value); - }; - }; - - button_close.on_select = [this, &nav](Button&) { - if( on_changed ) { - on_changed(this->value()); - } - nav.pop(); - }; - - set_value(value); + NavigationView& nav, + const rf::Frequency value) { + add_child(&text_value); + + const auto button_fn = [this](Button& button) { + this->on_button(button); + }; + + const char* const key_caps = "123456789<0."; + + int n = 0; + for (auto& button : buttons) { + add_child(&button); + const std::string label{ + key_caps[n]}; + button.id = n; + button.on_highlight = [this](Button& button) { + focused_button = button.id; + }; + button.on_select = button_fn; + button.set_parent_rect({(n % 3) * button_w, + (n / 3) * button_h + 24, + button_w, button_h}); + button.set_text(label); + n++; + } + + add_children({&button_save, + &button_load, + &button_close}); + + button_save.on_select = [this, &nav](Button&) { + nav.push(this->value()); + }; + button_load.on_select = [this, &nav](Button&) { + auto load_view = nav.push(); + load_view->on_frequency_loaded = [this](rf::Frequency value) { + set_value(value); + }; + }; + + button_close.on_select = [this, &nav](Button&) { + if (on_changed) { + on_changed(this->value()); + } + nav.pop(); + }; + + set_value(value); } void FrequencyKeypadView::focus() { - button_close.focus(); + button_close.focus(); } rf::Frequency FrequencyKeypadView::value() const { - return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base; + return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base; } void FrequencyKeypadView::set_value(const rf::Frequency new_value) { - mhz.set(new_value / 1000000); - mhz.remove_leading_zeros(); + mhz.set(new_value / 1000000); + mhz.remove_leading_zeros(); - submhz.set((new_value % 1000000) / submhz_base); - submhz.remove_trailing_zeros(); + submhz.set((new_value % 1000000) / submhz_base); + submhz.remove_trailing_zeros(); - update_text(); + update_text(); } void FrequencyKeypadView::on_button(Button& button) { - const auto s = button.text(); - if( s == "." ) { - field_toggle(); - } else if( s == "<" ) { - digit_delete(); - } else { - digit_add(s[0]); - } - update_text(); + const auto s = button.text(); + if (s == ".") { + field_toggle(); + } else if (s == "<") { + digit_delete(); + } else { + digit_add(s[0]); + } + update_text(); } void FrequencyKeypadView::digit_add(const char c) { - if( state == State::DigitMHz ) { - if( clear_field_if_digits_entered ) { - mhz.clear(); - } - mhz.add_digit(c); - } else { - submhz.add_digit(c); - } - clear_field_if_digits_entered = false; + if (state == State::DigitMHz) { + if (clear_field_if_digits_entered) { + mhz.clear(); + } + mhz.add_digit(c); + } else { + submhz.add_digit(c); + } + clear_field_if_digits_entered = false; } void FrequencyKeypadView::digit_delete() { - if( state == State::DigitMHz ) { - mhz.delete_digit(); - } else { - submhz.delete_digit(); - } + if (state == State::DigitMHz) { + mhz.delete_digit(); + } else { + submhz.delete_digit(); + } } void FrequencyKeypadView::field_toggle() { - if( state == State::DigitMHz ) { - state = State::DigitSubMHz; - submhz.clear(); - } else { - state = State::DigitMHz; - clear_field_if_digits_entered = true; - } + if (state == State::DigitMHz) { + state = State::DigitSubMHz; + submhz.clear(); + } else { + state = State::DigitMHz; + clear_field_if_digits_entered = true; + } } void FrequencyKeypadView::update_text() { - const auto s = mhz.as_string() + "." + submhz.as_string(); - text_value.set(s); + const auto s = mhz.as_string() + "." + submhz.as_string(); + text_value.set(s); } /* FrequencyOptionsView **************************************************/ FrequencyOptionsView::FrequencyOptionsView( - const Rect parent_rect, - const Style* const style -) : View { parent_rect } -{ - set_style(style); - - field_step.on_change = [this](size_t n, OptionsField::value_t v) { - (void)n; - this->on_step_changed(v); - }; - - field_ppm.on_change = [this](int32_t v) { - this->on_reference_ppm_correction_changed(v); - }; - - add_children({ - &text_step, - &field_step, - }); - - if( portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal ) { - add_child(&field_ppm); - add_child(&text_ppm); - } + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + field_step.on_change = [this](size_t n, OptionsField::value_t v) { + (void)n; + this->on_step_changed(v); + }; + + field_ppm.on_change = [this](int32_t v) { + this->on_reference_ppm_correction_changed(v); + }; + + add_children({ + &text_step, + &field_step, + }); + + if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal) { + add_child(&field_ppm); + add_child(&text_ppm); + } } void FrequencyOptionsView::set_step(rf::Frequency f) { - field_step.set_by_value(f); + field_step.set_by_value(f); } void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) { - field_ppm.set_value(v); + field_ppm.set_value(v); } void FrequencyOptionsView::on_step_changed(rf::Frequency v) { - if( on_change_step ) { - on_change_step(v); - } + if (on_change_step) { + on_change_step(v); + } } void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) { - if( on_change_reference_ppm_correction ) { - on_change_reference_ppm_correction(v); - } + if (on_change_reference_ppm_correction) { + on_change_reference_ppm_correction(v); + } } /* RFAmpField ************************************************************/ RFAmpField::RFAmpField( - Point parent_pos -) : NumberField { - parent_pos, - 1, - { 0, 1 }, - 1, - ' ', - } -{ - set_value(receiver_model.rf_amp()); - - on_change = [](int32_t v) { - receiver_model.set_rf_amp(v); - }; + Point parent_pos) + : NumberField{ + parent_pos, + 1, + {0, 1}, + 1, + ' ', + } { + set_value(receiver_model.rf_amp()); + + on_change = [](int32_t v) { + receiver_model.set_rf_amp(v); + }; } /* RadioGainOptionsView **************************************************/ RadioGainOptionsView::RadioGainOptionsView( - const Rect parent_rect, - const Style* const style -) : View { parent_rect } -{ - set_style(style); - - add_children({ - &label_rf_amp, - &field_rf_amp, - }); + const Rect parent_rect, + const Style* const style) + : View{parent_rect} { + set_style(style); + + add_children({ + &label_rf_amp, + &field_rf_amp, + }); } /* LNAGainField **********************************************************/ LNAGainField::LNAGainField( - Point parent_pos -) : NumberField { - parent_pos, 2, - { max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum }, - max283x::lna::gain_db_step, - ' ', - } -{ - set_value(receiver_model.lna()); - - on_change = [](int32_t v) { - receiver_model.set_lna(v); - }; + Point parent_pos) + : NumberField{ + parent_pos, + 2, + {max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum}, + max283x::lna::gain_db_step, + ' ', + } { + set_value(receiver_model.lna()); + + on_change = [](int32_t v) { + receiver_model.set_lna(v); + }; } void LNAGainField::on_focus() { - //Widget::on_focus(); - if( on_show_options ) { - on_show_options(); - } + // Widget::on_focus(); + if (on_show_options) { + on_show_options(); + } } /* VGAGainField **********************************************************/ VGAGainField::VGAGainField( - Point parent_pos -) : NumberField { - parent_pos, 2, - { max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum }, - max283x::vga::gain_db_step, - ' ', - } -{ - set_value(receiver_model.vga()); - - on_change = [](int32_t v) { - receiver_model.set_vga(v); - }; + Point parent_pos) + : NumberField{ + parent_pos, + 2, + {max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum}, + max283x::vga::gain_db_step, + ' ', + } { + set_value(receiver_model.vga()); + + on_change = [](int32_t v) { + receiver_model.set_vga(v); + }; } void VGAGainField::on_focus() { - //Widget::on_focus(); - if( on_show_options ) { - on_show_options(); - } + // Widget::on_focus(); + if (on_show_options) { + on_show_options(); + } } } /* namespace ui */ diff --git a/firmware/application/ui/ui_receiver.hpp b/firmware/application/ui/ui_receiver.hpp index 05e078d2f..a0d531f94 100644 --- a/firmware/application/ui/ui_receiver.hpp +++ b/firmware/application/ui/ui_receiver.hpp @@ -37,316 +37,306 @@ namespace ui { class FrequencyField : public Widget { -public: - std::function on_change { }; - std::function on_edit { }; - std::function on_show_options { }; + public: + std::function on_change{}; + std::function on_edit{}; + std::function on_show_options{}; - using range_t = rf::FrequencyRange; + using range_t = rf::FrequencyRange; - FrequencyField(const Point parent_pos); + FrequencyField(const Point parent_pos); - rf::Frequency value() const; + rf::Frequency value() const; - void set_value(rf::Frequency new_value); - void set_step(rf::Frequency new_value); + void set_value(rf::Frequency new_value); + void set_step(rf::Frequency new_value); - void paint(Painter& painter) override; + void paint(Painter& painter) override; - bool on_key(const ui::KeyEvent event) override; - bool on_encoder(const EncoderEvent delta) override; - bool on_touch(const TouchEvent event) override; - void on_focus() override; + bool on_key(const ui::KeyEvent event) override; + bool on_encoder(const EncoderEvent delta) override; + bool on_touch(const TouchEvent event) override; + void on_focus() override; -private: - const size_t length_; - const range_t range; - rf::Frequency value_ { 0 }; - rf::Frequency step { 25000 }; - uint64_t last_ms_ { 0 }; + private: + const size_t length_; + const range_t range; + rf::Frequency value_{0}; + rf::Frequency step{25000}; + uint64_t last_ms_{0}; - rf::Frequency clamp_value(rf::Frequency value); + rf::Frequency clamp_value(rf::Frequency value); }; -template +template class FieldString { -public: - enum Justify { - Right, - Left, - }; - - constexpr FieldString( - Justify justify - ) : justify { justify } - { - } - - uint32_t as_int() const { - uint32_t value = 0; - for(const auto c : s) { - const uint_fast8_t digit = (c == ' ') ? 0 : c - '0'; - value = (value * 10) + digit; - } - return value; - } - - void set(uint32_t value) { - std::generate(s.rbegin(), s.rend(), [&value]() { - const char digit = (value % 10) + '0'; - value /= 10; - return digit; - }); - } - - void clear() { - s.fill(' '); - } - - void add_digit(const char c) { - insert_right(c); - } - - void delete_digit() { - if( justify == Justify::Right ) { - shift_right(); - s.front() = ' '; - } else { - auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) { - return a != ' '; - }); - if( first_digit != s.rend() ) { - *first_digit = ' '; - } - } - } - - std::string as_string() const { - return { s.data(), s.size() }; - } - - void remove_leading_zeros() { - remove_zeros(s.begin(), s.end()); - } - - void remove_trailing_zeros() { - remove_zeros(s.rbegin(), s.rend()); - } - -private: - using array_type = std::array; - - array_type s { }; - Justify justify { Justify::Left }; - - template - void remove_zeros(Iterator begin, Iterator end) { - const auto first_significant_digit = - std::find_if(begin, end, [](const char& a) { - return a != '0'; - }); - std::fill(begin, first_significant_digit, ' '); - } - - void insert_right(const char c) { - auto insert_point = s.end() - 1; - - if( justify == Justify::Left ) { - insert_point = std::find_if(s.begin(), s.end(), [](const char& a) { - return a == ' '; - }); - } - - if( *insert_point != ' ' ) { - insert_point = shift_left(); - } - - *insert_point = c; - } - - typename array_type::iterator shift_left() { - return std::move(s.begin() + 1, s.end(), s.begin()); - } - - typename array_type::iterator shift_right() { - return std::move_backward(s.begin(), s.end() - 1, s.end()); - } + public: + enum Justify { + Right, + Left, + }; + + constexpr FieldString( + Justify justify) + : justify{justify} { + } + + uint32_t as_int() const { + uint32_t value = 0; + for (const auto c : s) { + const uint_fast8_t digit = (c == ' ') ? 0 : c - '0'; + value = (value * 10) + digit; + } + return value; + } + + void set(uint32_t value) { + std::generate(s.rbegin(), s.rend(), [&value]() { + const char digit = (value % 10) + '0'; + value /= 10; + return digit; + }); + } + + void clear() { + s.fill(' '); + } + + void add_digit(const char c) { + insert_right(c); + } + + void delete_digit() { + if (justify == Justify::Right) { + shift_right(); + s.front() = ' '; + } else { + auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) { + return a != ' '; + }); + if (first_digit != s.rend()) { + *first_digit = ' '; + } + } + } + + std::string as_string() const { + return {s.data(), s.size()}; + } + + void remove_leading_zeros() { + remove_zeros(s.begin(), s.end()); + } + + void remove_trailing_zeros() { + remove_zeros(s.rbegin(), s.rend()); + } + + private: + using array_type = std::array; + + array_type s{}; + Justify justify{Justify::Left}; + + template + void remove_zeros(Iterator begin, Iterator end) { + const auto first_significant_digit = + std::find_if(begin, end, [](const char& a) { + return a != '0'; + }); + std::fill(begin, first_significant_digit, ' '); + } + + void insert_right(const char c) { + auto insert_point = s.end() - 1; + + if (justify == Justify::Left) { + insert_point = std::find_if(s.begin(), s.end(), [](const char& a) { + return a == ' '; + }); + } + + if (*insert_point != ' ') { + insert_point = shift_left(); + } + + *insert_point = c; + } + + typename array_type::iterator shift_left() { + return std::move(s.begin() + 1, s.end(), s.begin()); + } + + typename array_type::iterator shift_right() { + return std::move_backward(s.begin(), s.end() - 1, s.end()); + } }; class FrequencyKeypadView : public View { -public: - std::function on_changed { }; - - FrequencyKeypadView( - NavigationView& nav, - const rf::Frequency value - ); - - void focus() override; - - rf::Frequency value() const; - void set_value(const rf::Frequency new_value); - bool on_encoder(const EncoderEvent delta) override; - -private: - int16_t focused_button = 0; - static constexpr int button_w = 240 / 3; - static constexpr int button_h = 48; - - static constexpr int mhz_digits = 4; - static constexpr int submhz_digits = 4; - - static constexpr int mhz_mod = pow(10, mhz_digits); - static constexpr int submhz_base = pow(10, 6 - submhz_digits); - static constexpr int text_digits = mhz_digits + 1 + submhz_digits; - - Text text_value { - { 0, 4, 240, 16 } - }; - - std::array buttons { }; - - Button button_save { - { 0, button_h * 5, 60, button_h }, - "Save" - }; - Button button_load { - { 60, button_h * 5, 60, button_h }, - "Load" - }; - Button button_close { - { 128, button_h * 5, 112, button_h }, - "Done" - }; - - /* TODO: Template arg required in enum?! */ - FieldString mhz { FieldString<4>::Justify::Right }; - FieldString submhz { FieldString<4>::Justify::Left }; - - enum State { - DigitMHz, - DigitSubMHz - }; - - State state { DigitMHz }; - bool clear_field_if_digits_entered { true }; - - void on_button(Button& button); - - void digit_add(const char c); - void digit_delete(); - - void field_toggle(); - void update_text(); + public: + std::function on_changed{}; + + FrequencyKeypadView( + NavigationView& nav, + const rf::Frequency value); + + void focus() override; + + rf::Frequency value() const; + void set_value(const rf::Frequency new_value); + bool on_encoder(const EncoderEvent delta) override; + + private: + int16_t focused_button = 0; + static constexpr int button_w = 240 / 3; + static constexpr int button_h = 48; + + static constexpr int mhz_digits = 4; + static constexpr int submhz_digits = 4; + + static constexpr int mhz_mod = pow(10, mhz_digits); + static constexpr int submhz_base = pow(10, 6 - submhz_digits); + static constexpr int text_digits = mhz_digits + 1 + submhz_digits; + + Text text_value{ + {0, 4, 240, 16}}; + + std::array buttons{}; + + Button button_save{ + {0, button_h * 5, 60, button_h}, + "Save"}; + Button button_load{ + {60, button_h * 5, 60, button_h}, + "Load"}; + Button button_close{ + {128, button_h * 5, 112, button_h}, + "Done"}; + + /* TODO: Template arg required in enum?! */ + FieldString mhz{FieldString<4>::Justify::Right}; + FieldString submhz{FieldString<4>::Justify::Left}; + + enum State { + DigitMHz, + DigitSubMHz + }; + + State state{DigitMHz}; + bool clear_field_if_digits_entered{true}; + + void on_button(Button& button); + + void digit_add(const char c); + void digit_delete(); + + void field_toggle(); + void update_text(); }; class FrequencyStepView : public OptionsField { -public: - FrequencyStepView( - Point parent_pos - ) : OptionsField { - parent_pos, - 5, - { - { " Auto", 0 }, /* Faster == larger step. */ - { " 10", 10 }, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */ - { " 50", 50 }, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */ - { " 100", 100 }, - { " 1k ", 1000 }, - { " 3k ", 3000 }, /* Approximate SSB bandwidth */ - { " 5k ", 5000 }, - { " 6k3", 6250 }, - { " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */ - { " 10k ", 10000 }, - { " 12k5", 12500 }, - { " 25k ", 25000 }, - { "100k ", 100000 }, - { " 1M ", 1000000 }, - { " 10M ", 10000000 }, - } - } - { - } + public: + FrequencyStepView( + Point parent_pos) + : OptionsField{ + parent_pos, + 5, + { + {" Auto", 0}, /* Faster == larger step. */ + {" 10", 10}, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */ + {" 50", 50}, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */ + {" 100", 100}, + {" 1k ", 1000}, + {" 3k ", 3000}, /* Approximate SSB bandwidth */ + {" 5k ", 5000}, + {" 6k3", 6250}, + {" 9k ", 9000}, /* channel spacing for LF, MF in some regions */ + {" 10k ", 10000}, + {" 12k5", 12500}, + {" 25k ", 25000}, + {"100k ", 100000}, + {" 1M ", 1000000}, + {" 10M ", 10000000}, + }} { + } }; class FrequencyOptionsView : public View { -public: - std::function on_change_step { }; - std::function on_change_reference_ppm_correction { }; - - FrequencyOptionsView(const Rect parent_rect, const Style* const style); - - void set_step(rf::Frequency f); - void set_reference_ppm_correction(int32_t v); - -private: - Text text_step { - { 0 * 8, 0 * 16, 4 * 8, 1 * 16 }, - "Step" - }; - - FrequencyStepView field_step { - { 5 * 8, 0 * 16 }, - }; - - void on_step_changed(rf::Frequency v); - void on_reference_ppm_correction_changed(int32_t v); - - NumberField field_ppm { - { 23 * 8, 0 * 16 }, - 3, - { -99, 99 }, - 1, - '0', - }; - Text text_ext { - { 23 * 8, 0 * 16, 3 * 8, 1 * 16 }, - "EXT", - }; - Text text_ppm { - { 27 * 8, 0 * 16, 3 * 8, 16 }, - "PPM", - }; + public: + std::function on_change_step{}; + std::function on_change_reference_ppm_correction{}; + + FrequencyOptionsView(const Rect parent_rect, const Style* const style); + + void set_step(rf::Frequency f); + void set_reference_ppm_correction(int32_t v); + + private: + Text text_step{ + {0 * 8, 0 * 16, 4 * 8, 1 * 16}, + "Step"}; + + FrequencyStepView field_step{ + {5 * 8, 0 * 16}, + }; + + void on_step_changed(rf::Frequency v); + void on_reference_ppm_correction_changed(int32_t v); + + NumberField field_ppm{ + {23 * 8, 0 * 16}, + 3, + {-99, 99}, + 1, + '0', + }; + Text text_ext{ + {23 * 8, 0 * 16, 3 * 8, 1 * 16}, + "EXT", + }; + Text text_ppm{ + {27 * 8, 0 * 16, 3 * 8, 16}, + "PPM", + }; }; class RFAmpField : public NumberField { -public: - RFAmpField(Point parent_pos); + public: + RFAmpField(Point parent_pos); }; class RadioGainOptionsView : public View { -public: - RadioGainOptionsView(const Rect parent_rect, const Style* const style); - -private: - Text label_rf_amp { - { 0 * 8, 0 * 16, 3 * 8, 1 * 16 }, - "Amp" - }; - - RFAmpField field_rf_amp { - { 4 * 8, 0 * 16}, - }; + public: + RadioGainOptionsView(const Rect parent_rect, const Style* const style); + + private: + Text label_rf_amp{ + {0 * 8, 0 * 16, 3 * 8, 1 * 16}, + "Amp"}; + + RFAmpField field_rf_amp{ + {4 * 8, 0 * 16}, + }; }; class LNAGainField : public NumberField { -public: - std::function on_show_options { }; + public: + std::function on_show_options{}; - LNAGainField(Point parent_pos); + LNAGainField(Point parent_pos); - void on_focus() override; + void on_focus() override; }; class VGAGainField : public NumberField { -public: - std::function on_show_options { }; + public: + std::function on_show_options{}; - VGAGainField(Point parent_pos); + VGAGainField(Point parent_pos); - void on_focus() override; + void on_focus() override; }; } /* namespace ui */ -#endif/*__UI_RECEIVER_H__*/ +#endif /*__UI_RECEIVER_H__*/ diff --git a/firmware/application/ui/ui_rssi.cpp b/firmware/application/ui/ui_rssi.cpp index 05fe8d2f0..7e59f0bfc 100644 --- a/firmware/application/ui/ui_rssi.cpp +++ b/firmware/application/ui/ui_rssi.cpp @@ -26,308 +26,276 @@ #include -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) -#define abs(x) ((x)>0?(x):-(x)) +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define abs(x) ((x) > 0 ? (x) : -(x)) namespace ui { - RSSI::RSSI( - Rect parent_rect, - bool instant_exec - ) : Widget { parent_rect }, - instant_exec_ { instant_exec } - { - } - - void RSSI::paint(Painter& painter) { - const auto r = screen_rect(); - - constexpr int rssi_sample_range = 256; - //constexpr float rssi_voltage_min = 0.4; - constexpr float rssi_voltage_max = 2.2; - constexpr float adc_voltage_max = 3.3; - //constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; - constexpr int raw_min = 0 ; - constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; - constexpr int raw_delta = raw_max - raw_min; - - - if( !vertical_rssi_enabled ) - { - // horizontal left to right level meters - const range_t x_avg_range { 0, r.width() - 1 }; - const int16_t x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta); - const range_t x_min_range { 0, x_avg }; - const int16_t x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta); - const range_t x_max_range { x_avg + 1, r.width() - 1 }; - const int16_t x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta); - const int16_t peak = x_max_range.clip((peak_ - raw_min) * r.width() / raw_delta); - - // x_min - const Rect r0 { r.left(), r.top(), x_min, r.height() }; - painter.fill_rectangle( - r0, - Color::blue() - ); - - // x_avg - const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() }; - painter.fill_rectangle( - r1, - Color::red() - ); - - // x_avg middle marker - const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() }; +RSSI::RSSI( + Rect parent_rect, + bool instant_exec) + : Widget{parent_rect}, + instant_exec_{instant_exec} { +} + +void RSSI::paint(Painter& painter) { + const auto r = screen_rect(); + + constexpr int rssi_sample_range = 256; + // constexpr float rssi_voltage_min = 0.4; + constexpr float rssi_voltage_max = 2.2; + constexpr float adc_voltage_max = 3.3; + // constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; + constexpr int raw_min = 0; + constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; + constexpr int raw_delta = raw_max - raw_min; + + if (!vertical_rssi_enabled) { + // horizontal left to right level meters + const range_t x_avg_range{0, r.width() - 1}; + const int16_t x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta); + const range_t x_min_range{0, x_avg}; + const int16_t x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta); + const range_t x_max_range{x_avg + 1, r.width() - 1}; + const int16_t x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta); + const int16_t peak = x_max_range.clip((peak_ - raw_min) * r.width() / raw_delta); + + // x_min + const Rect r0{r.left(), r.top(), x_min, r.height()}; + painter.fill_rectangle( + r0, + Color::blue()); + + // x_avg + const Rect r1{r.left() + x_min, r.top(), x_avg - x_min, r.height()}; + painter.fill_rectangle( + r1, + Color::red()); + + // x_avg middle marker + const Rect r2{r.left() + x_avg, r.top(), 1, r.height()}; + painter.fill_rectangle( + r2, + Color::white()); + + // x_max + const Rect r3{r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height()}; + painter.fill_rectangle( + r3, + Color::red()); + + // filling last part in black + const Rect r4{r.left() + x_max, r.top(), r.width() - x_max, r.height()}; + painter.fill_rectangle( + r4, + Color::black()); + + // show green peak value + if (peak_enabled) { + const Rect r5{r.left() + peak - 3, r.top(), 3, r.height()}; painter.fill_rectangle( - r2, - Color::white() - ); - - // x_max - const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() }; - painter.fill_rectangle( - r3, - Color::red() - ); - - // filling last part in black - const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() }; - painter.fill_rectangle( - r4, - Color::black() - ); - - // show green peak value - if( peak_enabled ) - { - const Rect r5 { r.left() + peak - 3 , r.top() , 3 , r.height() }; - painter.fill_rectangle( - r5, - Color::green() - ); - } + r5, + Color::green()); } - else - { - // vertical bottom to top level meters - const range_t y_avg_range { 0, r.height() - 1 }; - const int16_t y_avg = y_avg_range.clip((avg_ - raw_min) * r.height() / raw_delta); - const range_t y_min_range { 0, y_avg }; - const int16_t y_min = y_min_range.clip((min_ - raw_min) * r.height() / raw_delta); - const range_t y_max_range { y_avg + 1, r.height() - 1 }; - const int16_t y_max = y_max_range.clip((max_ - raw_min) * r.height() / raw_delta); - const range_t peak_range { 0, r.height() - 1 }; - const int16_t peak = peak_range.clip((peak_ - raw_min) * r.height() / raw_delta); - - // y_min - const Rect r0 { r.left(), r.bottom() - y_min, r.width() , y_min }; - painter.fill_rectangle( - r0, - Color::blue() - ); - - // y_avg - const Rect r1 { r.left(), r.bottom() - y_avg , r.width() , y_avg - y_min }; - painter.fill_rectangle( - r1, - Color::red() - ); - - // y_avg middle marker - const Rect r2 { r.left(), r.bottom() - y_avg , r.width() , 1 }; - painter.fill_rectangle( - r2, - Color::white() - ); - - // y_max - const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg }; - //const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg - 1 }; - painter.fill_rectangle( - r3, - Color::red() - ); - - // fill last part of level in black - const Rect r4 { r.left(), r.top() , r.width() , r.height() - y_max }; + } else { + // vertical bottom to top level meters + const range_t y_avg_range{0, r.height() - 1}; + const int16_t y_avg = y_avg_range.clip((avg_ - raw_min) * r.height() / raw_delta); + const range_t y_min_range{0, y_avg}; + const int16_t y_min = y_min_range.clip((min_ - raw_min) * r.height() / raw_delta); + const range_t y_max_range{y_avg + 1, r.height() - 1}; + const int16_t y_max = y_max_range.clip((max_ - raw_min) * r.height() / raw_delta); + const range_t peak_range{0, r.height() - 1}; + const int16_t peak = peak_range.clip((peak_ - raw_min) * r.height() / raw_delta); + + // y_min + const Rect r0{r.left(), r.bottom() - y_min, r.width(), y_min}; + painter.fill_rectangle( + r0, + Color::blue()); + + // y_avg + const Rect r1{r.left(), r.bottom() - y_avg, r.width(), y_avg - y_min}; + painter.fill_rectangle( + r1, + Color::red()); + + // y_avg middle marker + const Rect r2{r.left(), r.bottom() - y_avg, r.width(), 1}; + painter.fill_rectangle( + r2, + Color::white()); + + // y_max + const Rect r3{r.left(), r.bottom() - y_max, r.width(), y_max - y_avg}; + // const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg - 1 }; + painter.fill_rectangle( + r3, + Color::red()); + + // fill last part of level in black + const Rect r4{r.left(), r.top(), r.width(), r.height() - y_max}; + painter.fill_rectangle( + r4, + Color::black()); + + // show green peak value if enabled + if (peak_enabled) { + const Rect r5{r.left(), r.bottom() - peak - 3, r.width(), 3}; painter.fill_rectangle( - r4, - Color::black() - ); - - // show green peak value if enabled - if( peak_enabled ) - { - const Rect r5 { r.left(), r.bottom() - peak - 3 , r.width() , 3 }; - painter.fill_rectangle( - r5, - Color::green() - ); - } + r5, + Color::green()); } - if (pitch_rssi_enabled) { - baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true); - } - if( has_focus() || highlighted() ) - { - const Rect r6 { r.left(), r.top(), r.width(), r.height() }; - painter.draw_rectangle( - r6, - Color::white() - ); - } - } - - int16_t RSSI::get_min() - { - return min_ ; - } - - int16_t RSSI::get_avg() - { - return avg_ ; } - - int16_t RSSI::get_max() - { - return max_ ; - } - - int16_t RSSI::get_delta() - { - return max_ - min_ ; - } - - - void RSSI::set_pitch_rssi(bool enabled) { - pitch_rssi_enabled = enabled; - if (!enabled) baseband::set_pitch_rssi(0, false); + if (pitch_rssi_enabled) { + baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true); } - - void RSSI::set_vertical_rssi(bool enabled) { - if( enabled ) - vertical_rssi_enabled = true ; - else - vertical_rssi_enabled = false ; + if (has_focus() || highlighted()) { + const Rect r6{r.left(), r.top(), r.width(), r.height()}; + painter.draw_rectangle( + r6, + Color::white()); } - - void RSSI::set_peak(bool enabled, size_t duration) { - peak_enabled = enabled ; - peak_duration = duration ; - } - - void RSSI::on_statistics_update(const RSSIStatistics& statistics) { - min_ = statistics.min; - avg_ = statistics.accumulator / statistics.count; - max_ = statistics.max; - if( peak_enabled ) - { - peak_duration_ = peak_duration_ + 100 ; - if( max_ > peak_ ) - { - peak_ = max_ ; - } - if( peak_duration_ > peak_duration ) - { - peak_duration_ = 0 ; - peak_ = max_ ; - } +} + +int16_t RSSI::get_min() { + return min_; +} + +int16_t RSSI::get_avg() { + return avg_; +} + +int16_t RSSI::get_max() { + return max_; +} + +int16_t RSSI::get_delta() { + return max_ - min_; +} + +void RSSI::set_pitch_rssi(bool enabled) { + pitch_rssi_enabled = enabled; + if (!enabled) baseband::set_pitch_rssi(0, false); +} + +void RSSI::set_vertical_rssi(bool enabled) { + if (enabled) + vertical_rssi_enabled = true; + else + vertical_rssi_enabled = false; +} + +void RSSI::set_peak(bool enabled, size_t duration) { + peak_enabled = enabled; + peak_duration = duration; +} + +void RSSI::on_statistics_update(const RSSIStatistics& statistics) { + min_ = statistics.min; + avg_ = statistics.accumulator / statistics.count; + max_ = statistics.max; + if (peak_enabled) { + peak_duration_ = peak_duration_ + 100; + if (max_ > peak_) { + peak_ = max_; } - set_dirty(); - } - - void RSSIGraph::paint(Painter& painter) { - const auto r = screen_rect(); - - RSSIGraph_entry& prev_entry = graph_list[0]; - int xpos = 0 , prev_xpos = r.width(); - - for( int n = 1 ; (unsigned)n <= graph_list.size() ; n++ ) - { - xpos = ( r.width() * (graph_list.size() - n ) ) / graph_list.size() ; - int size = abs( xpos - prev_xpos ); - RSSIGraph_entry& entry = graph_list[ n - 1 ]; - - // black - const Rect r0{ r.right() - prev_xpos , r.top() , size , r.height() }; - painter.fill_rectangle( - r0, - Color::black()); - - // y_max - int top_y_val = max( entry.rssi_max , prev_entry.rssi_max ); - int width_y = abs( entry.rssi_max - prev_entry.rssi_max ); - if( width_y == 0 ) - width_y = 1 ; - const Point p1v{ r.right() - prev_xpos , r.bottom() - top_y_val }; - painter.draw_vline( - p1v, - width_y, - Color::red()); - const Point p1h{ r.right() - prev_xpos , r.bottom() - entry.rssi_max }; - painter.draw_hline( - p1h, - size, - Color::red()); - - // y_avg - top_y_val = max( entry.rssi_avg , prev_entry.rssi_avg ); - width_y = abs( entry.rssi_avg - prev_entry.rssi_avg ); - if( width_y == 0 ) - width_y = 1 ; - const Point p2v{ r.right() - prev_xpos , r.bottom() - top_y_val }; - painter.draw_vline( - p2v, - width_y, - Color::white()); - const Point p2h{ r.right() - prev_xpos , r.bottom() - entry.rssi_avg }; - painter.draw_hline( - p2h, - size, - Color::white()); - - // y_min - top_y_val = max( entry.rssi_min , prev_entry.rssi_min ); - width_y = abs( entry.rssi_min - prev_entry.rssi_min ); - if( width_y == 0 ) - width_y = 1 ; - const Point p3v{ r.right() - prev_xpos , r.bottom() - top_y_val }; - painter.draw_vline( - p3v, - width_y, - Color::blue()); - const Point p3h{ r.right() - prev_xpos , r.bottom() - entry.rssi_min }; - painter.draw_hline( - p3h, - size, - Color::blue()); - - // hack to display db - top_y_val = max( entry.db , prev_entry.db ); - width_y = abs( entry.db - prev_entry.db ); - if( width_y == 0 ) - width_y = 1 ; - const Point p4v{ r.right() - prev_xpos , r.bottom() - top_y_val }; - painter.draw_vline( - p4v, - width_y, - Color::green()); - const Point p4h{ r.right() - prev_xpos , r.bottom() - entry.db }; - painter.draw_hline( - p4h, - size, - Color::green()); - - prev_entry = entry ; - prev_xpos = xpos ; + if (peak_duration_ > peak_duration) { + peak_duration_ = 0; + peak_ = max_; } } + set_dirty(); +} + +void RSSIGraph::paint(Painter& painter) { + const auto r = screen_rect(); + + RSSIGraph_entry& prev_entry = graph_list[0]; + int xpos = 0, prev_xpos = r.width(); + + for (int n = 1; (unsigned)n <= graph_list.size(); n++) { + xpos = (r.width() * (graph_list.size() - n)) / graph_list.size(); + int size = abs(xpos - prev_xpos); + RSSIGraph_entry& entry = graph_list[n - 1]; + + // black + const Rect r0{r.right() - prev_xpos, r.top(), size, r.height()}; + painter.fill_rectangle( + r0, + Color::black()); + + // y_max + int top_y_val = max(entry.rssi_max, prev_entry.rssi_max); + int width_y = abs(entry.rssi_max - prev_entry.rssi_max); + if (width_y == 0) + width_y = 1; + const Point p1v{r.right() - prev_xpos, r.bottom() - top_y_val}; + painter.draw_vline( + p1v, + width_y, + Color::red()); + const Point p1h{r.right() - prev_xpos, r.bottom() - entry.rssi_max}; + painter.draw_hline( + p1h, + size, + Color::red()); + + // y_avg + top_y_val = max(entry.rssi_avg, prev_entry.rssi_avg); + width_y = abs(entry.rssi_avg - prev_entry.rssi_avg); + if (width_y == 0) + width_y = 1; + const Point p2v{r.right() - prev_xpos, r.bottom() - top_y_val}; + painter.draw_vline( + p2v, + width_y, + Color::white()); + const Point p2h{r.right() - prev_xpos, r.bottom() - entry.rssi_avg}; + painter.draw_hline( + p2h, + size, + Color::white()); + + // y_min + top_y_val = max(entry.rssi_min, prev_entry.rssi_min); + width_y = abs(entry.rssi_min - prev_entry.rssi_min); + if (width_y == 0) + width_y = 1; + const Point p3v{r.right() - prev_xpos, r.bottom() - top_y_val}; + painter.draw_vline( + p3v, + width_y, + Color::blue()); + const Point p3h{r.right() - prev_xpos, r.bottom() - entry.rssi_min}; + painter.draw_hline( + p3h, + size, + Color::blue()); + + // hack to display db + top_y_val = max(entry.db, prev_entry.db); + width_y = abs(entry.db - prev_entry.db); + if (width_y == 0) + width_y = 1; + const Point p4v{r.right() - prev_xpos, r.bottom() - top_y_val}; + painter.draw_vline( + p4v, + width_y, + Color::green()); + const Point p4h{r.right() - prev_xpos, r.bottom() - entry.db}; + painter.draw_hline( + p4h, + size, + Color::green()); + + prev_entry = entry; + prev_xpos = xpos; + } +} - - - /*void RSSIGraph::paintOld(Painter& painter) { +/*void RSSIGraph::paintOld(Painter& painter) { const auto r = screen_rect(); int16_t size = r.width() / nb_columns ; int16_t top_y_val = 0 ; @@ -336,7 +304,7 @@ namespace ui { if( size < 1 ) size = 1 ; - int xpos = r.right() ; + int xpos = r.right() ; for ( int n = 2 ; (unsigned)n <= graph_list.size(); n++) { auto& entry = graph_list[graph_list.size()-n]; auto& prev_entry = graph_list[graph_list.size()-(n-1)]; @@ -357,7 +325,7 @@ namespace ui { r1, Color::red()); - // y_avg + // y_avg top_y_val = max( entry.rssi_avg , prev_entry.rssi_avg ); width_y = abs( entry.rssi_avg - prev_entry.rssi_avg ); if( width_y == 0 ) @@ -397,105 +365,100 @@ namespace ui { } }*/ - void RSSIGraph::add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db ) - { - const auto r = screen_rect(); - - constexpr int rssi_sample_range = 256; - //constexpr float rssi_voltage_min = 0.4; - constexpr float rssi_voltage_max = 2.2; - constexpr float adc_voltage_max = 3.3; - //constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; - constexpr int raw_min = 0 ; - constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; - constexpr int raw_delta = raw_max - raw_min; - - // vertical bottom to top level meters - const range_t y_avg_range { 0, r.height() - 1 }; - const int16_t y_avg = y_avg_range.clip((rssi_avg - raw_min) * r.height() / raw_delta); - const range_t y_min_range { 0, y_avg }; - const int16_t y_min = y_min_range.clip((rssi_min - raw_min) * r.height() / raw_delta); - const range_t y_max_range { y_avg + 1, r.height() - 1 }; - const int16_t y_max = y_max_range.clip((rssi_max - raw_min) * r.height() / raw_delta); - const range_t db_range { -80 , 10 }; - int16_t db_ = db_range.clip( db ); - db_ = db_ - 10 ; - db_ = db_ * r.height() / 90 ; - db_ = r.height() + db_ ; - - graph_list . push_back( { y_min, y_avg, y_max , db_ } ); - while( graph_list.size() > nb_columns ) - { - graph_list.erase( graph_list.begin() ); - } - set_dirty(); - } - - void RSSIGraph::set_nb_columns( int16_t nb ) - { - nb_columns = nb ; - while( graph_list.size() > nb_columns ) - { - graph_list.erase( graph_list.begin() ); - } +void RSSIGraph::add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db) { + const auto r = screen_rect(); + + constexpr int rssi_sample_range = 256; + // constexpr float rssi_voltage_min = 0.4; + constexpr float rssi_voltage_max = 2.2; + constexpr float adc_voltage_max = 3.3; + // constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; + constexpr int raw_min = 0; + constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; + constexpr int raw_delta = raw_max - raw_min; + + // vertical bottom to top level meters + const range_t y_avg_range{0, r.height() - 1}; + const int16_t y_avg = y_avg_range.clip((rssi_avg - raw_min) * r.height() / raw_delta); + const range_t y_min_range{0, y_avg}; + const int16_t y_min = y_min_range.clip((rssi_min - raw_min) * r.height() / raw_delta); + const range_t y_max_range{y_avg + 1, r.height() - 1}; + const int16_t y_max = y_max_range.clip((rssi_max - raw_min) * r.height() / raw_delta); + const range_t db_range{-80, 10}; + int16_t db_ = db_range.clip(db); + db_ = db_ - 10; + db_ = db_ * r.height() / 90; + db_ = r.height() + db_; + + graph_list.push_back({y_min, y_avg, y_max, db_}); + while (graph_list.size() > nb_columns) { + graph_list.erase(graph_list.begin()); } + set_dirty(); +} - void RSSIGraph::on_hide() { - nb_columns_before_hide = nb_columns ; - nb_columns = 1 ; - while( graph_list.size() > nb_columns ) - { - graph_list.erase( graph_list.begin() ); - } +void RSSIGraph::set_nb_columns(int16_t nb) { + nb_columns = nb; + while (graph_list.size() > nb_columns) { + graph_list.erase(graph_list.begin()); } +} - void RSSIGraph::on_show() { - nb_columns = nb_columns_before_hide ; +void RSSIGraph::on_hide() { + nb_columns_before_hide = nb_columns; + nb_columns = 1; + while (graph_list.size() > nb_columns) { + graph_list.erase(graph_list.begin()); } - - void RSSI::on_focus() { - if( on_highlight ) - on_highlight(*this); +} + +void RSSIGraph::on_show() { + nb_columns = nb_columns_before_hide; +} + +void RSSI::on_focus() { + if (on_highlight) + on_highlight(*this); +} + +bool RSSI::on_key(const KeyEvent key) { + if (key == KeyEvent::Select) { + if (on_select) { + on_select(*this); + return true; + } + } else { + if (on_dir) { + return on_dir(*this, key); + } } - - bool RSSI::on_key(const KeyEvent key) { - if( key == KeyEvent::Select ) { - if( on_select ) { + return false; +} + +bool RSSI::on_touch(const TouchEvent event) { + switch (event.type) { + case TouchEvent::Type::Start: + set_highlighted(true); + set_dirty(); + if (on_touch_press) { + on_touch_press(*this); + } + if (on_select && instant_exec_) { on_select(*this); - return true; } - } else { - if( on_dir ) { - return on_dir(*this, key); + return true; + case TouchEvent::Type::End: + set_highlighted(false); + set_dirty(); + if (on_touch_release) { + on_touch_release(*this); } - } - return false; - } - - bool RSSI::on_touch(const TouchEvent event) { - switch(event.type) { - case TouchEvent::Type::Start: - set_highlighted(true); - set_dirty(); - if( on_touch_press) { - on_touch_press(*this); - } - if( on_select && instant_exec_ ) { - on_select(*this); - } - return true; - case TouchEvent::Type::End: - set_highlighted(false); - set_dirty(); - if( on_touch_release) { - on_touch_release(*this); - } - if( on_select && !instant_exec_ ) { - on_select(*this); - } - return true; - default: - return false; - } + if (on_select && !instant_exec_) { + on_select(*this); + } + return true; + default: + return false; } +} } /* namespace ui */ diff --git a/firmware/application/ui/ui_rssi.hpp b/firmware/application/ui/ui_rssi.hpp index 44f32c116..45bead60e 100644 --- a/firmware/application/ui/ui_rssi.hpp +++ b/firmware/application/ui/ui_rssi.hpp @@ -31,100 +31,95 @@ namespace ui { - class RSSI : public Widget { - public: - std::function on_select { }; - std::function on_touch_release { }; // Executed when releasing touch, after on_select. - std::function on_touch_press { }; // Executed when touching, before on_select. - std::function on_dir { }; - std::function on_highlight { }; - - RSSI(Rect parent_rect, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing - RSSI( - Rect parent_rect - ) : RSSI { parent_rect, false } - { - } - - RSSI( - ) : RSSI { { }, { } } - { - } - - int16_t get_min(); - int16_t get_avg(); - int16_t get_max(); - int16_t get_delta(); - void set_vertical_rssi(bool enabled); - void set_peak(bool enabled, size_t duration); - - void paint(Painter& painter) override; - void on_focus() override; - bool on_key(const KeyEvent key) override; - bool on_touch(const TouchEvent event) override; - - private: - int16_t min_ = 0; - int16_t avg_ = 0; - int16_t max_ = 0; - int16_t peak_ = 0 ; - size_t peak_duration_ = 0 ; - bool instant_exec_ { false }; - - bool pitch_rssi_enabled = false; - bool vertical_rssi_enabled = false; // scale [vertically/from bottom to top] - // instead of [horizontally/from left to right] - bool peak_enabled = false; - size_t peak_duration = 1000; // peak duration in msec before being reset to actual max_rssi - - MessageHandlerRegistration message_handler_stats { - Message::ID::RSSIStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - - MessageHandlerRegistration message_handler_pitch_rssi { - Message::ID::PitchRSSIConfigure, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->set_pitch_rssi(message.enabled); - } - }; - - void on_statistics_update(const RSSIStatistics& statistics); - void set_pitch_rssi(bool enabled); - }; - - struct RSSIGraph_entry { - int16_t rssi_min { 0 }; - int16_t rssi_avg { 0 }; - int16_t rssi_max { 0 }; - int16_t db { 0 }; - }; - - using RSSIGraphList = std::vector; - - class RSSIGraph : public Widget { - public: - RSSIGraph( - const Rect parent_rect - ) : Widget { parent_rect } - { - } - void paint(Painter& painter) override; - void add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db ); - void set_nb_columns( int16_t nb ); - - void on_hide() override ; - void on_show() override ; - - private: - uint16_t nb_columns_before_hide = 16 ; - uint16_t nb_columns = 16 ; - RSSIGraphList graph_list { } ; - }; - -} - -#endif/*__UI_RSSI_H__*/ +class RSSI : public Widget { + public: + std::function on_select{}; + std::function on_touch_release{}; // Executed when releasing touch, after on_select. + std::function on_touch_press{}; // Executed when touching, before on_select. + std::function on_dir{}; + std::function on_highlight{}; + + RSSI(Rect parent_rect, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing + RSSI( + Rect parent_rect) + : RSSI{parent_rect, false} { + } + + RSSI() + : RSSI{{}, {}} { + } + + int16_t get_min(); + int16_t get_avg(); + int16_t get_max(); + int16_t get_delta(); + void set_vertical_rssi(bool enabled); + void set_peak(bool enabled, size_t duration); + + void paint(Painter& painter) override; + void on_focus() override; + bool on_key(const KeyEvent key) override; + bool on_touch(const TouchEvent event) override; + + private: + int16_t min_ = 0; + int16_t avg_ = 0; + int16_t max_ = 0; + int16_t peak_ = 0; + size_t peak_duration_ = 0; + bool instant_exec_{false}; + + bool pitch_rssi_enabled = false; + bool vertical_rssi_enabled = false; // scale [vertically/from bottom to top] + // instead of [horizontally/from left to right] + bool peak_enabled = false; + size_t peak_duration = 1000; // peak duration in msec before being reset to actual max_rssi + + MessageHandlerRegistration message_handler_stats{ + Message::ID::RSSIStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + + MessageHandlerRegistration message_handler_pitch_rssi{ + Message::ID::PitchRSSIConfigure, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->set_pitch_rssi(message.enabled); + }}; + + void on_statistics_update(const RSSIStatistics& statistics); + void set_pitch_rssi(bool enabled); +}; + +struct RSSIGraph_entry { + int16_t rssi_min{0}; + int16_t rssi_avg{0}; + int16_t rssi_max{0}; + int16_t db{0}; +}; + +using RSSIGraphList = std::vector; + +class RSSIGraph : public Widget { + public: + RSSIGraph( + const Rect parent_rect) + : Widget{parent_rect} { + } + void paint(Painter& painter) override; + void add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db); + void set_nb_columns(int16_t nb); + + void on_hide() override; + void on_show() override; + + private: + uint16_t nb_columns_before_hide = 16; + uint16_t nb_columns = 16; + RSSIGraphList graph_list{}; +}; + +} // namespace ui + +#endif /*__UI_RSSI_H__*/ diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index 878b3c1f2..305a89de7 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -39,355 +39,337 @@ namespace spectrum { /* AudioSpectrumView******************************************************/ AudioSpectrumView::AudioSpectrumView( - const Rect parent_rect -) : View { parent_rect } -{ - set_focusable(true); - - add_children({ - &labels, - &field_frequency, - &waveform - }); - - field_frequency.on_change = [this](int32_t) { - set_dirty(); - }; - field_frequency.set_value(0); + const Rect parent_rect) + : View{parent_rect} { + set_focusable(true); + + add_children({&labels, + &field_frequency, + &waveform}); + + field_frequency.on_change = [this](int32_t) { + set_dirty(); + }; + field_frequency.set_value(0); } void AudioSpectrumView::paint(Painter& painter) { - const auto r = screen_rect(); - - painter.fill_rectangle(r, Color::black()); - - //if( !spectrum_sampling_rate ) return; - - // Cursor - const Rect r_cursor { - field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, - 1, cursor_band_height - }; - painter.fill_rectangle( - r_cursor, - Color::red() - ); + const auto r = screen_rect(); + + painter.fill_rectangle(r, Color::black()); + + // if( !spectrum_sampling_rate ) return; + + // Cursor + const Rect r_cursor{ + field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, + 1, cursor_band_height}; + painter.fill_rectangle( + r_cursor, + Color::red()); } void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum* spectrum) { - for (size_t i = 0; i < spectrum->db.size(); i++) - audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; - waveform.set_dirty(); + for (size_t i = 0; i < spectrum->db.size(); i++) + audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; + waveform.set_dirty(); } /* FrequencyScale ********************************************************/ void FrequencyScale::on_show() { - clear(); + clear(); } void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) { - if( (spectrum_sampling_rate != new_sampling_rate) ) { - spectrum_sampling_rate = new_sampling_rate; - set_dirty(); - } + if ((spectrum_sampling_rate != new_sampling_rate)) { + spectrum_sampling_rate = new_sampling_rate; + set_dirty(); + } } void FrequencyScale::set_channel_filter( - const int low_frequency, - const int high_frequency, - const int transition -) { - if( (channel_filter_low_frequency != low_frequency) || - (channel_filter_high_frequency != high_frequency) || - (channel_filter_transition != transition) ) { - channel_filter_low_frequency = low_frequency; - channel_filter_high_frequency = high_frequency; - channel_filter_transition = transition; - set_dirty(); - } + const int low_frequency, + const int high_frequency, + const int transition) { + if ((channel_filter_low_frequency != low_frequency) || + (channel_filter_high_frequency != high_frequency) || + (channel_filter_transition != transition)) { + channel_filter_low_frequency = low_frequency; + channel_filter_high_frequency = high_frequency; + channel_filter_transition = transition; + set_dirty(); + } } void FrequencyScale::paint(Painter& painter) { - const auto r = screen_rect(); - - clear_background(painter, r); - - if( !spectrum_sampling_rate ) { - // Can't draw without non-zero scale. - return; - } - - draw_filter_ranges(painter, r); - draw_frequency_ticks(painter, r); - - if (_blink) { - const Rect r_cursor { - 118 + cursor_position, r.bottom() - filter_band_height, - 5, filter_band_height - }; - painter.fill_rectangle( - r_cursor, - Color::red() - ); - } + const auto r = screen_rect(); + + clear_background(painter, r); + + if (!spectrum_sampling_rate) { + // Can't draw without non-zero scale. + return; + } + + draw_filter_ranges(painter, r); + draw_frequency_ticks(painter, r); + + if (_blink) { + const Rect r_cursor{ + 118 + cursor_position, r.bottom() - filter_band_height, + 5, filter_band_height}; + painter.fill_rectangle( + r_cursor, + Color::red()); + } } void FrequencyScale::clear() { - spectrum_sampling_rate = 0; - set_dirty(); + spectrum_sampling_rate = 0; + set_dirty(); } void FrequencyScale::clear_background(Painter& painter, const Rect r) { - painter.fill_rectangle(r, Color::black()); + painter.fill_rectangle(r, Color::black()); } void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) { - const auto x_center = r.width() / 2; - - const Rect tick { r.left() + x_center, r.top(), 1, r.height() }; - painter.fill_rectangle(tick, Color::white()); - - constexpr int tick_count_max = 4; - float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max; - int magnitude = 1; - int magnitude_n = 0; - while(rough_tick_interval >= 10.0f) { - rough_tick_interval /= 10; - magnitude *= 10; - magnitude_n += 1; - } - const int tick_interval = std::ceil(rough_tick_interval); - - auto tick_offset = tick_interval; - while((tick_offset * magnitude) < spectrum_sampling_rate / 2) { - const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate; - - const std::string zero_pad = - ((magnitude_n % 3) == 0) ? "" : - ((magnitude_n % 3) == 1) ? "0" : "00"; - const std::string unit = - (magnitude_n >= 6) ? "M" : - (magnitude_n >= 3) ? "k" : ""; - const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit; - const auto label_width = style().font.size_of(label).width(); - - const Coord offset_low = r.left() + x_center - pixel_offset; - const Rect tick_low { offset_low, r.top(), 1, r.height() }; - painter.fill_rectangle(tick_low, Color::white()); - painter.draw_string({ offset_low + 2, r.top() }, style(), label ); - - const Coord offset_high = r.left() + x_center + pixel_offset; - const Rect tick_high { offset_high, r.top(), 1, r.height() }; - painter.fill_rectangle(tick_high, Color::white()); - painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label ); - - tick_offset += tick_interval; - } + const auto x_center = r.width() / 2; + + const Rect tick{r.left() + x_center, r.top(), 1, r.height()}; + painter.fill_rectangle(tick, Color::white()); + + constexpr int tick_count_max = 4; + float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max; + int magnitude = 1; + int magnitude_n = 0; + while (rough_tick_interval >= 10.0f) { + rough_tick_interval /= 10; + magnitude *= 10; + magnitude_n += 1; + } + const int tick_interval = std::ceil(rough_tick_interval); + + auto tick_offset = tick_interval; + while ((tick_offset * magnitude) < spectrum_sampling_rate / 2) { + const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate; + + const std::string zero_pad = + ((magnitude_n % 3) == 0) ? "" : ((magnitude_n % 3) == 1) ? "0" + : "00"; + const std::string unit = + (magnitude_n >= 6) ? "M" : (magnitude_n >= 3) ? "k" + : ""; + const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit; + const auto label_width = style().font.size_of(label).width(); + + const Coord offset_low = r.left() + x_center - pixel_offset; + const Rect tick_low{offset_low, r.top(), 1, r.height()}; + painter.fill_rectangle(tick_low, Color::white()); + painter.draw_string({offset_low + 2, r.top()}, style(), label); + + const Coord offset_high = r.left() + x_center + pixel_offset; + const Rect tick_high{offset_high, r.top(), 1, r.height()}; + painter.fill_rectangle(tick_high, Color::white()); + painter.draw_string({offset_high - 2 - label_width, r.top()}, style(), label); + + tick_offset += tick_interval; + } } void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) { - if( channel_filter_low_frequency != channel_filter_high_frequency ) { - const auto x_center = r.width() / 2; - - const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate; - const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate; - - if( channel_filter_transition ) { - const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate; - - const Rect r_all { - r.left() + x_low - trans, r.bottom() - filter_band_height, - x_high - x_low + trans*2, filter_band_height - }; - painter.fill_rectangle( - r_all, - Color::yellow() - ); - } - - const Rect r_pass { - r.left() + x_low, r.bottom() - filter_band_height, - x_high - x_low, filter_band_height - }; - painter.fill_rectangle( - r_pass, - Color::green() - ); - } + if (channel_filter_low_frequency != channel_filter_high_frequency) { + const auto x_center = r.width() / 2; + + const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate; + const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate; + + if (channel_filter_transition) { + const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate; + + const Rect r_all{ + r.left() + x_low - trans, r.bottom() - filter_band_height, + x_high - x_low + trans * 2, filter_band_height}; + painter.fill_rectangle( + r_all, + Color::yellow()); + } + + const Rect r_pass{ + r.left() + x_low, r.bottom() - filter_band_height, + x_high - x_low, filter_band_height}; + painter.fill_rectangle( + r_pass, + Color::green()); + } } void FrequencyScale::on_focus() { - _blink = true; - on_tick_second(); - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; + _blink = true; + on_tick_second(); + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; } void FrequencyScale::on_blur() { - rtc_time::signal_tick_second -= signal_token_tick_second; - _blink = false; - set_dirty(); + rtc_time::signal_tick_second -= signal_token_tick_second; + _blink = false; + set_dirty(); } bool FrequencyScale::on_encoder(const EncoderEvent delta) { - cursor_position += delta; - - cursor_position = std::min(cursor_position, 119); - cursor_position = std::max(cursor_position, -120); - - set_dirty(); - - return true; + cursor_position += delta; + + cursor_position = std::min(cursor_position, 119); + cursor_position = std::max(cursor_position, -120); + + set_dirty(); + + return true; } bool FrequencyScale::on_key(const KeyEvent key) { - if( key == KeyEvent::Select ) { - if( on_select ) { - on_select((cursor_position * spectrum_sampling_rate) / 240); - cursor_position = 0; - return true; - } - } - - return false; + if (key == KeyEvent::Select) { + if (on_select) { + on_select((cursor_position * spectrum_sampling_rate) / 240); + cursor_position = 0; + return true; + } + } + + return false; } void FrequencyScale::on_tick_second() { - set_dirty(); - _blink = !_blink; + set_dirty(); + _blink = !_blink; } /* WaterfallView *********************************************************/ void WaterfallView::on_show() { - clear(); + clear(); - const auto screen_r = screen_rect(); - display.scroll_set_area(screen_r.top(), screen_r.bottom()); + const auto screen_r = screen_rect(); + display.scroll_set_area(screen_r.top(), screen_r.bottom()); } void WaterfallView::on_hide() { - /* TODO: Clear region to eliminate brief flash of content at un-shifted - * position? - */ - display.scroll_disable(); + /* TODO: Clear region to eliminate brief flash of content at un-shifted + * position? + */ + display.scroll_disable(); } void WaterfallView::paint(Painter& painter) { - // Do nothing. - (void)painter; + // Do nothing. + (void)painter; } void WaterfallView::on_channel_spectrum( - const ChannelSpectrum& spectrum -) { - /* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */ - - std::array pixel_row; - for(size_t i=0; i<120; i++) { - const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]]; - pixel_row[i] = pixel_color; - } - - for(size_t i=120; i<240; i++) { - const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]]; - pixel_row[i] = pixel_color; - } - - const auto draw_y = display.scroll(1); - - display.draw_pixels( - { { 0, draw_y }, { pixel_row.size(), 1 } }, - pixel_row - ); + const ChannelSpectrum& spectrum) { + /* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */ + + std::array pixel_row; + for (size_t i = 0; i < 120; i++) { + const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]]; + pixel_row[i] = pixel_color; + } + + for (size_t i = 120; i < 240; i++) { + const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]]; + pixel_row[i] = pixel_color; + } + + const auto draw_y = display.scroll(1); + + display.draw_pixels( + {{0, draw_y}, {pixel_row.size(), 1}}, + pixel_row); } void WaterfallView::clear() { - display.fill_rectangle( - screen_rect(), - Color::black() - ); + display.fill_rectangle( + screen_rect(), + Color::black()); } /* WaterfallWidget *******************************************************/ WaterfallWidget::WaterfallWidget(const bool cursor) { - add_children({ - &waterfall_view, - &frequency_scale - }); - - frequency_scale.set_focusable(cursor); - - // Making the event climb up all the way up to here kinda sucks - frequency_scale.on_select = [this](int32_t offset) { - if (on_select) on_select(offset); - }; + add_children({&waterfall_view, + &frequency_scale}); + + frequency_scale.set_focusable(cursor); + + // Making the event climb up all the way up to here kinda sucks + frequency_scale.on_select = [this](int32_t offset) { + if (on_select) on_select(offset); + }; } void WaterfallWidget::on_show() { - baseband::spectrum_streaming_start(); + baseband::spectrum_streaming_start(); } void WaterfallWidget::on_hide() { - baseband::spectrum_streaming_stop(); + baseband::spectrum_streaming_stop(); } void WaterfallWidget::show_audio_spectrum_view(const bool show) { - if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return; - - if (show) { - audio_spectrum_view = std::make_unique(audio_spectrum_view_rect); - add_child(audio_spectrum_view.get()); - update_widgets_rect(); - } else { - audio_spectrum_update = false; - remove_child(audio_spectrum_view.get()); - audio_spectrum_view.reset(); - update_widgets_rect(); - } + if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return; + + if (show) { + audio_spectrum_view = std::make_unique(audio_spectrum_view_rect); + add_child(audio_spectrum_view.get()); + update_widgets_rect(); + } else { + audio_spectrum_update = false; + remove_child(audio_spectrum_view.get()); + audio_spectrum_view.reset(); + update_widgets_rect(); + } } void WaterfallWidget::update_widgets_rect() { - if (audio_spectrum_view) { - frequency_scale.set_parent_rect({ 0, audio_spectrum_height, screen_rect().width(), scale_height }); - waterfall_view.set_parent_rect(waterfall_reduced_rect); - } else { - frequency_scale.set_parent_rect({ 0, 0, screen_rect().width(), scale_height }); - waterfall_view.set_parent_rect(waterfall_normal_rect); - } - waterfall_view.on_show(); + if (audio_spectrum_view) { + frequency_scale.set_parent_rect({0, audio_spectrum_height, screen_rect().width(), scale_height}); + waterfall_view.set_parent_rect(waterfall_reduced_rect); + } else { + frequency_scale.set_parent_rect({0, 0, screen_rect().width(), scale_height}); + waterfall_view.set_parent_rect(waterfall_normal_rect); + } + waterfall_view.on_show(); } void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - - waterfall_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; - waterfall_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height }; - - update_widgets_rect(); + View::set_parent_rect(new_parent_rect); + + waterfall_normal_rect = {0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; + waterfall_reduced_rect = {0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height}; + + update_widgets_rect(); } void WaterfallWidget::paint(Painter& painter) { - // TODO: - (void)painter; + // TODO: + (void)painter; } void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { - waterfall_view.on_channel_spectrum(spectrum); - sampling_rate = spectrum.sampling_rate; - frequency_scale.set_spectrum_sampling_rate(sampling_rate); - frequency_scale.set_channel_filter( - spectrum.channel_filter_low_frequency, - spectrum.channel_filter_high_frequency, - spectrum.channel_filter_transition - ); + waterfall_view.on_channel_spectrum(spectrum); + sampling_rate = spectrum.sampling_rate; + frequency_scale.set_spectrum_sampling_rate(sampling_rate); + frequency_scale.set_channel_filter( + spectrum.channel_filter_low_frequency, + spectrum.channel_filter_high_frequency, + spectrum.channel_filter_transition); } void WaterfallWidget::on_audio_spectrum() { - audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); + audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); } } /* namespace spectrum */ diff --git a/firmware/application/ui/ui_spectrum.hpp b/firmware/application/ui/ui_spectrum.hpp index 1ba8b4a30..83b9539c7 100644 --- a/firmware/application/ui/ui_spectrum.hpp +++ b/firmware/application/ui/ui_spectrum.hpp @@ -36,167 +36,161 @@ namespace ui { namespace spectrum { class AudioSpectrumView : public View { -public: - AudioSpectrumView(const Rect parent_rect); - - void paint(Painter& painter) override; - - void on_audio_spectrum(const AudioSpectrum* spectrum); - -private: - static constexpr int cursor_band_height = 4; - - int16_t audio_spectrum[128] { 0 }; - - Labels labels { - { { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() } - }; - - NumberField field_frequency { - { 0 * 8, 0 * 16 }, - 5, - { 0, 48000 }, - 48000 / 240, - ' ' - }; - - Waveform waveform { - { 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 }, - audio_spectrum, - 128, - 0, - false, - Color::white() - }; + public: + AudioSpectrumView(const Rect parent_rect); + + void paint(Painter& painter) override; + + void on_audio_spectrum(const AudioSpectrum* spectrum); + + private: + static constexpr int cursor_band_height = 4; + + int16_t audio_spectrum[128]{0}; + + Labels labels{ + {{6 * 8, 0 * 16}, "Hz", Color::light_grey()}}; + + NumberField field_frequency{ + {0 * 8, 0 * 16}, + 5, + {0, 48000}, + 48000 / 240, + ' '}; + + Waveform waveform{ + {0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16}, + audio_spectrum, + 128, + 0, + false, + Color::white()}; }; class FrequencyScale : public Widget { -public: - std::function on_select { }; - - void on_show() override; - void on_focus() override; - void on_blur() override; - - bool on_encoder(const EncoderEvent delta) override; - bool on_key(const KeyEvent key) override; - - void set_spectrum_sampling_rate(const int new_sampling_rate); - void set_channel_filter(const int low_frequency, const int high_frequency, const int transition); - - void paint(Painter& painter) override; - -private: - static constexpr int filter_band_height = 4; - - void on_tick_second(); - - bool _blink { false }; - int32_t cursor_position { 0 }; - SignalToken signal_token_tick_second { }; - int spectrum_sampling_rate { 0 }; - const int spectrum_bins = std::tuple_size::value; - int channel_filter_low_frequency { 0 }; - int channel_filter_high_frequency { 0 }; - int channel_filter_transition { 0 }; - - void clear(); - void clear_background(Painter& painter, const Rect r); - - void draw_frequency_ticks(Painter& painter, const Rect r); - void draw_filter_ranges(Painter& painter, const Rect r); + public: + std::function on_select{}; + + void on_show() override; + void on_focus() override; + void on_blur() override; + + bool on_encoder(const EncoderEvent delta) override; + bool on_key(const KeyEvent key) override; + + void set_spectrum_sampling_rate(const int new_sampling_rate); + void set_channel_filter(const int low_frequency, const int high_frequency, const int transition); + + void paint(Painter& painter) override; + + private: + static constexpr int filter_band_height = 4; + + void on_tick_second(); + + bool _blink{false}; + int32_t cursor_position{0}; + SignalToken signal_token_tick_second{}; + int spectrum_sampling_rate{0}; + const int spectrum_bins = std::tuple_size::value; + int channel_filter_low_frequency{0}; + int channel_filter_high_frequency{0}; + int channel_filter_transition{0}; + + void clear(); + void clear_background(Painter& painter, const Rect r); + + void draw_frequency_ticks(Painter& painter, const Rect r); + void draw_filter_ranges(Painter& painter, const Rect r); }; class WaterfallView : public Widget { -public: - void on_show() override; - void on_hide() override; + public: + void on_show() override; + void on_hide() override; - void paint(Painter& painter) override; + void paint(Painter& painter) override; - void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_channel_spectrum(const ChannelSpectrum& spectrum); -private: - void clear(); + private: + void clear(); }; class WaterfallWidget : public View { -public: - std::function on_select { }; - - WaterfallWidget(const bool cursor = false); - - WaterfallWidget(const WaterfallWidget&) = delete; - WaterfallWidget(WaterfallWidget&&) = delete; - WaterfallWidget& operator=(const WaterfallWidget&) = delete; - WaterfallWidget& operator=(WaterfallWidget&&) = delete; - - void on_show() override; - void on_hide() override; - - void set_parent_rect(const Rect new_parent_rect) override; - - void show_audio_spectrum_view(const bool show); - - void paint(Painter& painter) override; - -private: - void update_widgets_rect(); - - const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 }; - static constexpr Dim audio_spectrum_height = 16 * 2 + 20; - static constexpr Dim scale_height = 20; - - WaterfallView waterfall_view { }; - FrequencyScale frequency_scale { }; - - ChannelSpectrumFIFO* channel_fifo { nullptr }; - AudioSpectrum* audio_spectrum_data { nullptr }; - bool audio_spectrum_update { false }; - - std::unique_ptr audio_spectrum_view { }; - - int sampling_rate { 0 }; - int32_t cursor_position { 0 }; - ui::Rect waterfall_normal_rect { }; - ui::Rect waterfall_reduced_rect { }; - - MessageHandlerRegistration message_handler_channel_spectrum_config { - Message::ID::ChannelSpectrumConfig, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->channel_fifo = message.fifo; - } - }; - MessageHandlerRegistration message_handler_audio_spectrum { - Message::ID::AudioSpectrum, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->audio_spectrum_data = message.data; - this->audio_spectrum_update = true; - } - }; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->channel_fifo ) { - ChannelSpectrum channel_spectrum; - while( channel_fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - if (this->audio_spectrum_update) { - this->audio_spectrum_update = false; - this->on_audio_spectrum(); - } - } - }; - - void on_channel_spectrum(const ChannelSpectrum& spectrum); - void on_audio_spectrum(); + public: + std::function on_select{}; + + WaterfallWidget(const bool cursor = false); + + WaterfallWidget(const WaterfallWidget&) = delete; + WaterfallWidget(WaterfallWidget&&) = delete; + WaterfallWidget& operator=(const WaterfallWidget&) = delete; + WaterfallWidget& operator=(WaterfallWidget&&) = delete; + + void on_show() override; + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void show_audio_spectrum_view(const bool show); + + void paint(Painter& painter) override; + + private: + void update_widgets_rect(); + + const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20}; + static constexpr Dim audio_spectrum_height = 16 * 2 + 20; + static constexpr Dim scale_height = 20; + + WaterfallView waterfall_view{}; + FrequencyScale frequency_scale{}; + + ChannelSpectrumFIFO* channel_fifo{nullptr}; + AudioSpectrum* audio_spectrum_data{nullptr}; + bool audio_spectrum_update{false}; + + std::unique_ptr audio_spectrum_view{}; + + int sampling_rate{0}; + int32_t cursor_position{0}; + ui::Rect waterfall_normal_rect{}; + ui::Rect waterfall_reduced_rect{}; + + MessageHandlerRegistration message_handler_channel_spectrum_config{ + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->channel_fifo = message.fifo; + }}; + MessageHandlerRegistration message_handler_audio_spectrum{ + Message::ID::AudioSpectrum, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->audio_spectrum_data = message.data; + this->audio_spectrum_update = true; + }}; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if (this->channel_fifo) { + ChannelSpectrum channel_spectrum; + while (channel_fifo->out(channel_spectrum)) { + this->on_channel_spectrum(channel_spectrum); + } + } + if (this->audio_spectrum_update) { + this->audio_spectrum_update = false; + this->on_audio_spectrum(); + } + }}; + + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_audio_spectrum(); }; } /* namespace spectrum */ } /* namespace ui */ -#endif/*__UI_SPECTRUM_H__*/ +#endif /*__UI_SPECTRUM_H__*/ diff --git a/firmware/application/ui/ui_tabview.cpp b/firmware/application/ui/ui_tabview.cpp index 9f250bc6a..682947caf 100644 --- a/firmware/application/ui/ui_tabview.cpp +++ b/firmware/application/ui/ui_tabview.cpp @@ -28,127 +28,123 @@ using namespace portapack; namespace ui { Tab::Tab() { - set_focusable(true); + set_focusable(true); }; void Tab::set( - uint32_t index, - Dim width, - std::string text, - Color text_color -) { - set_parent_rect({ (Coord)(index * width), 0, width, 24 }); - - text_ = text.substr(0, (width - 8) / 8); - text_color_ = text_color; - - index_ = index; + uint32_t index, + Dim width, + std::string text, + Color text_color) { + set_parent_rect({(Coord)(index * width), 0, width, 24}); + + text_ = text.substr(0, (width - 8) / 8); + text_color_ = text_color; + + index_ = index; } void Tab::paint(Painter& painter) { - const auto rect = screen_rect(); - const Color color = highlighted() ? Color::black() : Color::grey(); - - painter.fill_rectangle({ rect.left(), rect.top(), rect.width() - 8, rect.height() }, color); - - if (!highlighted()) - painter.draw_hline({ rect.left(), rect.top() }, rect.width() - 9, Color::light_grey()); - - painter.draw_bitmap( - { rect.right() - 8, rect.top() }, - bitmap_tab_edge, - color, - Color::dark_grey() - ); - - auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2); - - painter.draw_string( - text_point, - { ui::font::fixed_8x16, color, text_color_ }, - text_ - ); - - if (has_focus()) - painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white()); + const auto rect = screen_rect(); + const Color color = highlighted() ? Color::black() : Color::grey(); + + painter.fill_rectangle({rect.left(), rect.top(), rect.width() - 8, rect.height()}, color); + + if (!highlighted()) + painter.draw_hline({rect.left(), rect.top()}, rect.width() - 9, Color::light_grey()); + + painter.draw_bitmap( + {rect.right() - 8, rect.top()}, + bitmap_tab_edge, + color, + Color::dark_grey()); + + auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2); + + painter.draw_string( + text_point, + {ui::font::fixed_8x16, color, text_color_}, + text_); + + if (has_focus()) + painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white()); } bool Tab::on_key(const KeyEvent key) { - if( key == KeyEvent::Select ) { - static_cast(parent())->set_selected(index_); - return true; - } + if (key == KeyEvent::Select) { + static_cast(parent())->set_selected(index_); + return true; + } - return false; + return false; } - bool Tab::on_touch(const TouchEvent event) { - switch(event.type) { - case TouchEvent::Type::Start: - focus(); - set_dirty(); - return true; - - case TouchEvent::Type::End: - static_cast(parent())->set_selected(index_); - return true; - - default: - return false; - } + switch (event.type) { + case TouchEvent::Type::Start: + focus(); + set_dirty(); + return true; + + case TouchEvent::Type::End: + static_cast(parent())->set_selected(index_); + return true; + + default: + return false; + } } void TabView::set_selected(uint32_t index) { - Tab * tab; - - if (index >= n_tabs) - return; - - // Hide previous view - views[current_tab]->hidden(true); - - tab = &tabs[current_tab]; - tab->set_highlighted(false); - tab->set_focusable(true); - tab->set_dirty(); - - // Show new view - views[index]->hidden(false); - - tab = &tabs[index]; - current_tab = index; - tab->set_highlighted(true); - tab->set_focusable(false); - tab->set_dirty(); + Tab* tab; + + if (index >= n_tabs) + return; + + // Hide previous view + views[current_tab]->hidden(true); + + tab = &tabs[current_tab]; + tab->set_highlighted(false); + tab->set_focusable(true); + tab->set_dirty(); + + // Show new view + views[index]->hidden(false); + + tab = &tabs[index]; + current_tab = index; + tab->set_highlighted(true); + tab->set_focusable(false); + tab->set_dirty(); } void TabView::on_show() { - set_selected(current_tab); + set_selected(current_tab); } - + void TabView::focus() { - views[current_tab]->focus(); + views[current_tab]->focus(); } TabView::TabView(std::initializer_list tab_definitions) { - size_t i = 0; - - n_tabs = tab_definitions.size(); - if (n_tabs > MAX_TABS) - n_tabs = MAX_TABS; - - size_t tab_width = 240 / n_tabs; - - set_parent_rect({ 0, 0, 30 * 8, 3 * 8 }); - - for (auto &tab_definition : tab_definitions) { - tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color); - views[i] = tab_definition.view; - add_child(&tabs[i]); - i++; - if (i == MAX_TABS) break; - } + size_t i = 0; + + n_tabs = tab_definitions.size(); + if (n_tabs > MAX_TABS) + n_tabs = MAX_TABS; + + size_t tab_width = 240 / n_tabs; + + set_parent_rect({0, 0, 30 * 8, 3 * 8}); + + for (auto& tab_definition : tab_definitions) { + tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color); + views[i] = tab_definition.view; + add_child(&tabs[i]); + i++; + if (i == MAX_TABS) break; + } } TabView::~TabView() { diff --git a/firmware/application/ui/ui_tabview.hpp b/firmware/application/ui/ui_tabview.hpp index 0fed8619e..4b7de15c5 100644 --- a/firmware/application/ui/ui_tabview.hpp +++ b/firmware/application/ui/ui_tabview.hpp @@ -29,52 +29,52 @@ #include "ui_painter.hpp" namespace ui { - + #define MAX_TABS 5 class Tab : public Widget { -public: - Tab(); - - void paint(Painter& painter) override; - - bool on_key(const KeyEvent key) override; - bool on_touch(const TouchEvent event) override; - - void set(uint32_t index, Dim width, std::string text, Color text_color); - -private: - std::string text_ { }; - Color text_color_ { }; - uint32_t index_ { }; + public: + Tab(); + + void paint(Painter& painter) override; + + bool on_key(const KeyEvent key) override; + bool on_touch(const TouchEvent event) override; + + void set(uint32_t index, Dim width, std::string text, Color text_color); + + private: + std::string text_{}; + Color text_color_{}; + uint32_t index_{}; }; class TabView : public View { -public: - struct TabDef { - std::string text; - ui::Color color; - View* view; - }; - - TabView(std::initializer_list tab_definitions); - ~TabView(); - - void focus() override; - void on_show() override; - - void set_selected(uint32_t index); - uint32_t selected() { - return current_tab; - }; - -private: - size_t n_tabs { }; - std::array tabs { }; - std::array views { }; - uint32_t current_tab { 0 }; + public: + struct TabDef { + std::string text; + ui::Color color; + View* view; + }; + + TabView(std::initializer_list tab_definitions); + ~TabView(); + + void focus() override; + void on_show() override; + + void set_selected(uint32_t index); + uint32_t selected() { + return current_tab; + }; + + private: + size_t n_tabs{}; + std::array tabs{}; + std::array views{}; + uint32_t current_tab{0}; }; } /* namespace ui */ -#endif/*__UI_TABVIEW_H__*/ +#endif /*__UI_TABVIEW_H__*/ diff --git a/firmware/application/ui/ui_textentry.cpp b/firmware/application/ui/ui_textentry.cpp index 3718b39f2..4a832a289 100644 --- a/firmware/application/ui/ui_textentry.cpp +++ b/firmware/application/ui/ui_textentry.cpp @@ -30,71 +30,66 @@ using namespace portapack; namespace ui { void text_prompt( - NavigationView& nav, - std::string& str, - size_t max_length, - std::function on_done -) { - text_prompt(nav, str, str.length(), max_length, on_done); + NavigationView& nav, + std::string& str, + size_t max_length, + std::function on_done) { + text_prompt(nav, str, str.length(), max_length, on_done); } void text_prompt( - NavigationView& nav, - std::string& str, - uint32_t cursor_pos, - size_t max_length, - std::function on_done -) { - //if (persistent_memory::ui_config_textentry() == 0) { - auto te_view = nav.push(str, max_length); - te_view->set_cursor(cursor_pos); - te_view->on_changed = [on_done](std::string& value) { - if (on_done) - on_done(value); - }; - /*} else { - auto te_view = nav.push(str, max_length); - te_view->on_changed = [on_done](std::string * value) { - if (on_done) - on_done(value); - }; - }*/ + NavigationView& nav, + std::string& str, + uint32_t cursor_pos, + size_t max_length, + std::function on_done) { + // if (persistent_memory::ui_config_textentry() == 0) { + auto te_view = nav.push(str, max_length); + te_view->set_cursor(cursor_pos); + te_view->on_changed = [on_done](std::string& value) { + if (on_done) + on_done(value); + }; + /*} else { + auto te_view = nav.push(str, max_length); + te_view->on_changed = [on_done](std::string * value) { + if (on_done) + on_done(value); + }; + }*/ } /* TextEntryView ***********************************************************/ void TextEntryView::char_delete() { - text_input.char_delete(); + text_input.char_delete(); } void TextEntryView::char_add(const char c) { - text_input.char_add(c); + text_input.char_add(c); } void TextEntryView::set_cursor(uint32_t pos) { - text_input.set_cursor(pos); + text_input.set_cursor(pos); } void TextEntryView::focus() { - text_input.focus(); + text_input.focus(); } TextEntryView::TextEntryView( - NavigationView& nav, - std::string& str, - size_t max_length -) : text_input{ str, max_length, { 0, 0 } } -{ - add_children({ - &text_input, - &button_ok - }); + NavigationView& nav, + std::string& str, + size_t max_length) + : text_input{str, max_length, {0, 0}} { + add_children({&text_input, + &button_ok}); - button_ok.on_select = [this, &str, &nav](Button&) { - if (on_changed) - on_changed(str); - nav.pop(); - }; + button_ok.on_select = [this, &str, &nav](Button&) { + if (on_changed) + on_changed(str); + nav.pop(); + }; } } /* namespace ui */ diff --git a/firmware/application/ui/ui_textentry.hpp b/firmware/application/ui/ui_textentry.hpp index 02240847f..9163272fe 100644 --- a/firmware/application/ui/ui_textentry.hpp +++ b/firmware/application/ui/ui_textentry.hpp @@ -29,30 +29,29 @@ namespace ui { class TextEntryView : public View { -public: - std::function on_changed { }; - - void focus() override; - std::string title() const override { return "Text entry"; }; + public: + std::function on_changed{}; - void set_cursor(uint32_t pos); - -protected: - TextEntryView(NavigationView& nav, std::string& str, size_t max_length); - - TextEntryView(const TextEntryView&) = delete; - TextEntryView(TextEntryView&&) = delete; - TextEntryView& operator=(const TextEntryView&) = delete; - TextEntryView& operator=(TextEntryView&&) = delete; + void focus() override; + std::string title() const override { return "Text entry"; }; - void char_add(const char c); - void char_delete(); - - TextField text_input; - Button button_ok { - { 10 * 8, 33 * 8, 9 * 8, 32 }, - "OK" - }; + void set_cursor(uint32_t pos); + + protected: + TextEntryView(NavigationView& nav, std::string& str, size_t max_length); + + TextEntryView(const TextEntryView&) = delete; + TextEntryView(TextEntryView&&) = delete; + TextEntryView& operator=(const TextEntryView&) = delete; + TextEntryView& operator=(TextEntryView&&) = delete; + + void char_add(const char c); + void char_delete(); + + TextField text_input; + Button button_ok{ + {10 * 8, 33 * 8, 9 * 8, 32}, + "OK"}; }; // Show the TextEntry view to receive keyboard input. @@ -60,18 +59,18 @@ class TextEntryView : public View { // by reference and its lifetime must be ensured by the // caller until the TextEntry view is dismissed. void text_prompt( - NavigationView& nav, - std::string& str, - size_t max_length, - std::function on_done = nullptr); + NavigationView& nav, + std::string& str, + size_t max_length, + std::function on_done = nullptr); void text_prompt( - NavigationView& nav, - std::string& str, - uint32_t cursor_pos, - size_t max_length, - std::function on_done = nullptr); + NavigationView& nav, + std::string& str, + uint32_t cursor_pos, + size_t max_length, + std::function on_done = nullptr); } /* namespace ui */ -#endif/*__UI_TEXTENTRY_H__*/ +#endif /*__UI_TEXTENTRY_H__*/ diff --git a/firmware/application/ui/ui_transmitter.cpp b/firmware/application/ui/ui_transmitter.cpp index 9b1586b98..b0218e4ca 100644 --- a/firmware/application/ui/ui_transmitter.cpp +++ b/firmware/application/ui/ui_transmitter.cpp @@ -36,157 +36,156 @@ namespace ui { /* TransmitterView *******************************************************/ void TransmitterView::paint(Painter& painter) { - size_t c; - Point pos = { 0, screen_pos().y() }; - - for (c = 0; c < 20; c++) { - painter.draw_bitmap( - pos, - bitmap_stripes, - ui::Color(191, 191, 0), - ui::Color::black() - ); - if (c != 9) - pos += { 24, 0 }; - else - pos = { 0, screen_pos().y() + 32 + 8 }; - } + size_t c; + Point pos = {0, screen_pos().y()}; + + for (c = 0; c < 20; c++) { + painter.draw_bitmap( + pos, + bitmap_stripes, + ui::Color(191, 191, 0), + ui::Color::black()); + if (c != 9) + pos += {24, 0}; + else + pos = {0, screen_pos().y() + 32 + 8}; + } } void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) { - transmitter_model.set_tuning_frequency(f); + transmitter_model.set_tuning_frequency(f); } void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) { - transmitter_model.set_channel_bandwidth(channel_bandwidth); + transmitter_model.set_channel_bandwidth(channel_bandwidth); } void TransmitterView::on_tx_gain_changed(int32_t tx_gain) { - transmitter_model.set_tx_gain(tx_gain); - update_gainlevel_styles(); + transmitter_model.set_tx_gain(tx_gain); + update_gainlevel_styles(); } void TransmitterView::on_tx_amp_changed(bool rf_amp) { - transmitter_model.set_rf_amp(rf_amp); - update_gainlevel_styles(); + transmitter_model.set_rf_amp(rf_amp); + update_gainlevel_styles(); } void TransmitterView::update_gainlevel_styles() { - const Style *new_style_ptr = NULL; - int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0); - - if(tot_gain > POWER_THRESHOLD_HIGH) { - new_style_ptr = &style_power_high; - } else if(tot_gain > POWER_THRESHOLD_MED) { - new_style_ptr = &style_power_med; - } else if(tot_gain > POWER_THRESHOLD_LOW) { - new_style_ptr = &style_power_low; - } - - field_gain.set_style(new_style_ptr); - text_gain.set_style(new_style_ptr); - field_amp.set_style(new_style_ptr); - text_amp.set_style(new_style_ptr); + const Style* new_style_ptr = NULL; + int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0); + + if (tot_gain > POWER_THRESHOLD_HIGH) { + new_style_ptr = &style_power_high; + } else if (tot_gain > POWER_THRESHOLD_MED) { + new_style_ptr = &style_power_med; + } else if (tot_gain > POWER_THRESHOLD_LOW) { + new_style_ptr = &style_power_low; + } + + field_gain.set_style(new_style_ptr); + text_gain.set_style(new_style_ptr); + field_amp.set_style(new_style_ptr); + text_amp.set_style(new_style_ptr); } void TransmitterView::set_transmitting(const bool transmitting) { - if (transmitting) { - button_start.set_text("STOP"); - button_start.set_style(&style_stop); - } else { - button_start.set_text("START"); - button_start.set_style(&style_start); - } - - transmitting_ = transmitting; + if (transmitting) { + button_start.set_text("STOP"); + button_start.set_style(&style_stop); + } else { + button_start.set_text("START"); + button_start.set_style(&style_start); + } + + transmitting_ = transmitting; } void TransmitterView::on_show() { - field_frequency.set_value(transmitter_model.tuning_frequency()); - field_frequency_step.set_by_value(receiver_model.frequency_step()); + field_frequency.set_value(transmitter_model.tuning_frequency()); + field_frequency_step.set_by_value(receiver_model.frequency_step()); - field_gain.set_value(transmitter_model.tx_gain()); - field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0); + field_gain.set_value(transmitter_model.tx_gain()); + field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0); - update_gainlevel_styles(); + update_gainlevel_styles(); } void TransmitterView::focus() { - button_start.focus(); + button_start.focus(); } TransmitterView::TransmitterView( - const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock -) : lock_ { lock } -{ - set_parent_rect({ 0, y, 30 * 8, 6 * 8 }); - - add_children({ - &field_frequency, - &field_frequency_step, - &text_gain, - &field_gain, - &button_start, - &text_amp, - &field_amp, - }); - - set_transmitting(false); - - if (lock_) { - field_frequency.set_focusable(false); - field_frequency.set_style(&style_locked); - } else { - if (channel_bandwidth) { - add_children({ - &text_bw, - &field_bw - }); - - field_bw.on_change = [this](int32_t v) { - on_channel_bandwidth_changed(v * 1000); - }; - field_bw.set_value(channel_bandwidth); - } - } - - //field_frequency.set_value(transmitter_model.tuning_frequency()); - field_frequency.set_step(frequency_step); - field_frequency.on_change = [this](rf::Frequency f) { - on_tuning_frequency_changed(f); - }; - field_frequency.on_edit = [this]() { - if (on_edit_frequency) - on_edit_frequency(); - }; - - field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { - this->field_frequency.set_step(v); - }; - - field_gain.on_change = [this](uint32_t tx_gain) { - on_tx_gain_changed(tx_gain); - }; - - field_amp.on_change = [this](uint32_t rf_amp) { - on_tx_amp_changed((bool) rf_amp); - }; - - button_start.on_select = [this](Button&){ - if (transmitting_) { - if (on_stop) - on_stop(); - } else { - if (on_start) - on_start(); - } - }; + const Coord y, + const uint64_t frequency_step, + const uint32_t channel_bandwidth, + const bool lock) + : lock_{lock} { + set_parent_rect({0, y, 30 * 8, 6 * 8}); + + add_children({ + &field_frequency, + &field_frequency_step, + &text_gain, + &field_gain, + &button_start, + &text_amp, + &field_amp, + }); + + set_transmitting(false); + + if (lock_) { + field_frequency.set_focusable(false); + field_frequency.set_style(&style_locked); + } else { + if (channel_bandwidth) { + add_children({&text_bw, + &field_bw}); + + field_bw.on_change = [this](int32_t v) { + on_channel_bandwidth_changed(v * 1000); + }; + field_bw.set_value(channel_bandwidth); + } + } + + // field_frequency.set_value(transmitter_model.tuning_frequency()); + field_frequency.set_step(frequency_step); + field_frequency.on_change = [this](rf::Frequency f) { + on_tuning_frequency_changed(f); + }; + field_frequency.on_edit = [this]() { + if (on_edit_frequency) + on_edit_frequency(); + }; + + field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { + this->field_frequency.set_step(v); + }; + + field_gain.on_change = [this](uint32_t tx_gain) { + on_tx_gain_changed(tx_gain); + }; + + field_amp.on_change = [this](uint32_t rf_amp) { + on_tx_amp_changed((bool)rf_amp); + }; + + button_start.on_select = [this](Button&) { + if (transmitting_) { + if (on_stop) + on_stop(); + } else { + if (on_start) + on_start(); + } + }; } TransmitterView::~TransmitterView() { - audio::output::stop(); - transmitter_model.disable(); - baseband::shutdown(); + audio::output::stop(); + transmitter_model.disable(); + baseband::shutdown(); } /* TransmitterView2 *******************************************************/ @@ -196,82 +195,81 @@ TransmitterView::~TransmitterView() { // We use short compact char lines , in Replay / GPS Simul / Playlist App , called (x pos , y pos,SHORT_UI ) void TransmitterView2::paint(Painter& painter) { -// Not using TransmitterView2, but if we delete it,we got , top line 1 a blanking rect. - (void) painter; // Avoid warning: unused parameter . + // Not using TransmitterView2, but if we delete it,we got , top line 1 a blanking rect. + (void)painter; // Avoid warning: unused parameter . } void TransmitterView2::on_tx_gain_changed(int32_t tx_gain) { - transmitter_model.set_tx_gain(tx_gain); - update_gainlevel_styles(); + transmitter_model.set_tx_gain(tx_gain); + update_gainlevel_styles(); } void TransmitterView2::on_tx_amp_changed(bool rf_amp) { - transmitter_model.set_rf_amp(rf_amp); - update_gainlevel_styles(); + transmitter_model.set_rf_amp(rf_amp); + update_gainlevel_styles(); } void TransmitterView2::update_gainlevel_styles() { - const Style *new_style_ptr = NULL; - int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0); - - if(tot_gain > POWER_THRESHOLD_HIGH) { - new_style_ptr = &style_power_high; - } else if(tot_gain > POWER_THRESHOLD_MED) { - new_style_ptr = &style_power_med; - } else if(tot_gain > POWER_THRESHOLD_LOW) { - new_style_ptr = &style_power_low; - } - - field_gain.set_style(new_style_ptr); - text_gain_amp.set_style(new_style_ptr); - field_amp.set_style(new_style_ptr); - - field_gain_short_UI.set_style(new_style_ptr); - text_gain_amp_short_UI.set_style(new_style_ptr); - field_amp_short_UI.set_style(new_style_ptr); + const Style* new_style_ptr = NULL; + int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0); + + if (tot_gain > POWER_THRESHOLD_HIGH) { + new_style_ptr = &style_power_high; + } else if (tot_gain > POWER_THRESHOLD_MED) { + new_style_ptr = &style_power_med; + } else if (tot_gain > POWER_THRESHOLD_LOW) { + new_style_ptr = &style_power_low; + } + + field_gain.set_style(new_style_ptr); + text_gain_amp.set_style(new_style_ptr); + field_amp.set_style(new_style_ptr); + + field_gain_short_UI.set_style(new_style_ptr); + text_gain_amp_short_UI.set_style(new_style_ptr); + field_amp_short_UI.set_style(new_style_ptr); } void TransmitterView2::on_show() { - field_gain.set_value(transmitter_model.tx_gain()); - field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0); + field_gain.set_value(transmitter_model.tx_gain()); + field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0); - field_gain_short_UI.set_value(transmitter_model.tx_gain()); - field_amp_short_UI.set_value(transmitter_model.rf_amp() ? 14 : 0); - - update_gainlevel_styles(); + field_gain_short_UI.set_value(transmitter_model.tx_gain()); + field_amp_short_UI.set_value(transmitter_model.rf_amp() ? 14 : 0); + + update_gainlevel_styles(); } -TransmitterView2::TransmitterView2( const Coord x, const Coord y, bool short_UI) -{ - set_parent_rect({ x , y, 20 * 8, 1 * 8 }); // set_parent_rect({ 0, y, 30 * 8, 6 * 8 }); - - add_children({ - &(short_UI ? text_gain_amp_short_UI : text_gain_amp), - &(short_UI ? field_gain_short_UI : field_gain), - &(short_UI ? field_amp_short_UI : field_amp), - }); - - if (short_UI) { - field_gain_short_UI.on_change = [this](uint32_t tx_gain) { - on_tx_gain_changed(tx_gain); - }; - field_amp_short_UI.on_change = [this](uint32_t rf_amp) { - on_tx_amp_changed((bool) rf_amp); - }; - } else { - field_gain.on_change = [this](uint32_t tx_gain) { - on_tx_gain_changed(tx_gain); - }; - field_amp.on_change = [this](uint32_t rf_amp) { - on_tx_amp_changed((bool) rf_amp); - }; - } +TransmitterView2::TransmitterView2(const Coord x, const Coord y, bool short_UI) { + set_parent_rect({x, y, 20 * 8, 1 * 8}); // set_parent_rect({ 0, y, 30 * 8, 6 * 8 }); + + add_children({ + &(short_UI ? text_gain_amp_short_UI : text_gain_amp), + &(short_UI ? field_gain_short_UI : field_gain), + &(short_UI ? field_amp_short_UI : field_amp), + }); + + if (short_UI) { + field_gain_short_UI.on_change = [this](uint32_t tx_gain) { + on_tx_gain_changed(tx_gain); + }; + field_amp_short_UI.on_change = [this](uint32_t rf_amp) { + on_tx_amp_changed((bool)rf_amp); + }; + } else { + field_gain.on_change = [this](uint32_t tx_gain) { + on_tx_gain_changed(tx_gain); + }; + field_amp.on_change = [this](uint32_t rf_amp) { + on_tx_amp_changed((bool)rf_amp); + }; + } } TransmitterView2::~TransmitterView2() { - audio::output::stop(); - transmitter_model.disable(); - baseband::shutdown(); + audio::output::stop(); + transmitter_model.disable(); + baseband::shutdown(); } } /* namespace ui */ diff --git a/firmware/application/ui/ui_transmitter.hpp b/firmware/application/ui/ui_transmitter.hpp index 43160c2a6..9d4a80550 100644 --- a/firmware/application/ui/ui_transmitter.hpp +++ b/firmware/application/ui/ui_transmitter.hpp @@ -37,212 +37,195 @@ #include #include -#define POWER_THRESHOLD_HIGH 47 -#define POWER_THRESHOLD_MED 38 -#define POWER_THRESHOLD_LOW 17 +#define POWER_THRESHOLD_HIGH 47 +#define POWER_THRESHOLD_MED 38 +#define POWER_THRESHOLD_LOW 17 namespace ui { class TXGainField : public NumberField { -public: - std::function on_show_options { }; + public: + std::function on_show_options{}; - TXGainField(Point parent_pos); + TXGainField(Point parent_pos); }; class TransmitterView : public View { -public: - std::function on_edit_frequency { }; - std::function on_start { }; - std::function on_stop { }; - - TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock); - TransmitterView( - const Coord y, const uint32_t frequency_step, const uint32_t channel_bandwidth - ) : TransmitterView { y, frequency_step, channel_bandwidth, false } - { - } - - ~TransmitterView(); - - void on_show() override; - void paint(Painter& painter) override; - void focus() override; - - void set_transmitting(const bool transmitting); - -private: - const Style style_start { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - const Style style_stop { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - const Style style_locked { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::dark_grey(), - }; - const Style style_power_low { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::yellow(), - }; - const Style style_power_med { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::orange(), - }; - const Style style_power_high { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - bool lock_ { false }; - bool transmitting_ { false }; - - FrequencyField field_frequency { - { 0, 1 * 8 } - }; - - Text text_gain { - { 0, 3 * 8, 5 * 8, 1 * 16 }, - "Gain:" - }; - - NumberField field_gain { - { 5 * 8, 3 * 8 }, - 2, - { max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum }, - max2837::tx::gain_db_step, - ' ' - }; - - Text text_bw { - { 18 * 8, 1 * 8, 3 * 8, 1 * 16 }, - "kHz" - }; - NumberField field_bw { - { 15 * 8, 1 * 8 }, - 3, - { 1, 150 }, - 1, - ' ' - }; - - Text text_amp { - { 11 * 8, 3 * 8, 5 * 8, 1 * 16 }, - "Amp:" - }; - - NumberField field_amp { - { 16 * 8, 3 * 8 }, - 2, - { 0, 14 }, - 14, - ' ' - }; - - Button button_start { - { 21 * 8, 1 * 8, 9 * 8, 32 }, - "START" - }; - - FrequencyStepView field_frequency_step { - { 10 * 8 - 4, 1 * 8 }, - }; - - void on_tuning_frequency_changed(rf::Frequency f); - void on_channel_bandwidth_changed(uint32_t channel_bandwidth); - void on_tx_gain_changed(int32_t tx_gain); - void on_tx_amp_changed(bool rf_amp); - - void update_gainlevel_styles(void); + public: + std::function on_edit_frequency{}; + std::function on_start{}; + std::function on_stop{}; + + TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock); + TransmitterView( + const Coord y, + const uint32_t frequency_step, + const uint32_t channel_bandwidth) + : TransmitterView{y, frequency_step, channel_bandwidth, false} { + } + + ~TransmitterView(); + + void on_show() override; + void paint(Painter& painter) override; + void focus() override; + + void set_transmitting(const bool transmitting); + + private: + const Style style_start{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + const Style style_stop{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + const Style style_locked{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::dark_grey(), + }; + const Style style_power_low{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::yellow(), + }; + const Style style_power_med{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::orange(), + }; + const Style style_power_high{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + bool lock_{false}; + bool transmitting_{false}; + + FrequencyField field_frequency{ + {0, 1 * 8}}; + + Text text_gain{ + {0, 3 * 8, 5 * 8, 1 * 16}, + "Gain:"}; + + NumberField field_gain{ + {5 * 8, 3 * 8}, + 2, + {max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum}, + max2837::tx::gain_db_step, + ' '}; + + Text text_bw{ + {18 * 8, 1 * 8, 3 * 8, 1 * 16}, + "kHz"}; + NumberField field_bw{ + {15 * 8, 1 * 8}, + 3, + {1, 150}, + 1, + ' '}; + + Text text_amp{ + {11 * 8, 3 * 8, 5 * 8, 1 * 16}, + "Amp:"}; + + NumberField field_amp{ + {16 * 8, 3 * 8}, + 2, + {0, 14}, + 14, + ' '}; + + Button button_start{ + {21 * 8, 1 * 8, 9 * 8, 32}, + "START"}; + + FrequencyStepView field_frequency_step{ + {10 * 8 - 4, 1 * 8}, + }; + + void on_tuning_frequency_changed(rf::Frequency f); + void on_channel_bandwidth_changed(uint32_t channel_bandwidth); + void on_tx_gain_changed(int32_t tx_gain); + void on_tx_amp_changed(bool rf_amp); + + void update_gainlevel_styles(void); }; - class TransmitterView2 : public View { -public: - - TransmitterView2(const Coord x, const Coord y, bool short_UI); - - ~TransmitterView2(); - - void on_show() override; - void paint(Painter& painter) override; - -private: - const Style style_power_low { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::yellow(), - }; - const Style style_power_med { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::orange(), - }; - const Style style_power_high { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - - Text text_gain_amp { - { 0, 3 * 8, 5 * 8, 1 * 16 }, - "Gain: Amp:" - }; - - NumberField field_gain { - { 5 * 8, 3 * 8 }, - 2, - { max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum }, - max2837::tx::gain_db_step, - ' ' - }; - - NumberField field_amp { - { 12 * 8, 3 * 8 }, - 2, - { 0, 14 }, - 14, - ' ' - }; - - Text text_gain_amp_short_UI { - { 0, (3 * 8)-1, 5 * 8, 1 * 16 }, - "Gain A:" - }; - - NumberField field_gain_short_UI { - { (4 * 8)+2 , 3 * 8 }, - 2, - { max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum }, - max2837::tx::gain_db_step, - ' ' - }; - - NumberField field_amp_short_UI { - { (9 * 8)-2, 3 * 8 }, - 2, - { 0, 14 }, - 14, - ' ' - }; - - void on_tx_gain_changed(int32_t tx_gain); - void on_tx_amp_changed(bool rf_amp); - - void update_gainlevel_styles(void); + public: + TransmitterView2(const Coord x, const Coord y, bool short_UI); + + ~TransmitterView2(); + + void on_show() override; + void paint(Painter& painter) override; + + private: + const Style style_power_low{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::yellow(), + }; + const Style style_power_med{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::orange(), + }; + const Style style_power_high{ + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + + Text text_gain_amp{ + {0, 3 * 8, 5 * 8, 1 * 16}, + "Gain: Amp:"}; + + NumberField field_gain{ + {5 * 8, 3 * 8}, + 2, + {max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum}, + max2837::tx::gain_db_step, + ' '}; + + NumberField field_amp{ + {12 * 8, 3 * 8}, + 2, + {0, 14}, + 14, + ' '}; + + Text text_gain_amp_short_UI{ + {0, (3 * 8) - 1, 5 * 8, 1 * 16}, + "Gain A:"}; + + NumberField field_gain_short_UI{ + {(4 * 8) + 2, 3 * 8}, + 2, + {max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum}, + max2837::tx::gain_db_step, + ' '}; + + NumberField field_amp_short_UI{ + {(9 * 8) - 2, 3 * 8}, + 2, + {0, 14}, + 14, + ' '}; + + void on_tx_gain_changed(int32_t tx_gain); + void on_tx_amp_changed(bool rf_amp); + + void update_gainlevel_styles(void); }; - - } /* namespace ui */ -#endif/*__UI_TRANSMITTER_H__*/ +#endif /*__UI_TRANSMITTER_H__*/ diff --git a/firmware/application/ui/ui_tv.cpp b/firmware/application/ui/ui_tv.cpp index 5c3f0fbcc..cec989bfa 100644 --- a/firmware/application/ui/ui_tv.cpp +++ b/firmware/application/ui/ui_tv.cpp @@ -40,211 +40,198 @@ namespace tv { /* TimeScopeView******************************************************/ TimeScopeView::TimeScopeView( - const Rect parent_rect -) : View { parent_rect } -{ - set_focusable(true); - - add_children({ - //&labels, - //&field_frequency, - &waveform - }); - - /*field_frequency.on_change = [this](int32_t) { - set_dirty(); - }; - field_frequency.set_value(10);*/ + const Rect parent_rect) + : View{parent_rect} { + set_focusable(true); + + add_children({//&labels, + //&field_frequency, + &waveform}); + + /*field_frequency.on_change = [this](int32_t) { + set_dirty(); + }; + field_frequency.set_value(10);*/ } void TimeScopeView::paint(Painter& painter) { - const auto r = screen_rect(); - - painter.fill_rectangle(r, Color::black()); - - // Cursor - /* - const Rect r_cursor { - field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, - 1, cursor_band_height - }; - painter.fill_rectangle( - r_cursor, - Color::red() - );*/ + const auto r = screen_rect(); + + painter.fill_rectangle(r, Color::black()); + + // Cursor + /* + const Rect r_cursor { + field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, + 1, cursor_band_height + }; + painter.fill_rectangle( + r_cursor, + Color::red() + );*/ } void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) { - for (size_t i = 0; i < spectrum->db.size(); i++) - audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; - waveform.set_dirty(); + for (size_t i = 0; i < spectrum->db.size(); i++) + audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; + waveform.set_dirty(); } /* TVView *********************************************************/ void TVView::on_show() { - clear(); + clear(); - const auto screen_r = screen_rect(); - display.scroll_set_area(screen_r.top(), screen_r.bottom()); + const auto screen_r = screen_rect(); + display.scroll_set_area(screen_r.top(), screen_r.bottom()); } void TVView::on_hide() { - /* TODO: Clear region to eliminate brief flash of content at un-shifted - * position? - */ - display.scroll_disable(); + /* TODO: Clear region to eliminate brief flash of content at un-shifted + * position? + */ + display.scroll_disable(); } void TVView::paint(Painter& painter) { - // Do nothing. - (void)painter; + // Do nothing. + (void)painter; } -void TVView::on_adjust_xcorr(uint8_t xcorr){ - x_correction = xcorr; +void TVView::on_adjust_xcorr(uint8_t xcorr) { + x_correction = xcorr; } void TVView::on_channel_spectrum( - const ChannelSpectrum& spectrum -) { - //portapack has limitations - // 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256) - // 3.memory size (for ui::Color, the buffer size - //spectrum.db[i] is 256 long - //768x625 ->128x625 ->128x312 -> 128x104 - //originally @6MHz sample rate, the PAL should be 768x625 - //I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2) - //the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line - //However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer. - //So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame. - //then the resolution is changed to 128x312 - //128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large - //according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted. - //So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height. - //I was expecting to see 1/3 height of original video. - - //Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128 - //Each call back gives me 256 samples which is exactly 2 lines. What a coincidence! - - //After some experiment, I did some improvements. - //1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window. - //So I made the height twice simply by painting 2 identical lines in the place of original lines - //2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually - //3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value. - //I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically - //however this is useless until now. - - for(size_t i=0; i<256; i++) - { - //video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]]; - video_buffer_int[i+count*256] = 255 - spectrum.db[i]; - } - count = count + 1; - if (count == 52 -1) - { - ui::Color line_buffer[128]; - Coord line; - uint32_t bmp_px; - - /*for (line = 0; line < 104; line++) - { - for (bmp_px = 0; bmp_px < 128; bmp_px++) - { - //line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; - line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]]; - } - - display.render_line({ 0, line + 100 }, 128, line_buffer); - }*/ - for (line = 0; line < 208; line=line+2) - { - for (bmp_px = 0; bmp_px < 128; bmp_px++) - { - //line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; - line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]]; - } - - display.render_line({ 0, line + 100 }, 128, line_buffer); - display.render_line({ 0, line + 101 }, 128, line_buffer); - } - count = 0; - } - + const ChannelSpectrum& spectrum) { + // portapack has limitations + // 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256) + // 3.memory size (for ui::Color, the buffer size + // spectrum.db[i] is 256 long + // 768x625 ->128x625 ->128x312 -> 128x104 + // originally @6MHz sample rate, the PAL should be 768x625 + // I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2) + // the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line + // However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer. + // So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame. + // then the resolution is changed to 128x312 + // 128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large + // according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted. + // So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height. + // I was expecting to see 1/3 height of original video. + + // Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128 + // Each call back gives me 256 samples which is exactly 2 lines. What a coincidence! + + // After some experiment, I did some improvements. + // 1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window. + // So I made the height twice simply by painting 2 identical lines in the place of original lines + // 2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually + // 3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value. + // I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically + // however this is useless until now. + + for (size_t i = 0; i < 256; i++) { + // video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]]; + video_buffer_int[i + count * 256] = 255 - spectrum.db[i]; + } + count = count + 1; + if (count == 52 - 1) { + ui::Color line_buffer[128]; + Coord line; + uint32_t bmp_px; + + /*for (line = 0; line < 104; line++) + { + for (bmp_px = 0; bmp_px < 128; bmp_px++) + { + //line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; + line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]]; + } + + display.render_line({ 0, line + 100 }, 128, line_buffer); + }*/ + for (line = 0; line < 208; line = line + 2) { + for (bmp_px = 0; bmp_px < 128; bmp_px++) { + // line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; + line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px + line / 2 * 128 + x_correction]]; + } + + display.render_line({0, line + 100}, 128, line_buffer); + display.render_line({0, line + 101}, 128, line_buffer); + } + count = 0; + } } void TVView::clear() { - display.fill_rectangle( - screen_rect(), - Color::black() - ); + display.fill_rectangle( + screen_rect(), + Color::black()); } /* TVWidget *******************************************************/ TVWidget::TVWidget() { - add_children({ - &tv_view, - &field_xcorr - }); - field_xcorr.set_value(10); + add_children({&tv_view, + &field_xcorr}); + field_xcorr.set_value(10); } void TVWidget::on_show() { - baseband::spectrum_streaming_start(); + baseband::spectrum_streaming_start(); } void TVWidget::on_hide() { - baseband::spectrum_streaming_stop(); + baseband::spectrum_streaming_stop(); } void TVWidget::show_audio_spectrum_view(const bool show) { - if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return; - - if (show) { - audio_spectrum_view = std::make_unique(audio_spectrum_view_rect); - add_child(audio_spectrum_view.get()); - update_widgets_rect(); - } else { - audio_spectrum_update = false; - remove_child(audio_spectrum_view.get()); - audio_spectrum_view.reset(); - update_widgets_rect(); - } + if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return; + + if (show) { + audio_spectrum_view = std::make_unique(audio_spectrum_view_rect); + add_child(audio_spectrum_view.get()); + update_widgets_rect(); + } else { + audio_spectrum_update = false; + remove_child(audio_spectrum_view.get()); + audio_spectrum_view.reset(); + update_widgets_rect(); + } } void TVWidget::update_widgets_rect() { - if (audio_spectrum_view) { - tv_view.set_parent_rect(tv_reduced_rect); - } else { - tv_view.set_parent_rect(tv_normal_rect); - } - tv_view.on_show(); + if (audio_spectrum_view) { + tv_view.set_parent_rect(tv_reduced_rect); + } else { + tv_view.set_parent_rect(tv_normal_rect); + } + tv_view.on_show(); } void TVWidget::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); - - tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; - tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height }; - - update_widgets_rect(); + View::set_parent_rect(new_parent_rect); + + tv_normal_rect = {0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; + tv_reduced_rect = {0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height}; + + update_widgets_rect(); } void TVWidget::paint(Painter& painter) { - // TODO: - (void)painter; + // TODO: + (void)painter; } void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { - tv_view.on_channel_spectrum(spectrum); - tv_view.on_adjust_xcorr(field_xcorr.value()); - sampling_rate = spectrum.sampling_rate; - + tv_view.on_channel_spectrum(spectrum); + tv_view.on_adjust_xcorr(field_xcorr.value()); + sampling_rate = spectrum.sampling_rate; } void TVWidget::on_audio_spectrum() { - audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); + audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); } } /* namespace tv */ diff --git a/firmware/application/ui/ui_tv.hpp b/firmware/application/ui/ui_tv.hpp index 7accf7d45..2fb9bf608 100644 --- a/firmware/application/ui/ui_tv.hpp +++ b/firmware/application/ui/ui_tv.hpp @@ -37,140 +37,135 @@ namespace ui { namespace tv { class TimeScopeView : public View { -public: - TimeScopeView(const Rect parent_rect); - - void paint(Painter& painter) override; - - void on_audio_spectrum(const AudioSpectrum* spectrum); - -private: - static constexpr int cursor_band_height = 4; - - int16_t audio_spectrum[128] { 0 }; - - /*Labels labels { - { { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() } - };*/ - /* - NumberField field_frequency { - { 0 * 8, 0 * 16 }, - 5, - { 0, 48000 }, - 48000 / 240, - ' ' - };*/ - - Waveform waveform { - { 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 }, - audio_spectrum, - 128, - 0, - false, - Color::white() - }; + public: + TimeScopeView(const Rect parent_rect); + + void paint(Painter& painter) override; + + void on_audio_spectrum(const AudioSpectrum* spectrum); + + private: + static constexpr int cursor_band_height = 4; + + int16_t audio_spectrum[128]{0}; + + /*Labels labels { + { { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() } + };*/ + /* + NumberField field_frequency { + { 0 * 8, 0 * 16 }, + 5, + { 0, 48000 }, + 48000 / 240, + ' ' + };*/ + + Waveform waveform{ + {0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16}, + audio_spectrum, + 128, + 0, + false, + Color::white()}; }; class TVView : public Widget { -public: - void on_show() override; - void on_hide() override; - - void paint(Painter& painter) override; - void on_channel_spectrum(const ChannelSpectrum& spectrum); - void on_adjust_xcorr(uint8_t xcorr); - //ui::Color video_buffer[13312]; - uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction - uint32_t count=0; - uint8_t x_correction=0; -private: - void clear(); - + public: + void on_show() override; + void on_hide() override; + + void paint(Painter& painter) override; + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_adjust_xcorr(uint8_t xcorr); + // ui::Color video_buffer[13312]; + uint8_t video_buffer_int[13312 + 128]{0}; // 128 is for the over length caused by x_correction + uint32_t count = 0; + uint8_t x_correction = 0; + + private: + void clear(); }; class TVWidget : public View { -public: - std::function on_select { }; - - TVWidget(); - - TVWidget(const TVWidget&) = delete; - TVWidget(TVWidget&&) = delete; - TVWidget& operator=(const TVWidget&) = delete; - TVWidget& operator=(TVWidget&&) = delete; - - void on_show() override; - void on_hide() override; - - void set_parent_rect(const Rect new_parent_rect) override; - - void show_audio_spectrum_view(const bool show); - - void paint(Painter& painter) override; - NumberField field_xcorr { - { 0 * 8, 0 * 16 }, - 5, - { 0, 128 }, - 1, - ' ' - }; - -private: - void update_widgets_rect(); - - const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 }; - static constexpr Dim audio_spectrum_height = 16 * 2 + 20; - static constexpr Dim scale_height = 20; - - TVView tv_view { }; - - ChannelSpectrumFIFO* channel_fifo { nullptr }; - AudioSpectrum* audio_spectrum_data { nullptr }; - bool audio_spectrum_update { false }; - - std::unique_ptr audio_spectrum_view { }; - - int sampling_rate { 0 }; - int32_t cursor_position { 0 }; - ui::Rect tv_normal_rect { }; - ui::Rect tv_reduced_rect { }; - - MessageHandlerRegistration message_handler_channel_spectrum_config { - Message::ID::ChannelSpectrumConfig, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->channel_fifo = message.fifo; - } - }; - MessageHandlerRegistration message_handler_audio_spectrum { - Message::ID::AudioSpectrum, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->audio_spectrum_data = message.data; - this->audio_spectrum_update = true; - } - }; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->channel_fifo ) { - ChannelSpectrum channel_spectrum; - while( channel_fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - if (this->audio_spectrum_update) { - this->audio_spectrum_update = false; - this->on_audio_spectrum(); - } - } - }; - - void on_channel_spectrum(const ChannelSpectrum& spectrum); - void on_audio_spectrum(); + public: + std::function on_select{}; + + TVWidget(); + + TVWidget(const TVWidget&) = delete; + TVWidget(TVWidget&&) = delete; + TVWidget& operator=(const TVWidget&) = delete; + TVWidget& operator=(TVWidget&&) = delete; + + void on_show() override; + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void show_audio_spectrum_view(const bool show); + + void paint(Painter& painter) override; + NumberField field_xcorr{ + {0 * 8, 0 * 16}, + 5, + {0, 128}, + 1, + ' '}; + + private: + void update_widgets_rect(); + + const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20}; + static constexpr Dim audio_spectrum_height = 16 * 2 + 20; + static constexpr Dim scale_height = 20; + + TVView tv_view{}; + + ChannelSpectrumFIFO* channel_fifo{nullptr}; + AudioSpectrum* audio_spectrum_data{nullptr}; + bool audio_spectrum_update{false}; + + std::unique_ptr audio_spectrum_view{}; + + int sampling_rate{0}; + int32_t cursor_position{0}; + ui::Rect tv_normal_rect{}; + ui::Rect tv_reduced_rect{}; + + MessageHandlerRegistration message_handler_channel_spectrum_config{ + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->channel_fifo = message.fifo; + }}; + MessageHandlerRegistration message_handler_audio_spectrum{ + Message::ID::AudioSpectrum, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->audio_spectrum_data = message.data; + this->audio_spectrum_update = true; + }}; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if (this->channel_fifo) { + ChannelSpectrum channel_spectrum; + while (channel_fifo->out(channel_spectrum)) { + this->on_channel_spectrum(channel_spectrum); + } + } + if (this->audio_spectrum_update) { + this->audio_spectrum_update = false; + this->on_audio_spectrum(); + } + }}; + + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_audio_spectrum(); }; } /* namespace tv */ } /* namespace ui */ -#endif/*__UI_TV_H__*/ +#endif /*__UI_TV_H__*/ diff --git a/firmware/application/ui_baseband_stats_view.cpp b/firmware/application/ui_baseband_stats_view.cpp index b6a9525f2..572aa84b2 100644 --- a/firmware/application/ui_baseband_stats_view.cpp +++ b/firmware/application/ui_baseband_stats_view.cpp @@ -34,29 +34,25 @@ namespace ui { /* BasebandStatsView *****************************************************/ BasebandStatsView::BasebandStatsView() { - add_children({ - &text_stats, - }); + add_children({ + &text_stats, + }); } static std::string ticks_to_percent_string(const uint32_t ticks) { - constexpr size_t decimal_digits = 1; - constexpr size_t decimal_factor = decimal_digits * 10; - - const uint32_t percent_x10 = ticks / (base_m4_clk_f / (100 * decimal_factor)); - const uint32_t percent_x10_clipped = std::min(percent_x10, static_cast(100 * decimal_factor) - 1); - return - to_string_dec_uint(percent_x10_clipped / decimal_factor, 2) + "." + - to_string_dec_uint(percent_x10_clipped % decimal_factor, decimal_digits, '0'); + constexpr size_t decimal_digits = 1; + constexpr size_t decimal_factor = decimal_digits * 10; + + const uint32_t percent_x10 = ticks / (base_m4_clk_f / (100 * decimal_factor)); + const uint32_t percent_x10_clipped = std::min(percent_x10, static_cast(100 * decimal_factor) - 1); + return to_string_dec_uint(percent_x10_clipped / decimal_factor, 2) + "." + + to_string_dec_uint(percent_x10_clipped % decimal_factor, decimal_digits, '0'); } void BasebandStatsView::on_statistics_update(const BasebandStatistics& statistics) { - std::string message = ticks_to_percent_string(statistics.idle_ticks) - + " " + ticks_to_percent_string(statistics.main_ticks) - + " " + ticks_to_percent_string(statistics.rssi_ticks) - + " " + ticks_to_percent_string(statistics.baseband_ticks); + std::string message = ticks_to_percent_string(statistics.idle_ticks) + " " + ticks_to_percent_string(statistics.main_ticks) + " " + ticks_to_percent_string(statistics.rssi_ticks) + " " + ticks_to_percent_string(statistics.baseband_ticks); - text_stats.set(message); + text_stats.set(message); } } /* namespace ui */ diff --git a/firmware/application/ui_baseband_stats_view.hpp b/firmware/application/ui_baseband_stats_view.hpp index 2c07c20b6..ca8721f3f 100644 --- a/firmware/application/ui_baseband_stats_view.hpp +++ b/firmware/application/ui_baseband_stats_view.hpp @@ -31,25 +31,24 @@ namespace ui { class BasebandStatsView : public View { -public: - BasebandStatsView(); - -private: - Text text_stats { - { 0 * 8, 0, (4 * 4 + 3) * 8, 1 * 16 }, - "", - }; - - MessageHandlerRegistration message_handler_stats { - Message::ID::BasebandStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - - void on_statistics_update(const BasebandStatistics& statistics); + public: + BasebandStatsView(); + + private: + Text text_stats{ + {0 * 8, 0, (4 * 4 + 3) * 8, 1 * 16}, + "", + }; + + MessageHandlerRegistration message_handler_stats{ + Message::ID::BasebandStatistics, + [this](const Message* const p) { + this->on_statistics_update(static_cast(p)->statistics); + }}; + + void on_statistics_update(const BasebandStatistics& statistics); }; } /* namespace ui */ -#endif/*__UI_BASEBAND_STATS_VIEW_H__*/ +#endif /*__UI_BASEBAND_STATS_VIEW_H__*/ diff --git a/firmware/application/ui_handwrite.cpp b/firmware/application/ui_handwrite.cpp index 7e2e8fff1..4ef103bda 100644 --- a/firmware/application/ui_handwrite.cpp +++ b/firmware/application/ui_handwrite.cpp @@ -33,327 +33,312 @@ using namespace portapack; namespace ui { void HandWriteView::paint(Painter& painter) { - _painter = &painter; + _painter = &painter; } HandWriteView::HandWriteView( - NavigationView& nav, - std::string * str, - size_t max_length -) : TextEntryView(nav, str, max_length) -{ - size_t n; - - // Handwriting alphabet definition here - handwriting = &handwriting_unistroke; - - add_children({ - &button_case - }); - - const auto button_fn = [this](Button& button) { - this->on_button(button); - }; - - n = 0; - for (auto& button : num_buttons) { - add_child(&button); - button.on_select = button_fn; - button.set_parent_rect({ - static_cast(n * 24), - static_cast(236), - 24, 28 - }); - const std::string label { - (char)(n + '0') - }; - button.set_text(label); - button.id = n + '0'; - n++; - } - - n = 0; - for (auto& button : special_buttons) { - add_child(&button); - button.on_select = button_fn; - button.set_parent_rect({ - static_cast(50 + n * 24), - static_cast(270), - 24, 28 - }); - const std::string label { - (char)(special_chars[n]) - }; - button.set_text(label); - button.id = special_chars[n]; - n++; - } - - button_case.on_select = [this, &nav](Button&) { - if (_lowercase == true) { - _lowercase = false; - button_case.set_text("LC"); - } else { - _lowercase = true; - button_case.set_text("UC"); - } - }; - - button_ok.on_select = [this, &nav](Button&) { - if (on_changed) - on_changed(_str); - nav.pop(); - }; - - update_text(); + NavigationView& nav, + std::string* str, + size_t max_length) + : TextEntryView(nav, str, max_length) { + size_t n; + + // Handwriting alphabet definition here + handwriting = &handwriting_unistroke; + + add_children({&button_case}); + + const auto button_fn = [this](Button& button) { + this->on_button(button); + }; + + n = 0; + for (auto& button : num_buttons) { + add_child(&button); + button.on_select = button_fn; + button.set_parent_rect({static_cast(n * 24), + static_cast(236), + 24, 28}); + const std::string label{ + (char)(n + '0')}; + button.set_text(label); + button.id = n + '0'; + n++; + } + + n = 0; + for (auto& button : special_buttons) { + add_child(&button); + button.on_select = button_fn; + button.set_parent_rect({static_cast(50 + n * 24), + static_cast(270), + 24, 28}); + const std::string label{ + (char)(special_chars[n])}; + button.set_text(label); + button.id = special_chars[n]; + n++; + } + + button_case.on_select = [this, &nav](Button&) { + if (_lowercase == true) { + _lowercase = false; + button_case.set_text("LC"); + } else { + _lowercase = true; + button_case.set_text("UC"); + } + }; + + button_ok.on_select = [this, &nav](Button&) { + if (on_changed) + on_changed(_str); + nav.pop(); + }; + + update_text(); } bool HandWriteView::on_touch(const TouchEvent event) { - if (event.type == ui::TouchEvent::Type::Start) { - stroke_index = 0; - move_wait = 3; - tracing = true; - } - if (event.type == ui::TouchEvent::Type::End) { - tracing = false; - guess_letter(); - } - if (event.type == ui::TouchEvent::Type::Move) { - if (tracing) - current_pos = event.point; - } - return true; + if (event.type == ui::TouchEvent::Type::Start) { + stroke_index = 0; + move_wait = 3; + tracing = true; + } + if (event.type == ui::TouchEvent::Type::End) { + tracing = false; + guess_letter(); + } + if (event.type == ui::TouchEvent::Type::Move) { + if (tracing) + current_pos = event.point; + } + return true; } void HandWriteView::clear_zone(const Color color, const bool flash) { - display.fill_rectangle( - {{0, 32}, {240, 216}}, - color - ); - if (flash) { - flash_timer = 8; - } else { - // Draw grid - _painter->draw_rectangle( - {{0, 32}, {80, 216}}, - Color::grey() - ); - _painter->draw_rectangle( - {{80, 32}, {80, 216}}, - Color::grey() - ); - _painter->draw_rectangle( - {{160, 32}, {80, 216}}, - Color::grey() - ); - _painter->draw_rectangle( - {{0, 104}, {240, 72}}, - Color::grey() - ); - } + display.fill_rectangle( + {{0, 32}, {240, 216}}, + color); + if (flash) { + flash_timer = 8; + } else { + // Draw grid + _painter->draw_rectangle( + {{0, 32}, {80, 216}}, + Color::grey()); + _painter->draw_rectangle( + {{80, 32}, {80, 216}}, + Color::grey()); + _painter->draw_rectangle( + {{160, 32}, {80, 216}}, + Color::grey()); + _painter->draw_rectangle( + {{0, 104}, {240, 72}}, + Color::grey()); + } } void HandWriteView::guess_letter() { - uint32_t symbol, match, count, stroke_idx, stroke_data; - Condition cond; - Direction dir; - bool matched; - - // Letter guessing - if (stroke_index) { - for (symbol = 0; symbol < handwriting->letter_count; symbol++) { - count = handwriting->letter[symbol].count; - matched = false; - if (count) { - // We have a count match to do - if ((count == 1) && (stroke_index == 1)) matched = true; - if ((count == 2) && (stroke_index == 2)) matched = true; - if ((count == 3) && (stroke_index > 2)) matched = true; - } else { - matched = true; - } - if (matched) { - for (match = 0; match < 3; match++) { - cond = handwriting->letter[symbol].match[match].cond; - dir = handwriting->letter[symbol].match[match].dir; - if ((cond != cond_empty) && (dir != dir_empty)) { - if (cond == last) { - if (stroke_index) - stroke_idx = stroke_index - 1; - else - stroke_idx = 0; - } else if (cond == stroke_a) - stroke_idx = 0; - else if (cond == stroke_b) - stroke_idx = 1; - else if (cond == stroke_c) - stroke_idx = 2; - else - stroke_idx = 3; - if (stroke_idx >= stroke_index) break; - stroke_data = stroke_list[stroke_idx]; - if ((dir & 0xF0) == 0xF0) { - if ((dir & 0x0F) != (stroke_data & 0x0F)) break; - } else if ((dir & 0x0F) == 0x0F) { - if ((dir & 0xF0) != (stroke_data & 0xF0)) break; - } else { - if (dir != (int32_t)stroke_data) break; - } - } - } - if (match == 3) - break; - else - matched = false; - } - } - if (matched) { - if (symbol) { - if (_lowercase) - char_add('a' + symbol - 1); - else - char_add('A' + symbol - 1); - clear_zone(Color::green(), true); // Green flash - } else { - if (_cursor_pos) { - char_delete(); - clear_zone(Color::yellow(), true); // Yellow flash - } else { - clear_zone(Color::red(), true); // Red flash - } - } - } else { - clear_zone(Color::red(), true); // Red flash - } - } else { - // Short tap is space - char_add(' '); - clear_zone(Color::green(), true); // Green flash - } - update_text(); - stroke_index = 0; + uint32_t symbol, match, count, stroke_idx, stroke_data; + Condition cond; + Direction dir; + bool matched; + + // Letter guessing + if (stroke_index) { + for (symbol = 0; symbol < handwriting->letter_count; symbol++) { + count = handwriting->letter[symbol].count; + matched = false; + if (count) { + // We have a count match to do + if ((count == 1) && (stroke_index == 1)) matched = true; + if ((count == 2) && (stroke_index == 2)) matched = true; + if ((count == 3) && (stroke_index > 2)) matched = true; + } else { + matched = true; + } + if (matched) { + for (match = 0; match < 3; match++) { + cond = handwriting->letter[symbol].match[match].cond; + dir = handwriting->letter[symbol].match[match].dir; + if ((cond != cond_empty) && (dir != dir_empty)) { + if (cond == last) { + if (stroke_index) + stroke_idx = stroke_index - 1; + else + stroke_idx = 0; + } else if (cond == stroke_a) + stroke_idx = 0; + else if (cond == stroke_b) + stroke_idx = 1; + else if (cond == stroke_c) + stroke_idx = 2; + else + stroke_idx = 3; + if (stroke_idx >= stroke_index) break; + stroke_data = stroke_list[stroke_idx]; + if ((dir & 0xF0) == 0xF0) { + if ((dir & 0x0F) != (stroke_data & 0x0F)) break; + } else if ((dir & 0x0F) == 0x0F) { + if ((dir & 0xF0) != (stroke_data & 0xF0)) break; + } else { + if (dir != (int32_t)stroke_data) break; + } + } + } + if (match == 3) + break; + else + matched = false; + } + } + if (matched) { + if (symbol) { + if (_lowercase) + char_add('a' + symbol - 1); + else + char_add('A' + symbol - 1); + clear_zone(Color::green(), true); // Green flash + } else { + if (_cursor_pos) { + char_delete(); + clear_zone(Color::yellow(), true); // Yellow flash + } else { + clear_zone(Color::red(), true); // Red flash + } + } + } else { + clear_zone(Color::red(), true); // Red flash + } + } else { + // Short tap is space + char_add(' '); + clear_zone(Color::green(), true); // Green flash + } + update_text(); + stroke_index = 0; } void HandWriteView::add_stroke(uint8_t dir) { - if (stroke_index < 8) { - stroke_list[stroke_index] = dir; - stroke_index++; - } else { - guess_letter(); - } + if (stroke_index < 8) { + stroke_list[stroke_index] = dir; + stroke_index++; + } else { + guess_letter(); + } } void HandWriteView::sample_pen() { - int16_t diff_x, diff_y; - uint8_t dir, dir_ud, dir_lr, stroke_prev; - - draw_cursor(); - - if (flash_timer) { - if (flash_timer == 1) clear_zone(Color::black(), false); - flash_timer--; - } - - if (!(sample_skip & 1)) { - if (tracing) { - if (move_wait) { - move_wait--; // ~100ms delay to get rid of jitter from touch start - } else { - diff_x = current_pos.x() - last_pos.x(); - diff_y = current_pos.y() - last_pos.y(); - - if (current_pos.y() <= 240) { - display.fill_rectangle( - {{current_pos.x(), current_pos.y()}, {4, 4}}, - Color::grey() - ); - } - - dir = 0; // UL by default - if (abs(diff_x) > 7) { - if (diff_x > 0) - dir |= 0x01; // R - } else { - dir |= 0x02; // ? - } - if (abs(diff_y) > 7) { - if (diff_y > 0) - dir |= 0x10; // D - } else { - dir |= 0x20; // ? - } - - // Need at least two identical directions to validate stroke - if ((dir & 0x11) == (dir_prev & 0x11)) - dir_cnt++; - else - dir_cnt = 0; - dir_prev = dir; - - if (dir_cnt > 1) { - dir_cnt = 0; - if (stroke_index) { - if ((stroke_list[stroke_index - 1] != dir) && (dir != 0x22)) { - // Register stroke if different from last one - dir_ud = (dir & 0xF0); - dir_lr = (dir & 0x0F); - stroke_prev = stroke_list[stroke_index - 1]; - if (dir_ud == 0x20) { - // LR changed - if ((stroke_prev & 0x0F) != dir_lr) add_stroke(dir); - } else if (dir_lr == 0x02) { - // UD changed - if ((stroke_prev & 0xF0) != dir_ud) add_stroke(dir); - } else { - // Add direction - if (((stroke_prev & 0xF0) == 0x20) && (dir_ud != 0x20)) { - // Add UD - if ((stroke_prev & 0x0F) == dir_lr) { - // Replace totally - stroke_list[stroke_index - 1] = dir; - } else if (dir_lr == 0x02) { - // Merge UD - stroke_list[stroke_index - 1] = dir_ud | (stroke_prev & 0x0F); - } else { - add_stroke(dir); - } - } else if (((stroke_prev & 0x0F) == 0x02) && (dir_lr != 0x02)) { - // Add LR - if ((stroke_prev & 0xF0) == dir_ud) { - // Replace totally - stroke_list[stroke_index - 1] = dir; - } else if (dir_ud == 0x20) { - // Merge LR - stroke_list[stroke_index - 1] = dir_lr | (stroke_prev & 0xF0); - } else { - add_stroke(dir); - } - } else { - add_stroke(dir); - } - } - } - } else { - // Register first stroke - if (dir != 0x22) add_stroke(dir); - } - } - } - - last_pos = current_pos; - } - } - - sample_skip++; + int16_t diff_x, diff_y; + uint8_t dir, dir_ud, dir_lr, stroke_prev; + + draw_cursor(); + + if (flash_timer) { + if (flash_timer == 1) clear_zone(Color::black(), false); + flash_timer--; + } + + if (!(sample_skip & 1)) { + if (tracing) { + if (move_wait) { + move_wait--; // ~100ms delay to get rid of jitter from touch start + } else { + diff_x = current_pos.x() - last_pos.x(); + diff_y = current_pos.y() - last_pos.y(); + + if (current_pos.y() <= 240) { + display.fill_rectangle( + {{current_pos.x(), current_pos.y()}, {4, 4}}, + Color::grey()); + } + + dir = 0; // UL by default + if (abs(diff_x) > 7) { + if (diff_x > 0) + dir |= 0x01; // R + } else { + dir |= 0x02; // ? + } + if (abs(diff_y) > 7) { + if (diff_y > 0) + dir |= 0x10; // D + } else { + dir |= 0x20; // ? + } + + // Need at least two identical directions to validate stroke + if ((dir & 0x11) == (dir_prev & 0x11)) + dir_cnt++; + else + dir_cnt = 0; + dir_prev = dir; + + if (dir_cnt > 1) { + dir_cnt = 0; + if (stroke_index) { + if ((stroke_list[stroke_index - 1] != dir) && (dir != 0x22)) { + // Register stroke if different from last one + dir_ud = (dir & 0xF0); + dir_lr = (dir & 0x0F); + stroke_prev = stroke_list[stroke_index - 1]; + if (dir_ud == 0x20) { + // LR changed + if ((stroke_prev & 0x0F) != dir_lr) add_stroke(dir); + } else if (dir_lr == 0x02) { + // UD changed + if ((stroke_prev & 0xF0) != dir_ud) add_stroke(dir); + } else { + // Add direction + if (((stroke_prev & 0xF0) == 0x20) && (dir_ud != 0x20)) { + // Add UD + if ((stroke_prev & 0x0F) == dir_lr) { + // Replace totally + stroke_list[stroke_index - 1] = dir; + } else if (dir_lr == 0x02) { + // Merge UD + stroke_list[stroke_index - 1] = dir_ud | (stroke_prev & 0x0F); + } else { + add_stroke(dir); + } + } else if (((stroke_prev & 0x0F) == 0x02) && (dir_lr != 0x02)) { + // Add LR + if ((stroke_prev & 0xF0) == dir_ud) { + // Replace totally + stroke_list[stroke_index - 1] = dir; + } else if (dir_ud == 0x20) { + // Merge LR + stroke_list[stroke_index - 1] = dir_lr | (stroke_prev & 0xF0); + } else { + add_stroke(dir); + } + } else { + add_stroke(dir); + } + } + } + } else { + // Register first stroke + if (dir != 0x22) add_stroke(dir); + } + } + } + + last_pos = current_pos; + } + } + + sample_skip++; } void HandWriteView::on_show() { - clear_zone(Color::black(), false); + clear_zone(Color::black(), false); } void HandWriteView::on_button(Button& button) { - char_add(button.id); - update_text(); + char_add(button.id); + update_text(); } -} +} // namespace ui diff --git a/firmware/application/ui_handwrite.hpp b/firmware/application/ui_handwrite.hpp index 0393b9bbc..e039fcd8d 100644 --- a/firmware/application/ui_handwrite.hpp +++ b/firmware/application/ui_handwrite.hpp @@ -32,60 +32,57 @@ namespace ui { class HandWriteView : public TextEntryView { -public: - HandWriteView(NavigationView& nav, std::string * str, size_t max_length); + public: + HandWriteView(NavigationView& nav, std::string* str, size_t max_length); - HandWriteView(const HandWriteView&) = delete; - HandWriteView(HandWriteView&&) = delete; - HandWriteView& operator=(const HandWriteView&) = delete; - HandWriteView& operator=(HandWriteView&&) = delete; - - void paint(Painter& painter) override; - void on_show() override; - bool on_touch(const TouchEvent event) override; - -private: - const char special_chars[5] = { '\'', '.', '?', '!', '=' }; - - const HandWriting * handwriting { }; - Painter * _painter { }; - uint8_t dir_cnt { 0 }; - uint8_t dir_prev { 0 }; - uint8_t flash_timer { 0 }; - bool tracing { false }; - uint8_t stroke_index { 0 }; - uint8_t sample_skip { 0 }, move_wait { 0 }; - uint8_t stroke_list[8]; - Point start_pos { }, current_pos { }, last_pos { }; - bool _lowercase = false; - - void sample_pen(); - void add_stroke(uint8_t dir); - void guess_letter(); - void clear_zone(const Color color, const bool flash); - void on_button(Button& button); - - std::array num_buttons { }; - std::array special_buttons { }; + HandWriteView(const HandWriteView&) = delete; + HandWriteView(HandWriteView&&) = delete; + HandWriteView& operator=(const HandWriteView&) = delete; + HandWriteView& operator=(HandWriteView&&) = delete; - Button button_case { - { 8, 270, 32, 28 }, - "UC" - }; + void paint(Painter& painter) override; + void on_show() override; + bool on_touch(const TouchEvent event) override; - Button button_ok { - { 190, 270, 40, 28 }, - "OK" - }; - - MessageHandlerRegistration message_handler_sample { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - this->sample_pen(); - } - }; + private: + const char special_chars[5] = {'\'', '.', '?', '!', '='}; + + const HandWriting* handwriting{}; + Painter* _painter{}; + uint8_t dir_cnt{0}; + uint8_t dir_prev{0}; + uint8_t flash_timer{0}; + bool tracing{false}; + uint8_t stroke_index{0}; + uint8_t sample_skip{0}, move_wait{0}; + uint8_t stroke_list[8]; + Point start_pos{}, current_pos{}, last_pos{}; + bool _lowercase = false; + + void sample_pen(); + void add_stroke(uint8_t dir); + void guess_letter(); + void clear_zone(const Color color, const bool flash); + void on_button(Button& button); + + std::array num_buttons{}; + std::array special_buttons{}; + + Button button_case{ + {8, 270, 32, 28}, + "UC"}; + + Button button_ok{ + {190, 270, 40, 28}, + "OK"}; + + MessageHandlerRegistration message_handler_sample{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->sample_pen(); + }}; }; } /* namespace ui */ -#endif/*__UNISTROKE_H__*/ +#endif /*__UNISTROKE_H__*/ diff --git a/firmware/application/ui_loadmodule.cpp b/firmware/application/ui_loadmodule.cpp index 30eba7e4b..592488871 100644 --- a/firmware/application/ui_loadmodule.cpp +++ b/firmware/application/ui_loadmodule.cpp @@ -51,126 +51,120 @@ using namespace portapack; namespace ui { void LoadModuleView::focus() { - button_ok.focus(); + button_ok.focus(); } void LoadModuleView::on_show() { - char md5_signature[16]; - uint8_t c; - - memcpy(md5_signature, (const void *)(0x10087FF0), 16); - for (c=0; c<16; c++) { - if (md5_signature[c] != _hash[c]) break; - } - //text_info.set(to_string_hex(*((unsigned int*)0x10087FF0), 8)); - - if (c == 16) { - text_info.set("Module already loaded :)"); - _mod_loaded = true; - } else { - text_info.set("Loading module"); - loadmodule(); - } + char md5_signature[16]; + uint8_t c; + + memcpy(md5_signature, (const void*)(0x10087FF0), 16); + for (c = 0; c < 16; c++) { + if (md5_signature[c] != _hash[c]) break; + } + // text_info.set(to_string_hex(*((unsigned int*)0x10087FF0), 8)); + + if (c == 16) { + text_info.set("Module already loaded :)"); + _mod_loaded = true; + } else { + text_info.set("Loading module"); + loadmodule(); + } } int LoadModuleView::load_image() { - const char magic[6] = {'P', 'P', 'M', ' ', 0x02, 0x00}; - UINT bw; - uint8_t i; - uint32_t cnt; - char md5sum[16]; - FILINFO modinfo; - FIL modfile; - DIR rootdir; - FRESULT res; - - // Scan SD card root directory for files with the right MD5 fingerprint at the right location - if (f_opendir(&rootdir, "/") == FR_OK) { - for (;;) { - res = f_readdir(&rootdir, &modinfo); - if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort - // Only care about files with .bin extension - if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) { - res = f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ); - if (res != FR_OK) return 0; - // Magic bytes and version check - f_read(&modfile, &md5sum, 6, &bw); - for (i = 0; i < 6; i++) - if (md5sum[i] != magic[i]) break; - if (i == 6) { - f_lseek(&modfile, 26); - f_read(&modfile, &md5sum, 16, &bw); - for (i = 0; i < 16; i++) - if (md5sum[i] != _hash[i]) break; - // f_read can't read more than 512 bytes at a time ? - if (i == 16) { - f_lseek(&modfile, 512); - for (cnt = 0; cnt < 64; cnt++) { - if (f_read(&modfile, reinterpret_cast(portapack::memory::map::m4_code.base() + (cnt * 512)), 512, &bw)) return 0; - } - f_close(&modfile); - f_closedir(&rootdir); - LPC_RGU->RESET_CTRL[0] = (1 << 13); - return 1; - } - } - f_close(&modfile); - } - } - f_closedir(&rootdir); - } - - return 0; + const char magic[6] = {'P', 'P', 'M', ' ', 0x02, 0x00}; + UINT bw; + uint8_t i; + uint32_t cnt; + char md5sum[16]; + FILINFO modinfo; + FIL modfile; + DIR rootdir; + FRESULT res; + + // Scan SD card root directory for files with the right MD5 fingerprint at the right location + if (f_opendir(&rootdir, "/") == FR_OK) { + for (;;) { + res = f_readdir(&rootdir, &modinfo); + if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort + // Only care about files with .bin extension + if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) { + res = f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ); + if (res != FR_OK) return 0; + // Magic bytes and version check + f_read(&modfile, &md5sum, 6, &bw); + for (i = 0; i < 6; i++) + if (md5sum[i] != magic[i]) break; + if (i == 6) { + f_lseek(&modfile, 26); + f_read(&modfile, &md5sum, 16, &bw); + for (i = 0; i < 16; i++) + if (md5sum[i] != _hash[i]) break; + // f_read can't read more than 512 bytes at a time ? + if (i == 16) { + f_lseek(&modfile, 512); + for (cnt = 0; cnt < 64; cnt++) { + if (f_read(&modfile, reinterpret_cast(portapack::memory::map::m4_code.base() + (cnt * 512)), 512, &bw)) return 0; + } + f_close(&modfile); + f_closedir(&rootdir); + LPC_RGU->RESET_CTRL[0] = (1 << 13); + return 1; + } + } + f_close(&modfile); + } + } + f_closedir(&rootdir); + } + + return 0; } void LoadModuleView::loadmodule() { - //baseband::shutdown(); - - /*EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch, - [this](Message* const p) { - (void)p;*/ - if (load_image()) { - text_infob.set("Module loaded :)"); - _mod_loaded = true; - } else { - text_info.set("Module not found :("); - _mod_loaded = false; - } - // } - //); - + // baseband::shutdown(); + + /*EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch, + [this](Message* const p) { + (void)p;*/ + if (load_image()) { + text_infob.set("Module loaded :)"); + _mod_loaded = true; + } else { + text_info.set("Module not found :("); + _mod_loaded = false; + } + // } + //); } LoadModuleView::LoadModuleView( - NavigationView& nav, - const char * hash, - ViewID viewid -) -{ - - add_children({ - &text_info, - &text_infob, - &button_ok - }); - - _hash = hash; - - button_ok.on_select = [this, &nav, viewid](Button&){ - nav.pop(); - if (_mod_loaded == true) { - if (viewid == AudioTX) nav.push(); - if (viewid == Xylos) nav.push(); - if (viewid == EPAR) nav.push(); - if (viewid == LCR) nav.push(); - if (viewid == SoundBoard) nav.push(); - if (viewid == AnalogAudio) nav.push(); - if (viewid == RDS) nav.push(); - if (viewid == CloseCall) nav.push(); - if (viewid == Receiver) nav.push(); - if (viewid == Jammer) nav.push(); - } - }; + NavigationView& nav, + const char* hash, + ViewID viewid) { + add_children({&text_info, + &text_infob, + &button_ok}); + + _hash = hash; + + button_ok.on_select = [this, &nav, viewid](Button&) { + nav.pop(); + if (_mod_loaded == true) { + if (viewid == AudioTX) nav.push(); + if (viewid == Xylos) nav.push(); + if (viewid == EPAR) nav.push(); + if (viewid == LCR) nav.push(); + if (viewid == SoundBoard) nav.push(); + if (viewid == AnalogAudio) nav.push(); + if (viewid == RDS) nav.push(); + if (viewid == CloseCall) nav.push(); + if (viewid == Receiver) nav.push(); + if (viewid == Jammer) nav.push(); + } + }; } } /* namespace ui */ diff --git a/firmware/application/ui_loadmodule.hpp b/firmware/application/ui_loadmodule.hpp index 3f727cf04..ac3c3ff5f 100644 --- a/firmware/application/ui_loadmodule.hpp +++ b/firmware/application/ui_loadmodule.hpp @@ -31,44 +31,41 @@ namespace ui { enum ViewID { - Receiver, - AudioTX, - CloseCall, - Xylos, - EPAR, - LCR, - SoundBoard, - AnalogAudio, - RDS, - Jammer + Receiver, + AudioTX, + CloseCall, + Xylos, + EPAR, + LCR, + SoundBoard, + AnalogAudio, + RDS, + Jammer }; class LoadModuleView : public View { -public: - LoadModuleView(NavigationView& nav, const char * hash, ViewID viewid); - void loadmodule(); - - void on_show() override; - void focus() override; + public: + LoadModuleView(NavigationView& nav, const char* hash, ViewID viewid); + void loadmodule(); -private: - int load_image(void); - const char * _hash; - bool _mod_loaded = false; - - Text text_info { - { 8, 64, 224, 16 }, - "-" - }; - Text text_infob { - { 8, 64+16, 224, 16 }, - "-" - }; - - Button button_ok { - { 88, 128, 64, 32 }, - "OK" - }; + void on_show() override; + void focus() override; + + private: + int load_image(void); + const char* _hash; + bool _mod_loaded = false; + + Text text_info{ + {8, 64, 224, 16}, + "-"}; + Text text_infob{ + {8, 64 + 16, 224, 16}, + "-"}; + + Button button_ok{ + {88, 128, 64, 32}, + "OK"}; }; } /* namespace ui */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 20cd1213f..b3ed39fc9 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -101,811 +101,751 @@ namespace ui { /* SystemStatusView ******************************************************/ SystemStatusView::SystemStatusView( - NavigationView& nav -) : nav_ (nav) -{ - static constexpr Style style_systemstatus { - .font = font::fixed_8x16, - .background = Color::dark_grey(), - .foreground = Color::white(), - }; - - add_children({ - &backdrop, - &button_back, - &title, - &button_title, - &button_converter, - &button_speaker, - &button_stealth, - //&button_textentry, - &button_camera, - &button_sleep, - &button_bias_tee, - &button_clock_status, - &sd_card_status_view, - }); - - if (portapack::persistent_memory::config_speaker()) - button_speaker.hidden(false); - else - button_speaker.hidden(true); - - - if( portapack::persistent_memory::config_hide_converter() ) - { - button_converter.hidden(true); - } - else - { - button_converter.hidden(false); - if( portapack::persistent_memory::config_converter() ) - { - button_converter.set_foreground(Color::red()); - } - else - { - button_converter.set_foreground(Color::light_grey()); - } - } - - button_back.id = -1; // Special ID used by FocusManager - title.set_style(&style_systemstatus); - - if (portapack::persistent_memory::stealth_mode()) - button_stealth.set_foreground(ui::Color::green()); - - /*if (!portapack::persistent_memory::ui_config_textentry()) - button_textentry.set_bitmap(&bitmap_icon_keyboard); - else - button_textentry.set_bitmap(&bitmap_icon_unistroke);*/ - - refresh(); - - button_back.on_select = [this](ImageButton&){ - if (this->on_back) - this->on_back(); - }; - - button_title.on_select = [this](ImageButton&) { - this->on_title(); - }; - - button_converter.on_select = [this](ImageButton&) { - this->on_converter(); - }; - - button_speaker.on_select = [this](ImageButton&) { - this->on_speaker(); - }; - - button_stealth.on_select = [this](ImageButton&) { - this->on_stealth(); - }; - - button_bias_tee.on_select = [this](ImageButton&) { - this->on_bias_tee(); - }; - - /*button_textentry.on_select = [this](ImageButton&) { - this->on_textentry(); - };*/ - - button_camera.on_select = [this](ImageButton&) { - this->on_camera(); - }; - - button_sleep.on_select = [this](ImageButton&) { - DisplaySleepMessage message; - EventDispatcher::send_message(message); - }; - - button_clock_status.on_select = [this](ImageButton&) { - this->on_clk(); - }; + NavigationView& nav) + : nav_(nav) { + static constexpr Style style_systemstatus{ + .font = font::fixed_8x16, + .background = Color::dark_grey(), + .foreground = Color::white(), + }; + + add_children({ + &backdrop, + &button_back, + &title, + &button_title, + &button_converter, + &button_speaker, + &button_stealth, + //&button_textentry, + &button_camera, + &button_sleep, + &button_bias_tee, + &button_clock_status, + &sd_card_status_view, + }); + + if (portapack::persistent_memory::config_speaker()) + button_speaker.hidden(false); + else + button_speaker.hidden(true); + + if (portapack::persistent_memory::config_hide_converter()) { + button_converter.hidden(true); + } else { + button_converter.hidden(false); + if (portapack::persistent_memory::config_converter()) { + button_converter.set_foreground(Color::red()); + } else { + button_converter.set_foreground(Color::light_grey()); + } + } + + button_back.id = -1; // Special ID used by FocusManager + title.set_style(&style_systemstatus); + + if (portapack::persistent_memory::stealth_mode()) + button_stealth.set_foreground(ui::Color::green()); + + /*if (!portapack::persistent_memory::ui_config_textentry()) + button_textentry.set_bitmap(&bitmap_icon_keyboard); + else + button_textentry.set_bitmap(&bitmap_icon_unistroke);*/ + + refresh(); + + button_back.on_select = [this](ImageButton&) { + if (this->on_back) + this->on_back(); + }; + + button_title.on_select = [this](ImageButton&) { + this->on_title(); + }; + + button_converter.on_select = [this](ImageButton&) { + this->on_converter(); + }; + + button_speaker.on_select = [this](ImageButton&) { + this->on_speaker(); + }; + + button_stealth.on_select = [this](ImageButton&) { + this->on_stealth(); + }; + + button_bias_tee.on_select = [this](ImageButton&) { + this->on_bias_tee(); + }; + + /*button_textentry.on_select = [this](ImageButton&) { + this->on_textentry(); + };*/ + + button_camera.on_select = [this](ImageButton&) { + this->on_camera(); + }; + + button_sleep.on_select = [this](ImageButton&) { + DisplaySleepMessage message; + EventDispatcher::send_message(message); + }; + + button_clock_status.on_select = [this](ImageButton&) { + this->on_clk(); + }; } void SystemStatusView::refresh() { - if( portapack::persistent_memory::config_hide_converter() ) - { - button_converter.hidden(true); - } - else - { - if( portapack::persistent_memory::config_updown_converter() ) - { - button_converter.set_bitmap(&bitmap_icon_downconvert); - } - else - { - button_converter.set_bitmap(&bitmap_icon_upconvert); - } - button_converter.hidden(false); - if( portapack::persistent_memory::config_converter() ) - { - button_converter.set_foreground(Color::red()); - } - else - { - button_converter.set_foreground(Color::light_grey()); - } - } - // Retune to take converter change in account - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); - transmitter_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); - - if (!portapack::persistent_memory::config_speaker()) { - button_speaker.set_foreground(Color::light_grey()); - button_speaker.set_bitmap(&bitmap_icon_speaker_mute); - button_speaker.hidden(false); - } - else { - button_speaker.hidden(true); - } - - if (portapack::get_antenna_bias()) { - button_bias_tee.set_bitmap(&bitmap_icon_biast_on); - button_bias_tee.set_foreground(ui::Color::yellow()); - } else { - button_bias_tee.set_bitmap(&bitmap_icon_biast_off); - button_bias_tee.set_foreground(ui::Color::light_grey()); - } - - if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) { - button_clock_status.set_bitmap(&bitmap_icon_clk_ext); -// button_bias_tee.set_foreground(ui::Color::green()); Typo? - } else { - button_clock_status.set_bitmap(&bitmap_icon_clk_int); -// button_bias_tee.set_foreground(ui::Color::green()); - } - - if(portapack::persistent_memory::clkout_enabled()) { - button_clock_status.set_foreground(ui::Color::green()); - } else { - button_clock_status.set_foreground(ui::Color::light_grey()); - } - - set_dirty(); + if (portapack::persistent_memory::config_hide_converter()) { + button_converter.hidden(true); + } else { + if (portapack::persistent_memory::config_updown_converter()) { + button_converter.set_bitmap(&bitmap_icon_downconvert); + } else { + button_converter.set_bitmap(&bitmap_icon_upconvert); + } + button_converter.hidden(false); + if (portapack::persistent_memory::config_converter()) { + button_converter.set_foreground(Color::red()); + } else { + button_converter.set_foreground(Color::light_grey()); + } + } + // Retune to take converter change in account + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + + if (!portapack::persistent_memory::config_speaker()) { + button_speaker.set_foreground(Color::light_grey()); + button_speaker.set_bitmap(&bitmap_icon_speaker_mute); + button_speaker.hidden(false); + } else { + button_speaker.hidden(true); + } + + if (portapack::get_antenna_bias()) { + button_bias_tee.set_bitmap(&bitmap_icon_biast_on); + button_bias_tee.set_foreground(ui::Color::yellow()); + } else { + button_bias_tee.set_bitmap(&bitmap_icon_biast_off); + button_bias_tee.set_foreground(ui::Color::light_grey()); + } + + if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) { + button_clock_status.set_bitmap(&bitmap_icon_clk_ext); + // button_bias_tee.set_foreground(ui::Color::green()); Typo? + } else { + button_clock_status.set_bitmap(&bitmap_icon_clk_int); + // button_bias_tee.set_foreground(ui::Color::green()); + } + + if (portapack::persistent_memory::clkout_enabled()) { + button_clock_status.set_foreground(ui::Color::green()); + } else { + button_clock_status.set_foreground(ui::Color::light_grey()); + } + + set_dirty(); } void SystemStatusView::set_back_enabled(bool new_value) { - - if(new_value){ - add_child(&button_back); - } - else{ - remove_child(&button_back); - } + if (new_value) { + add_child(&button_back); + } else { + remove_child(&button_back); + } } void SystemStatusView::set_title_image_enabled(bool new_value) { - - if(new_value){ - add_child(&button_title); - } - else{ - remove_child(&button_title); - } + if (new_value) { + add_child(&button_title); + } else { + remove_child(&button_title); + } } void SystemStatusView::set_title(const std::string new_value) { - if( new_value.empty() ) { - title.set(default_title); - } else { - title.set(new_value); - } + if (new_value.empty()) { + title.set(default_title); + } else { + title.set(new_value); + } } void SystemStatusView::on_converter() { - if(!portapack::persistent_memory::config_converter()) - { - portapack::persistent_memory::set_config_converter( true ); - button_converter.set_foreground(Color::red()); - } - else - { - portapack::persistent_memory::set_config_converter( false ); - button_converter.set_foreground(Color::light_grey()); - } - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // Retune - } + if (!portapack::persistent_memory::config_converter()) { + portapack::persistent_memory::set_config_converter(true); + button_converter.set_foreground(Color::red()); + } else { + portapack::persistent_memory::set_config_converter(false); + button_converter.set_foreground(Color::light_grey()); + } + receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); // Retune +} void SystemStatusView::on_speaker() { - if (!portapack::speaker_mode) - { - portapack::set_speaker_mode(true); - button_speaker.set_foreground(Color::green()); - button_speaker.set_bitmap(&bitmap_icon_speaker); - } - else - { - portapack::set_speaker_mode(false); - button_speaker.set_foreground(Color::light_grey()); - button_speaker.set_bitmap(&bitmap_icon_speaker_mute); - } - - } - + if (!portapack::speaker_mode) { + portapack::set_speaker_mode(true); + button_speaker.set_foreground(Color::green()); + button_speaker.set_bitmap(&bitmap_icon_speaker); + } else { + portapack::set_speaker_mode(false); + button_speaker.set_foreground(Color::light_grey()); + button_speaker.set_bitmap(&bitmap_icon_speaker_mute); + } +} void SystemStatusView::on_stealth() { - bool mode = not portapack::persistent_memory::stealth_mode(); - - portapack::persistent_memory::set_stealth_mode(mode); + bool mode = not portapack::persistent_memory::stealth_mode(); + + portapack::persistent_memory::set_stealth_mode(mode); - button_stealth.set_foreground(mode ? Color::green() : Color::light_grey()); + button_stealth.set_foreground(mode ? Color::green() : Color::light_grey()); } void SystemStatusView::on_bias_tee() { - if (!portapack::antenna_bias) { - nav_.display_modal("Bias voltage", "Enable DC voltage on\nantenna connector?", YESNO, [this](bool v) { - if (v) { - portapack::set_antenna_bias(true); - //radio::set_antenna_bias(true); - receiver_model.set_antenna_bias(); - transmitter_model.set_antenna_bias(); - refresh(); - } - }); - } else { - portapack::set_antenna_bias(false); - //radio::set_antenna_bias(false); - receiver_model.set_antenna_bias(); - transmitter_model.set_antenna_bias(); - refresh(); - } + if (!portapack::antenna_bias) { + nav_.display_modal("Bias voltage", "Enable DC voltage on\nantenna connector?", YESNO, [this](bool v) { + if (v) { + portapack::set_antenna_bias(true); + // radio::set_antenna_bias(true); + receiver_model.set_antenna_bias(); + transmitter_model.set_antenna_bias(); + refresh(); + } + }); + } else { + portapack::set_antenna_bias(false); + // radio::set_antenna_bias(false); + receiver_model.set_antenna_bias(); + transmitter_model.set_antenna_bias(); + refresh(); + } } /*void SystemStatusView::on_textentry() { - uint8_t cfg; - - cfg = portapack::persistent_memory::ui_config_textentry(); - portapack::persistent_memory::set_config_textentry(cfg ^ 1); - - if (!cfg) - button_textentry.set_bitmap(&bitmap_icon_unistroke); - else - button_textentry.set_bitmap(&bitmap_icon_keyboard); + uint8_t cfg; + + cfg = portapack::persistent_memory::ui_config_textentry(); + portapack::persistent_memory::set_config_textentry(cfg ^ 1); + + if (!cfg) + button_textentry.set_bitmap(&bitmap_icon_unistroke); + else + button_textentry.set_bitmap(&bitmap_icon_keyboard); }*/ void SystemStatusView::on_camera() { - ensure_directory("SCREENSHOTS"); - auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG"); + ensure_directory("SCREENSHOTS"); + auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG"); - if( path.empty() ) { - return; - } + if (path.empty()) { + return; + } - PNGWriter png; - auto create_error = png.create(path); - if( create_error.is_valid() ) { - return; - } + PNGWriter png; + auto create_error = png.create(path); + if (create_error.is_valid()) { + return; + } - for(int i = 0; i < 320; i++) { - std::array row; - portapack::display.read_pixels({ 0, i, 240, 1 }, row); - png.write_scanline(row); - } + for (int i = 0; i < 320; i++) { + std::array row; + portapack::display.read_pixels({0, i, 240, 1}, row); + png.write_scanline(row); + } } void SystemStatusView::on_clk() { - bool v = portapack::persistent_memory::clkout_enabled(); - if(v) { - v = false; - } else { - v = true; - } - portapack::clock_manager.enable_clock_output(v); - portapack::persistent_memory::set_clkout_enabled(v); - refresh(); + bool v = portapack::persistent_memory::clkout_enabled(); + if (v) { + v = false; + } else { + v = true; + } + portapack::clock_manager.enable_clock_output(v); + portapack::persistent_memory::set_clkout_enabled(v); + refresh(); } void SystemStatusView::on_title() { - if(nav_.is_top()) - nav_.push(); - else - nav_.pop(); + if (nav_.is_top()) + nav_.push(); + else + nav_.pop(); } /* Information View *****************************************************/ InformationView::InformationView( - NavigationView& nav -) : nav_ (nav) -{ - static constexpr Style style_infobar { - .font = font::fixed_8x16, - .background = {33, 33, 33}, - .foreground = Color::white(), - }; - - add_children({ - &backdrop, - &version, - <ime - }); - - version.set_style(&style_infobar); - - ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); - ltime.set_style(&style_infobar); - ltime.set_seconds_enabled(true); - ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); - set_dirty(); + NavigationView& nav) + : nav_(nav) { + static constexpr Style style_infobar{ + .font = font::fixed_8x16, + .background = {33, 33, 33}, + .foreground = Color::white(), + }; + + add_children({&backdrop, + &version, + <ime}); + + version.set_style(&style_infobar); + + ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); + ltime.set_style(&style_infobar); + ltime.set_seconds_enabled(true); + ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); + set_dirty(); } void InformationView::refresh() { - ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); - ltime.set_seconds_enabled(true); - ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); - + ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); + ltime.set_seconds_enabled(true); + ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); } /* Navigation ************************************************************/ bool NavigationView::is_top() const { - return view_stack.size() == 1; + return view_stack.size() == 1; } View* NavigationView::push_view(std::unique_ptr new_view) { - free_view(); + free_view(); - const auto p = new_view.get(); - view_stack.emplace_back(std::move(new_view)); + const auto p = new_view.get(); + view_stack.emplace_back(std::move(new_view)); - update_view(); + update_view(); - return p; + return p; } void NavigationView::pop() { - if( view() == modal_view ) { - modal_view = nullptr; - } + if (view() == modal_view) { + modal_view = nullptr; + } - // Can't pop last item from stack. - if( view_stack.size() > 1 ) { - free_view(); + // Can't pop last item from stack. + if (view_stack.size() > 1) { + free_view(); - view_stack.pop_back(); - - update_view(); - } + view_stack.pop_back(); + update_view(); + } } void NavigationView::pop_modal() { - if( view() == modal_view ) { - modal_view = nullptr; - } + if (view() == modal_view) { + modal_view = nullptr; + } - // Pop modal view + underlying app view - if( view_stack.size() > 2 ) { - free_view(); - view_stack.pop_back(); - free_view(); - view_stack.pop_back(); + // Pop modal view + underlying app view + if (view_stack.size() > 2) { + free_view(); + view_stack.pop_back(); + free_view(); + view_stack.pop_back(); - update_view(); - } + update_view(); + } } void NavigationView::display_modal( - const std::string& title, - const std::string& message -) { - display_modal(title, message, INFO, nullptr); + const std::string& title, + const std::string& message) { + display_modal(title, message, INFO, nullptr); } void NavigationView::display_modal( - const std::string& title, - const std::string& message, - const modal_t type, - const std::function on_choice -) { - /* If a modal view is already visible, don't display another */ - if( !modal_view ) { - modal_view = push(title, message, type, on_choice); - } + const std::string& title, + const std::string& message, + const modal_t type, + const std::function on_choice) { + /* If a modal view is already visible, don't display another */ + if (!modal_view) { + modal_view = push(title, message, type, on_choice); + } } void NavigationView::free_view() { - remove_child(view()); + remove_child(view()); } void NavigationView::update_view() { - const auto new_view = view_stack.back().get(); - - add_child(new_view); - new_view->set_parent_rect({ {0, 0}, size() }); - - focus(); - set_dirty(); + const auto new_view = view_stack.back().get(); + + add_child(new_view); + new_view->set_parent_rect({{0, 0}, size()}); - if( on_view_changed ) { - on_view_changed(*new_view); - } + focus(); + set_dirty(); + + if (on_view_changed) { + on_view_changed(*new_view); + } } Widget* NavigationView::view() const { - return children_.empty() ? nullptr : children_[0]; + return children_.empty() ? nullptr : children_[0]; } void NavigationView::focus() { - if( view() ) { - view()->focus(); - } + if (view()) { + view()->focus(); + } } /* ReceiversMenuView *****************************************************/ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { - if( portapack::persistent_memory::show_gui_return_icon() ) - { - add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous , [&nav](){ nav.pop(); } } } ); - } - add_items( { - { "ADS-B", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, - //{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, - { "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push(); } }, - { "AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav](){ nav.push(); } }, - { "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push(); } }, - { "NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav](){ nav.push(); } }, - { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, - { "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, - { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, - { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, - { "Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav](){ nav.push(); } }, - { "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push(); } }, - { "Recon", ui::Color::green(), &bitmap_icon_scanner, [&nav](){ nav.push(); } }, - { "Level", ui::Color::green(), &bitmap_icon_options_radio, [&nav](){ nav.push(); } }, - { "APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav](){ nav.push(); } } - /* - { "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); } }, - { "SIGFOX", ui::Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); } }, // SIGFRXView - { "LoRa", ui::Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push(); } }, - { "SSTV", ui::Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, - { "TETRA", ui::Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push(); } },*/ - } ); - - //set_highlighted(0); // Default selection is "Audio" + if (portapack::persistent_memory::show_gui_return_icon()) { + add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + } + add_items({ + { + "ADS-B", + ui::Color::green(), + &bitmap_icon_adsb, + [&nav]() { nav.push(); }, + }, + //{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, + {"AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav]() { nav.push(); }}, + {"AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push(); }}, + {"BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, + {"NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push(); }}, + {"Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push(); }}, + {"Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, + {"ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav]() { nav.push(); }}, + {"POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, + {"Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push(); }}, + {"TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push(); }}, + {"Recon", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, + {"Level", ui::Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, + {"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }} + /* + { "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); } }, + { "SIGFOX", ui::Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); } }, // SIGFRXView + { "LoRa", ui::Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push(); } }, + { "SSTV", ui::Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, + { "TETRA", ui::Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push(); } },*/ + }); + + // set_highlighted(0); // Default selection is "Audio" } /* TransmittersMenuView **************************************************/ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { - if( portapack::persistent_memory::show_gui_return_icon() ) - { - add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous , [&nav](){ nav.pop(); } } } ); - } - add_items({ - { "ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); } }, - { "APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, - { "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push(); } }, - { "GPS Sim", ui::Color::yellow(), &bitmap_icon_gps_sim, [&nav](){ nav.push(); } }, - { "Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav](){ nav.push(); } }, - //{ "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, - { "LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav](){ nav.push(); } }, - { "Morse", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push(); } }, - { "BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push(); } }, - //{ "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push(); } }, - { "OOK", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, - { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, - { "RDS", ui::Color::green(), &bitmap_icon_rds, [&nav](){ nav.push(); } }, - { "Soundbrd", ui::Color::green(), &bitmap_icon_soundboard,[&nav](){ nav.push(); } }, - { "SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, - { "TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push(); } }, - { "TouchTune", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, - { "Playlist", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, - { "S.Painter", ui::Color::orange(), &bitmap_icon_morse, [&nav](){ nav.push(); } }, - //{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, - }); + if (portapack::persistent_memory::show_gui_return_icon()) { + add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + } + add_items({ + {"ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav]() { nav.push(); }}, + {"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }}, + {"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push(); }}, + {"GPS Sim", ui::Color::yellow(), &bitmap_icon_gps_sim, [&nav]() { nav.push(); }}, + {"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push(); }}, + //{ "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, + {"LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push(); }}, + {"Morse", ui::Color::green(), &bitmap_icon_morse, [&nav]() { nav.push(); }}, + {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push(); }}, + //{ "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push(); } }, + {"OOK", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, + {"POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, + {"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push(); }}, + {"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, + {"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, + {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push(); }}, + {"TouchTune", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, + {"Playlist", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, + {"S.Painter", ui::Color::orange(), &bitmap_icon_morse, [&nav]() { nav.push(); }}, + //{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, + }); } /* UtilitiesMenuView *****************************************************/ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) { - if( portapack::persistent_memory::show_gui_return_icon() ) - { - add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous , [&nav](){ nav.pop(); } } } ); - } - add_items({ - //{ "Test app", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, - { "Freq. manager", ui::Color::green(), &bitmap_icon_freqman, [&nav](){ nav.push(); } }, - { "File manager", ui::Color::yellow(), &bitmap_icon_dir, [&nav](){ nav.push(); } }, - //{ "Notepad", ui::Color::dark_grey(), &bitmap_icon_notepad, [&nav](){ nav.push(); } }, - { "Signal gen", ui::Color::green(), &bitmap_icon_cwgen, [&nav](){ nav.push(); } }, - //{ "Tone search", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, - { "Wav viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav](){ nav.push(); } }, - { "Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav](){ nav.push(); } }, - - { "Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav](){ nav.push(); } }, - { "Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav](){ nav.push(); } }, - { "SD over USB", ui::Color::yellow(), &bitmap_icon_hackrf, [&nav](){ nav.push(); } }, - }); - set_max_rows(2); // allow wider buttons + if (portapack::persistent_memory::show_gui_return_icon()) { + add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + } + add_items({ + //{ "Test app", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, + {"Freq. manager", ui::Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push(); }}, + {"File manager", ui::Color::yellow(), &bitmap_icon_dir, [&nav]() { nav.push(); }}, + //{ "Notepad", ui::Color::dark_grey(), &bitmap_icon_notepad, [&nav](){ nav.push(); } }, + {"Signal gen", ui::Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push(); }}, + //{ "Tone search", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, + {"Wav viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, + {"Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push(); }}, + + {"Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push(); }}, + {"Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push(); }}, + {"SD over USB", ui::Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push(); }}, + }); + set_max_rows(2); // allow wider buttons } /* SystemMenuView ********************************************************/ void SystemMenuView::hackrf_mode(NavigationView& nav) { - nav.push("HackRF mode", " This mode enables HackRF\n functionality. To return,\n press the reset button.\n\n Switch to HackRF mode?", YESNO, - [this](bool choice) { - if (choice) { - EventDispatcher::request_stop(); - } - } - ); + nav.push("HackRF mode", " This mode enables HackRF\n functionality. To return,\n press the reset button.\n\n Switch to HackRF mode?", YESNO, + [this](bool choice) { + if (choice) { + EventDispatcher::request_stop(); + } + }); } SystemMenuView::SystemMenuView(NavigationView& nav) { - add_items({ - //{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push(); } }, - { "Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push(); } }, - { "Transmit", ui::Color::cyan(), &bitmap_icon_transmit, [&nav](){ nav.push(); } }, - { "Capture", ui::Color::red(), &bitmap_icon_capture, [&nav](){ nav.push(); } }, - { "Replay", ui::Color::green(), &bitmap_icon_replay, [&nav](){ nav.push(); } }, - { "Search", ui::Color::yellow(), &bitmap_icon_search, [&nav](){ nav.push(); } }, - { "Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav](){ nav.push(); } }, - { "Microphone", ui::Color::yellow(), &bitmap_icon_microphone,[&nav](){ nav.push(); } }, - { "Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav](){ nav.push(); } }, - { "Utilities", ui::Color::cyan(), &bitmap_icon_utilities, [&nav](){ nav.push(); } }, - { "Settings", ui::Color::cyan(), &bitmap_icon_setup, [&nav](){ nav.push(); } }, - { "Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav](){ nav.push(); } }, - { "HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } }, - //{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } } - }); - set_max_rows(2); // allow wider buttons - set_arrow_enabled(false); - //set_highlighted(1); // Startup selection + add_items({ + //{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push(); } }, + {"Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push(); }}, + {"Transmit", ui::Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push(); }}, + {"Capture", ui::Color::red(), &bitmap_icon_capture, [&nav]() { nav.push(); }}, + {"Replay", ui::Color::green(), &bitmap_icon_replay, [&nav]() { nav.push(); }}, + {"Search", ui::Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push(); }}, + {"Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, + {"Microphone", ui::Color::yellow(), &bitmap_icon_microphone, [&nav]() { nav.push(); }}, + {"Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav]() { nav.push(); }}, + {"Utilities", ui::Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push(); }}, + {"Settings", ui::Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push(); }}, + {"Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push(); }}, + {"HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }}, + //{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } } + }); + set_max_rows(2); // allow wider buttons + set_arrow_enabled(false); + // set_highlighted(1); // Startup selection } /* SystemView ************************************************************/ -static constexpr ui::Style style_default { - .font = ui::font::fixed_8x16, - .background = ui::Color::black(), - .foreground = ui::Color::white() -}; +static constexpr ui::Style style_default{ + .font = ui::font::fixed_8x16, + .background = ui::Color::black(), + .foreground = ui::Color::white()}; SystemView::SystemView( - Context& context, - const Rect parent_rect -) : View { parent_rect }, - context_(context) -{ - set_style(&style_default); - - constexpr ui::Dim status_view_height = 16; - constexpr ui::Dim info_view_height = 16; - - add_child(&status_view); - status_view.set_parent_rect({ - { 0, 0 }, - { parent_rect.width(), status_view_height } - }); - status_view.on_back = [this]() { - this->navigation_view.pop(); - }; - - add_child(&navigation_view); - navigation_view.set_parent_rect({ - { 0, status_view_height }, - { parent_rect.width(), static_cast(parent_rect.height() - status_view_height) } - }); - - add_child(&info_view); - info_view.set_parent_rect({ - {0, 19 * 16}, - { parent_rect.width(), info_view_height } - }); - - navigation_view.on_view_changed = [this](const View& new_view) { - - if(!this->navigation_view.is_top()){ - remove_child(&info_view); - } - else{ - add_child(&info_view); - info_view.refresh(); - } - - this->status_view.set_back_enabled(!this->navigation_view.is_top()); - this->status_view.set_title_image_enabled(this->navigation_view.is_top()); - this->status_view.set_title(new_view.title()); - this->status_view.set_dirty(); - - }; - - - // portapack::persistent_memory::set_playdead_sequence(0x8D1); - - // Initial view - /*if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code - (portapack::persistent_memory::ui_config() & 16)) { // Login option - navigation_view.push(); - } else {*/ - - navigation_view.push(); - - File pmem_flag_file_handle ; - std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ; - auto result = pmem_flag_file_handle.open(pmem_flag_file); - if(!result.is_valid()) - { - portapack::persistent_memory::load_persistent_settings_from_file("SETTINGS/pmem_settings"); - } - - if (portapack::persistent_memory::config_splash()) - { - navigation_view.push(); - } - status_view.set_back_enabled(false); - status_view.set_title_image_enabled(true); - status_view.set_dirty(); - //else - // navigation_view.push(); - - //} + Context& context, + const Rect parent_rect) + : View{parent_rect}, + context_(context) { + set_style(&style_default); + + constexpr ui::Dim status_view_height = 16; + constexpr ui::Dim info_view_height = 16; + + add_child(&status_view); + status_view.set_parent_rect({{0, 0}, + {parent_rect.width(), status_view_height}}); + status_view.on_back = [this]() { + this->navigation_view.pop(); + }; + + add_child(&navigation_view); + navigation_view.set_parent_rect({{0, status_view_height}, + {parent_rect.width(), static_cast(parent_rect.height() - status_view_height)}}); + + add_child(&info_view); + info_view.set_parent_rect({{0, 19 * 16}, + {parent_rect.width(), info_view_height}}); + + navigation_view.on_view_changed = [this](const View& new_view) { + if (!this->navigation_view.is_top()) { + remove_child(&info_view); + } else { + add_child(&info_view); + info_view.refresh(); + } + + this->status_view.set_back_enabled(!this->navigation_view.is_top()); + this->status_view.set_title_image_enabled(this->navigation_view.is_top()); + this->status_view.set_title(new_view.title()); + this->status_view.set_dirty(); + }; + + // portapack::persistent_memory::set_playdead_sequence(0x8D1); + + // Initial view + /*if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code + (portapack::persistent_memory::ui_config() & 16)) { // Login option + navigation_view.push(); + } else {*/ + + navigation_view.push(); + + File pmem_flag_file_handle; + std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG"; + auto result = pmem_flag_file_handle.open(pmem_flag_file); + if (!result.is_valid()) { + portapack::persistent_memory::load_persistent_settings_from_file("SETTINGS/pmem_settings"); + } + + if (portapack::persistent_memory::config_splash()) { + navigation_view.push(); + } + status_view.set_back_enabled(false); + status_view.set_title_image_enabled(true); + status_view.set_dirty(); + // else + // navigation_view.push(); + + //} } Context& SystemView::context() const { - return context_; + return context_; } void SystemView::toggle_overlay() { - if (overlay_active){ - this->remove_child(&this->overlay); - this->set_dirty(); - shared_memory.request_m4_performance_counter = 0; - } - else{ - this->add_child(&this->overlay); - this->set_dirty(); - shared_memory.request_m4_performance_counter = 1; - shared_memory.m4_cpu_usage = 0; - shared_memory.m4_heap_usage = 0; - shared_memory.m4_stack_usage = 0; - } - - overlay_active = !overlay_active; + if (overlay_active) { + this->remove_child(&this->overlay); + this->set_dirty(); + shared_memory.request_m4_performance_counter = 0; + } else { + this->add_child(&this->overlay); + this->set_dirty(); + shared_memory.request_m4_performance_counter = 1; + shared_memory.m4_cpu_usage = 0; + shared_memory.m4_heap_usage = 0; + shared_memory.m4_stack_usage = 0; + } + + overlay_active = !overlay_active; } void SystemView::paint_overlay() { - static bool last_paint_state = false; - if (overlay_active){ - // paint background only every other second - if ((((chTimeNow()>>10) & 0x01) == 0x01) == last_paint_state) - return; + static bool last_paint_state = false; + if (overlay_active) { + // paint background only every other second + if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state) + return; - last_paint_state = !last_paint_state; - this->overlay.set_dirty(); - } + last_paint_state = !last_paint_state; + this->overlay.set_dirty(); + } } /* ***********************************************************************/ void BMPView::focus() { - button_done.focus(); + button_done.focus(); } BMPView::BMPView(NavigationView& nav) { - add_children({ - &button_done - }); - - button_done.on_select = [this, &nav](Button&){ - nav.pop(); - }; + add_children({&button_done}); + + button_done.on_select = [this, &nav](Button&) { + nav.pop(); + }; } void BMPView::paint(Painter&) { - if(!portapack::display.drawBMP2({ 0, 0 }, "splash.bmp")) - portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false); + if (!portapack::display.drawBMP2({0, 0}, "splash.bmp")) + portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false); } /* NotImplementedView ****************************************************/ /*NotImplementedView::NotImplementedView(NavigationView& nav) { - button_done.on_select = [&nav](Button&){ - nav.pop(); - }; + button_done.on_select = [&nav](Button&){ + nav.pop(); + }; - add_children({ - &text_title, - &button_done, - }); + add_children({ + &text_title, + &button_done, + }); } void NotImplementedView::focus() { - button_done.focus(); + button_done.focus(); }*/ /* ModalMessageView ******************************************************/ ModalMessageView::ModalMessageView( - NavigationView& nav, - const std::string& title, - const std::string& message, - const modal_t type, - const std::function on_choice -) : title_ { title }, - message_ { message }, - type_ { type }, - on_choice_ { on_choice } -{ - if (type == INFO) { - add_child(&button_ok); - - button_ok.on_select = [&nav](Button&){ - nav.pop(); - }; - } else if (type == YESNO) { - add_children({ - &button_yes, - &button_no - }); - - button_yes.on_select = [this, &nav](Button&){ - if (on_choice_) on_choice_(true); - nav.pop(); - }; - button_no.on_select = [this, &nav](Button&){ - if (on_choice_) on_choice_(false); - nav.pop(); - }; - } else if (type == YESCANCEL) { - add_children({ - &button_yes, - &button_no - }); - - button_yes.on_select = [this, &nav](Button&){ - if (on_choice_) on_choice_(true); - nav.pop(); - }; - button_no.on_select = [this, &nav](Button&){ - //if (on_choice_) on_choice_(false); - nav.pop_modal(); - }; - } else { // ABORT - add_child(&button_ok); - - button_ok.on_select = [this, &nav](Button&){ - if (on_choice_) on_choice_(true); - nav.pop_modal(); - }; - } + NavigationView& nav, + const std::string& title, + const std::string& message, + const modal_t type, + const std::function on_choice) + : title_{title}, + message_{message}, + type_{type}, + on_choice_{on_choice} { + if (type == INFO) { + add_child(&button_ok); + + button_ok.on_select = [&nav](Button&) { + nav.pop(); + }; + } else if (type == YESNO) { + add_children({&button_yes, + &button_no}); + + button_yes.on_select = [this, &nav](Button&) { + if (on_choice_) on_choice_(true); + nav.pop(); + }; + button_no.on_select = [this, &nav](Button&) { + if (on_choice_) on_choice_(false); + nav.pop(); + }; + } else if (type == YESCANCEL) { + add_children({&button_yes, + &button_no}); + + button_yes.on_select = [this, &nav](Button&) { + if (on_choice_) on_choice_(true); + nav.pop(); + }; + button_no.on_select = [this, &nav](Button&) { + // if (on_choice_) on_choice_(false); + nav.pop_modal(); + }; + } else { // ABORT + add_child(&button_ok); + + button_ok.on_select = [this, &nav](Button&) { + if (on_choice_) on_choice_(true); + nav.pop_modal(); + }; + } } void ModalMessageView::paint(Painter& painter) { - size_t pos, i = 0, start = 0; - - portapack::display.drawBMP({ 100, 48 }, modal_warning_bmp, false); - - // Terrible... - while ((pos = message_.find("\n", start)) != std::string::npos) { - painter.draw_string( - { 1 * 8, (Coord)(120 + (i * 16)) }, - style(), - message_.substr(start, pos - start) - ); - i++; - start = pos + 1; - } - painter.draw_string( - { 1 * 8, (Coord)(120 + (i * 16)) }, - style(), - message_.substr(start, pos) - ); + size_t pos, i = 0, start = 0; + + portapack::display.drawBMP({100, 48}, modal_warning_bmp, false); + + // Terrible... + while ((pos = message_.find("\n", start)) != std::string::npos) { + painter.draw_string( + {1 * 8, (Coord)(120 + (i * 16))}, + style(), + message_.substr(start, pos - start)); + i++; + start = pos + 1; + } + painter.draw_string( + {1 * 8, (Coord)(120 + (i * 16))}, + style(), + message_.substr(start, pos)); } void ModalMessageView::focus() { - if ((type_ == YESNO) || (type_ == YESCANCEL)) { - button_yes.focus(); - } else { - button_ok.focus(); - } + if ((type_ == YESNO) || (type_ == YESCANCEL)) { + button_yes.focus(); + } else { + button_ok.focus(); + } } } /* namespace ui */ diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 737cd38ec..ba7313fbb 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -46,327 +46,310 @@ using namespace sd_card; -namespace ui -{ - - enum modal_t - { - INFO = 0, - YESNO, - YESCANCEL, - ABORT - }; - - class NavigationView : public View - { - public: - std::function on_view_changed{}; - - NavigationView() = default; - - NavigationView(const NavigationView &) = delete; - NavigationView(NavigationView &&) = delete; - NavigationView &operator=(const NavigationView &) = delete; - NavigationView &operator=(NavigationView &&) = delete; - - bool is_top() const; - - template - T *push(Args &&...args) - { - return reinterpret_cast(push_view(std::unique_ptr(new T(*this, std::forward(args)...)))); - } - - // Pushes a new view under the current on the stack so the current view returns into this new one. - template - void push_under_current(Args &&...args) - { - auto new_view = std::unique_ptr(new T(*this, std::forward(args)...)); - view_stack.insert(view_stack.end() - 1, std::move(new_view)); - } - - template - T *replace(Args &&...args) - { - pop(); - return reinterpret_cast(push_view(std::unique_ptr(new T(*this, std::forward(args)...)))); - } - - void push(View *v); - void replace(View *v); - - void pop(); - void pop_modal(); - - void display_modal(const std::string &title, const std::string &message); - void display_modal(const std::string &title, const std::string &message, const modal_t type, const std::function on_choice = nullptr); - - void focus() override; - - private: - std::vector> view_stack{}; - Widget *modal_view{nullptr}; - - Widget *view() const; - - void free_view(); - void update_view(); - View *push_view(std::unique_ptr new_view); - }; - - class SystemStatusView : public View - { - public: - std::function on_back{}; - - SystemStatusView(NavigationView &nav); - - void set_back_enabled(bool new_value); - void set_title_image_enabled(bool new_value); - void set_title(const std::string new_value); - - private: - static constexpr auto default_title = ""; - - NavigationView &nav_; - - Rectangle backdrop{ - {0 * 8, 0 * 16, 240, 16}, - Color::dark_grey()}; - - ImageButton button_back{ - {0, 0 * 16, 12 * 8, 16}, // Back button also covers the title for easier touch. - &bitmap_icon_previous, - Color::white(), - Color::dark_grey()}; - - Text title{ - {20, 0, 14 * 8, 1 * 16}, - default_title, - }; - - ImageButton button_title{ - {2, 0, 80, 16}, - &bitmap_titlebar_image, - Color::white(), - Color::dark_grey()}; - - ImageButton button_speaker{ - {15 * 8, 0, 2 * 8, 1 * 16}, - &bitmap_icon_speaker_mute, - Color::light_grey(), - Color::dark_grey()}; - - ImageButton button_converter{ - {17 * 8, 0, 2 * 8, 1 * 16}, - &bitmap_icon_upconvert, - Color::light_grey(), - Color::dark_grey() - }; - - ImageButton button_stealth{ - {19 * 8, 0, 2 * 8, 1 * 16}, - &bitmap_icon_stealth, - Color::light_grey(), - Color::dark_grey()}; - - /*ImageButton button_textentry { - { 170, 0, 2 * 8, 1 * 16 }, - &bitmap_icon_unistroke, - Color::white(), - Color::dark_grey() - };*/ - - ImageButton button_camera{ - {21 * 8, 0, 2 * 8, 1 * 16}, - &bitmap_icon_camera, - Color::white(), - Color::dark_grey()}; - - ImageButton button_sleep{ - {23 * 8, 0, 2 * 8, 1 * 16}, - &bitmap_icon_sleep, - Color::white(), - Color::dark_grey()}; - - ImageButton button_bias_tee{ - {25 * 8, 0, 12, 1 * 16}, - &bitmap_icon_biast_off, - Color::light_grey(), - Color::dark_grey()}; - - ImageButton button_clock_status{ - {27 * 8, 0 * 16, 2 * 8, 1 * 16}, - &bitmap_icon_clk_int, - Color::light_grey(), - Color::dark_grey()}; - - SDCardStatusView sd_card_status_view{ - {28 * 8, 0 * 16, 2 * 8, 1 * 16}}; - - void on_converter(); - void on_speaker(); - void on_stealth(); - void on_bias_tee(); - // void on_textentry(); - void on_camera(); - void on_title(); - void refresh(); - void on_clk(); - - MessageHandlerRegistration message_handler_refresh{ - Message::ID::StatusRefresh, - [this](const Message *const p) - { - (void)p; - this->refresh(); - }}; - }; - - class InformationView : public View - { - public: - InformationView(NavigationView &nav); - void refresh(); - - private: - // static constexpr auto version_string = "v1.4.4"; // This is commented out as we are now setting the version via ENV (VERSION_STRING=v1.0.0) - NavigationView &nav_; - - Rectangle backdrop{ - {0, 0 * 16, 240, 16}, - {33, 33, 33}}; - - Text version{ - {2, 0, 11 * 8, 16}, - VERSION_STRING}; - - LiveDateTime ltime{ - {86, 0, 19 * 8, 16}}; - }; - - class BMPView : public View - { - public: - BMPView(NavigationView &nav); - void paint(Painter &) override; - void focus() override; - - private: - Text text_info{ - {4 * 8, 284, 20 * 8, 16}, - "Version " VERSION_STRING}; - - Button button_done{ - {240, 0, 1, 1}, - ""}; - }; - - class ReceiversMenuView : public BtnGridView - { - public: - ReceiversMenuView(NavigationView &nav); - std::string title() const override { return "Receive"; }; - }; - - class TransmittersMenuView : public BtnGridView - { - public: - TransmittersMenuView(NavigationView &nav); - std::string title() const override { return "Transmit"; }; - }; - - class UtilitiesMenuView : public BtnGridView - { - public: - UtilitiesMenuView(NavigationView &nav); - std::string title() const override { return "Utilities"; }; - }; - - class SystemMenuView : public BtnGridView - { - public: - SystemMenuView(NavigationView &nav); - - private: - void hackrf_mode(NavigationView &nav); - }; - - class SystemView : public View - { - public: - SystemView( - Context &context, - const Rect parent_rect); - - Context &context() const override; - void toggle_overlay(); - void paint_overlay(); - - private: - bool overlay_active {false}; - - SystemStatusView status_view{navigation_view}; - InformationView info_view{navigation_view}; - DfuMenu overlay{navigation_view}; - NavigationView navigation_view{}; - Context &context_; - }; - - /*class NotImplementedView : public View { +namespace ui { + +enum modal_t { + INFO = 0, + YESNO, + YESCANCEL, + ABORT +}; + +class NavigationView : public View { + public: + std::function on_view_changed{}; + + NavigationView() = default; + + NavigationView(const NavigationView&) = delete; + NavigationView(NavigationView&&) = delete; + NavigationView& operator=(const NavigationView&) = delete; + NavigationView& operator=(NavigationView&&) = delete; + + bool is_top() const; + + template + T* push(Args&&... args) { + return reinterpret_cast(push_view(std::unique_ptr(new T(*this, std::forward(args)...)))); + } + + // Pushes a new view under the current on the stack so the current view returns into this new one. + template + void push_under_current(Args&&... args) { + auto new_view = std::unique_ptr(new T(*this, std::forward(args)...)); + view_stack.insert(view_stack.end() - 1, std::move(new_view)); + } + + template + T* replace(Args&&... args) { + pop(); + return reinterpret_cast(push_view(std::unique_ptr(new T(*this, std::forward(args)...)))); + } + + void push(View* v); + void replace(View* v); + + void pop(); + void pop_modal(); + + void display_modal(const std::string& title, const std::string& message); + void display_modal(const std::string& title, const std::string& message, const modal_t type, const std::function on_choice = nullptr); + + void focus() override; + + private: + std::vector> view_stack{}; + Widget* modal_view{nullptr}; + + Widget* view() const; + + void free_view(); + void update_view(); + View* push_view(std::unique_ptr new_view); +}; + +class SystemStatusView : public View { + public: + std::function on_back{}; + + SystemStatusView(NavigationView& nav); + + void set_back_enabled(bool new_value); + void set_title_image_enabled(bool new_value); + void set_title(const std::string new_value); + + private: + static constexpr auto default_title = ""; + + NavigationView& nav_; + + Rectangle backdrop{ + {0 * 8, 0 * 16, 240, 16}, + Color::dark_grey()}; + + ImageButton button_back{ + {0, 0 * 16, 12 * 8, 16}, // Back button also covers the title for easier touch. + &bitmap_icon_previous, + Color::white(), + Color::dark_grey()}; + + Text title{ + {20, 0, 14 * 8, 1 * 16}, + default_title, + }; + + ImageButton button_title{ + {2, 0, 80, 16}, + &bitmap_titlebar_image, + Color::white(), + Color::dark_grey()}; + + ImageButton button_speaker{ + {15 * 8, 0, 2 * 8, 1 * 16}, + &bitmap_icon_speaker_mute, + Color::light_grey(), + Color::dark_grey()}; + + ImageButton button_converter{ + {17 * 8, 0, 2 * 8, 1 * 16}, + &bitmap_icon_upconvert, + Color::light_grey(), + Color::dark_grey()}; + + ImageButton button_stealth{ + {19 * 8, 0, 2 * 8, 1 * 16}, + &bitmap_icon_stealth, + Color::light_grey(), + Color::dark_grey()}; + + /*ImageButton button_textentry { + { 170, 0, 2 * 8, 1 * 16 }, + &bitmap_icon_unistroke, + Color::white(), + Color::dark_grey() + };*/ + + ImageButton button_camera{ + {21 * 8, 0, 2 * 8, 1 * 16}, + &bitmap_icon_camera, + Color::white(), + Color::dark_grey()}; + + ImageButton button_sleep{ + {23 * 8, 0, 2 * 8, 1 * 16}, + &bitmap_icon_sleep, + Color::white(), + Color::dark_grey()}; + + ImageButton button_bias_tee{ + {25 * 8, 0, 12, 1 * 16}, + &bitmap_icon_biast_off, + Color::light_grey(), + Color::dark_grey()}; + + ImageButton button_clock_status{ + {27 * 8, 0 * 16, 2 * 8, 1 * 16}, + &bitmap_icon_clk_int, + Color::light_grey(), + Color::dark_grey()}; + + SDCardStatusView sd_card_status_view{ + {28 * 8, 0 * 16, 2 * 8, 1 * 16}}; + + void on_converter(); + void on_speaker(); + void on_stealth(); + void on_bias_tee(); + // void on_textentry(); + void on_camera(); + void on_title(); + void refresh(); + void on_clk(); + + MessageHandlerRegistration message_handler_refresh{ + Message::ID::StatusRefresh, + [this](const Message* const p) { + (void)p; + this->refresh(); + }}; +}; + +class InformationView : public View { + public: + InformationView(NavigationView& nav); + void refresh(); + + private: + // static constexpr auto version_string = "v1.4.4"; // This is commented out as we are now setting the version via ENV (VERSION_STRING=v1.0.0) + NavigationView& nav_; + + Rectangle backdrop{ + {0, 0 * 16, 240, 16}, + {33, 33, 33}}; + + Text version{ + {2, 0, 11 * 8, 16}, + VERSION_STRING}; + + LiveDateTime ltime{ + {86, 0, 19 * 8, 16}}; +}; + +class BMPView : public View { + public: + BMPView(NavigationView& nav); + void paint(Painter&) override; + void focus() override; + + private: + Text text_info{ + {4 * 8, 284, 20 * 8, 16}, + "Version " VERSION_STRING}; + + Button button_done{ + {240, 0, 1, 1}, + ""}; +}; + +class ReceiversMenuView : public BtnGridView { + public: + ReceiversMenuView(NavigationView& nav); + std::string title() const override { return "Receive"; }; +}; + +class TransmittersMenuView : public BtnGridView { + public: + TransmittersMenuView(NavigationView& nav); + std::string title() const override { return "Transmit"; }; +}; + +class UtilitiesMenuView : public BtnGridView { + public: + UtilitiesMenuView(NavigationView& nav); + std::string title() const override { return "Utilities"; }; +}; + +class SystemMenuView : public BtnGridView { + public: + SystemMenuView(NavigationView& nav); + + private: + void hackrf_mode(NavigationView& nav); +}; + +class SystemView : public View { + public: + SystemView( + Context& context, + const Rect parent_rect); + + Context& context() const override; + void toggle_overlay(); + void paint_overlay(); + + private: + bool overlay_active{false}; + + SystemStatusView status_view{navigation_view}; + InformationView info_view{navigation_view}; + DfuMenu overlay{navigation_view}; + NavigationView navigation_view{}; + Context& context_; +}; + +/*class NotImplementedView : public View { public: - NotImplementedView(NavigationView& nav); + NotImplementedView(NavigationView& nav); - void focus() override; + void focus() override; private: - Text text_title { - { 5 * 8, 7 * 16, 19 * 8, 16 }, - "Not Yet Implemented" - }; - - Button button_done { - { 10 * 8, 13 * 16, 10 * 8, 24 }, - "Bummer", - }; + Text text_title { + { 5 * 8, 7 * 16, 19 * 8, 16 }, + "Not Yet Implemented" + }; + + Button button_done { + { 10 * 8, 13 * 16, 10 * 8, 24 }, + "Bummer", + }; };*/ - class ModalMessageView : public View - { - public: - ModalMessageView( - NavigationView &nav, - const std::string &title, - const std::string &message, - const modal_t type, - const std::function on_choice); - - void paint(Painter &painter) override; - void focus() override; - - std::string title() const override { return title_; }; - - private: - const std::string title_; - const std::string message_; - const modal_t type_; - const std::function on_choice_; - - Button button_ok{ - {10 * 8, 14 * 16, 10 * 8, 48}, - "OK", - }; - - Button button_yes{ - {5 * 8, 14 * 16, 8 * 8, 48}, - "YES", - }; - - Button button_no{ - {17 * 8, 14 * 16, 8 * 8, 48}, - "NO", - }; - }; +class ModalMessageView : public View { + public: + ModalMessageView( + NavigationView& nav, + const std::string& title, + const std::string& message, + const modal_t type, + const std::function on_choice); + + void paint(Painter& painter) override; + void focus() override; + + std::string title() const override { return title_; }; + + private: + const std::string title_; + const std::string message_; + const modal_t type_; + const std::function on_choice_; + + Button button_ok{ + {10 * 8, 14 * 16, 10 * 8, 48}, + "OK", + }; + + Button button_yes{ + {5 * 8, 14 * 16, 8 * 8, 48}, + "YES", + }; + + Button button_no{ + {17 * 8, 14 * 16, 8 * 8, 48}, + "NO", + }; +}; } /* namespace ui */ diff --git a/firmware/application/ui_playdead.cpp b/firmware/application/ui_playdead.cpp index 83537c9de..adf4a2ae1 100644 --- a/firmware/application/ui_playdead.cpp +++ b/firmware/application/ui_playdead.cpp @@ -27,57 +27,56 @@ using namespace portapack; namespace ui { - + void PlayDeadView::focus() { - button_seq_entry.focus(); + button_seq_entry.focus(); } void PlayDeadView::paint(Painter& painter) { - if (persistent_memory::config_login()) { - // Blank the whole display - painter.fill_rectangle( - display.screen_rect(), - style().background - ); - } + if (persistent_memory::config_login()) { + // Blank the whole display + painter.fill_rectangle( + display.screen_rect(), + style().background); + } } PlayDeadView::PlayDeadView(NavigationView& nav) { - rtc::RTC datetime; - - persistent_memory::set_playing_dead(0x5920C1DF); // Enable - - add_children({ - &text_playdead1, - &text_playdead2, - &text_playdead3, - &button_seq_entry, - }); - - // Seed from RTC - rtcGetTime(&RTCD1, &datetime); - text_playdead2.set("0x" + to_string_hex(lfsr_iterate(datetime.second()), 6) + "00"); - - text_playdead3.hidden(true); - - button_seq_entry.on_dir = [this](Button&, KeyEvent key){ - sequence = (sequence << 3) | (static_cast::type>(key) + 1); - return true; - }; - - button_seq_entry.on_select = [this, &nav](Button&){ - if (sequence == persistent_memory::playdead_sequence()) { - persistent_memory::set_playing_dead(0x82175E23); // Disable - if (persistent_memory::config_login()) { - text_playdead3.hidden(false); - } else { - nav.pop(); - nav.push(); - } - } else { - sequence = 0; - } - }; + rtc::RTC datetime; + + persistent_memory::set_playing_dead(0x5920C1DF); // Enable + + add_children({ + &text_playdead1, + &text_playdead2, + &text_playdead3, + &button_seq_entry, + }); + + // Seed from RTC + rtcGetTime(&RTCD1, &datetime); + text_playdead2.set("0x" + to_string_hex(lfsr_iterate(datetime.second()), 6) + "00"); + + text_playdead3.hidden(true); + + button_seq_entry.on_dir = [this](Button&, KeyEvent key) { + sequence = (sequence << 3) | (static_cast::type>(key) + 1); + return true; + }; + + button_seq_entry.on_select = [this, &nav](Button&) { + if (sequence == persistent_memory::playdead_sequence()) { + persistent_memory::set_playing_dead(0x82175E23); // Disable + if (persistent_memory::config_login()) { + text_playdead3.hidden(false); + } else { + nav.pop(); + nav.push(); + } + } else { + sequence = 0; + } + }; } } /* namespace ui */ diff --git a/firmware/application/ui_playdead.hpp b/firmware/application/ui_playdead.hpp index 2169dbe68..394647e58 100644 --- a/firmware/application/ui_playdead.hpp +++ b/firmware/application/ui_playdead.hpp @@ -29,34 +29,32 @@ namespace ui { class PlayDeadView : public View { -public: - PlayDeadView(NavigationView& nav); - - void focus() override; - void paint(Painter& painter) override; - -private: - uint32_t sequence = 0; - - Text text_playdead1 { - { 6 * 8, 7 * 16, 14 * 8, 16 }, - "\x46irmwa" "re " "er\x72o\x72" - }; - Text text_playdead2 { - { 6 * 8, 9 * 16, 16 * 8, 16 }, - "" - }; - Text text_playdead3 { - { 6 * 8, 12 * 16, 16 * 8, 16 }, - "Please reset" - }; - - Button button_seq_entry { - { 240, 0, 1, 1 }, - "" - }; + public: + PlayDeadView(NavigationView& nav); + + void focus() override; + void paint(Painter& painter) override; + + private: + uint32_t sequence = 0; + + Text text_playdead1{ + {6 * 8, 7 * 16, 14 * 8, 16}, + "\x46irmwa" + "re " + "er\x72o\x72"}; + Text text_playdead2{ + {6 * 8, 9 * 16, 16 * 8, 16}, + ""}; + Text text_playdead3{ + {6 * 8, 12 * 16, 16 * 8, 16}, + "Please reset"}; + + Button button_seq_entry{ + {240, 0, 1, 1}, + ""}; }; } /* namespace ui */ -#endif/*__UI_PLAYDEAD_H__*/ +#endif /*__UI_PLAYDEAD_H__*/ diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 1a3c3df39..eaf9fc524 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -37,283 +37,275 @@ using namespace portapack; namespace ui { /*void RecordView::toggle_pitch_rssi() { - pitch_rssi_enabled = !pitch_rssi_enabled; - - // Send to RSSI widget - const PitchRSSIConfigureMessage message { - pitch_rssi_enabled, - 0 - }; - shared_memory.application_queue.push(message); - - if( !pitch_rssi_enabled ) { - button_pitch_rssi.set_foreground(Color::orange()); - } else { - button_pitch_rssi.set_foreground(Color::green()); - } + pitch_rssi_enabled = !pitch_rssi_enabled; + + // Send to RSSI widget + const PitchRSSIConfigureMessage message { + pitch_rssi_enabled, + 0 + }; + shared_memory.application_queue.push(message); + + if( !pitch_rssi_enabled ) { + button_pitch_rssi.set_foreground(Color::orange()); + } else { + button_pitch_rssi.set_foreground(Color::green()); + } }*/ RecordView::RecordView( - const Rect parent_rect, - const std::filesystem::path& filename_stem_pattern, - const std::filesystem::path& folder, - const FileType file_type, - const size_t write_size, - const size_t buffer_count -) : View { parent_rect }, - filename_stem_pattern { filename_stem_pattern }, - folder { folder }, - file_type { file_type }, - write_size { write_size }, - buffer_count { buffer_count } -{ - ensure_directory(folder); - add_children({ - &rect_background, - //&button_pitch_rssi, - &button_record, - &text_record_filename, - &text_record_dropped, - &text_time_available, - }); - - rect_background.set_parent_rect({ { 0, 0 }, size() }); - - /*button_pitch_rssi.on_select = [this](ImageButton&) { - this->toggle_pitch_rssi(); - };*/ - - button_record.on_select = [this](ImageButton&) { - this->toggle(); - }; - - signal_token_tick_second = rtc_time::signal_tick_second += [this]() { - this->on_tick_second(); - }; + const Rect parent_rect, + const std::filesystem::path& filename_stem_pattern, + const std::filesystem::path& folder, + const FileType file_type, + const size_t write_size, + const size_t buffer_count) + : View{parent_rect}, + filename_stem_pattern{filename_stem_pattern}, + folder{folder}, + file_type{file_type}, + write_size{write_size}, + buffer_count{buffer_count} { + ensure_directory(folder); + add_children({ + &rect_background, + //&button_pitch_rssi, + &button_record, + &text_record_filename, + &text_record_dropped, + &text_time_available, + }); + + rect_background.set_parent_rect({{0, 0}, size()}); + + /*button_pitch_rssi.on_select = [this](ImageButton&) { + this->toggle_pitch_rssi(); + };*/ + + button_record.on_select = [this](ImageButton&) { + this->toggle(); + }; + + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; } RecordView::~RecordView() { - rtc_time::signal_tick_second -= signal_token_tick_second; + rtc_time::signal_tick_second -= signal_token_tick_second; } void RecordView::focus() { - button_record.focus(); + button_record.focus(); } void RecordView::set_sampling_rate(const size_t new_sampling_rate) { - - /* We are changing "REC" icon background to yellow in BW rec Options >600kHz - where we are NOT recording full IQ .C16 files (recorded files are decimated ones). - Those decimated recorded files,has not the full IQ samples . - are ok as recorded spectrum indication, but they should not be used by Replay app. - - We keep original black background in all the correct IQ .C16 files BW's Options */ - if (new_sampling_rate > 4800000) { // > BW >600kHz (fs=8*BW), (750kHz ...2750kHz) - button_record.set_background(ui::Color::yellow()); - } else { - button_record.set_background(ui::Color::black()); - } - - if( new_sampling_rate != sampling_rate ) { - stop(); - - sampling_rate = new_sampling_rate; - baseband::set_sample_rate(sampling_rate); - - button_record.hidden(sampling_rate == 0); - text_record_filename.hidden(sampling_rate == 0); - text_record_dropped.hidden(sampling_rate == 0); - text_time_available.hidden(sampling_rate == 0); - rect_background.hidden(sampling_rate != 0); - - update_status_display(); - } + /* We are changing "REC" icon background to yellow in BW rec Options >600kHz + where we are NOT recording full IQ .C16 files (recorded files are decimated ones). + Those decimated recorded files,has not the full IQ samples . + are ok as recorded spectrum indication, but they should not be used by Replay app. + + We keep original black background in all the correct IQ .C16 files BW's Options */ + if (new_sampling_rate > 4800000) { // > BW >600kHz (fs=8*BW), (750kHz ...2750kHz) + button_record.set_background(ui::Color::yellow()); + } else { + button_record.set_background(ui::Color::black()); + } + + if (new_sampling_rate != sampling_rate) { + stop(); + + sampling_rate = new_sampling_rate; + baseband::set_sample_rate(sampling_rate); + + button_record.hidden(sampling_rate == 0); + text_record_filename.hidden(sampling_rate == 0); + text_record_dropped.hidden(sampling_rate == 0); + text_time_available.hidden(sampling_rate == 0); + rect_background.hidden(sampling_rate != 0); + + update_status_display(); + } } // Setter for datetime and frequency filename void RecordView::set_filename_date_frequency(bool set) { - filename_date_frequency = set; + filename_date_frequency = set; } bool RecordView::is_active() const { - return (bool)capture_thread; + return (bool)capture_thread; } void RecordView::toggle() { - if( is_active() ) { - stop(); - } else { - start(); - } + if (is_active()) { + stop(); + } else { + start(); + } } void RecordView::start() { - stop(); + stop(); - text_record_filename.set(""); - text_record_dropped.set(""); + text_record_filename.set(""); + text_record_dropped.set(""); - if( sampling_rate == 0 ) { - return; - } + if (sampling_rate == 0) { + return; + } std::filesystem::path base_path; - if(filename_date_frequency) { - rtcGetTime(&RTCD1, &datetime); - - //ISO 8601 - std::string date_time = to_string_dec_uint(datetime.year(), 4, '0') + - to_string_dec_uint(datetime.month(), 2, '0') + - to_string_dec_uint(datetime.day(), 2, '0') + "T" + - to_string_dec_uint(datetime.hour()) + - to_string_dec_uint(datetime.minute()) + - to_string_dec_uint(datetime.second()); - - base_path = filename_stem_pattern.string() + "_" + date_time + "_" + - trim(to_string_freq(receiver_model.tuning_frequency())) + "Hz"; - base_path = folder / base_path; - } else { - base_path = next_filename_matching_pattern(folder / filename_stem_pattern); - } - - if( base_path.empty() ) { - return; - } - - std::unique_ptr writer; - switch(file_type) { - case FileType::WAV: - { - auto p = std::make_unique(); - auto create_error = p->create( - base_path.replace_extension(u".WAV"), - sampling_rate, - to_string_dec_uint(receiver_model.tuning_frequency()) + "Hz" - ); - if( create_error.is_valid() ) { - handle_error(create_error.value()); - } else { - writer = std::move(p); - } - } - break; - - case FileType::RawS16: - { - const auto metadata_file_error = write_metadata_file(base_path.replace_extension(u".TXT")); - if( metadata_file_error.is_valid() ) { - handle_error(metadata_file_error.value()); - return; - } - - auto p = std::make_unique(); - auto create_error = p->create(base_path.replace_extension(u".C16")); - if( create_error.is_valid() ) { - handle_error(create_error.value()); - } else { - writer = std::move(p); - } - } - break; - - default: - break; - }; - - if( writer ) { - text_record_filename.set(truncate(base_path.filename().string(), 8)); - button_record.set_bitmap(&bitmap_stop); - capture_thread = std::make_unique( - std::move(writer), - write_size, buffer_count, - []() { - CaptureThreadDoneMessage message { }; - EventDispatcher::send_message(message); - }, - [](File::Error error) { - CaptureThreadDoneMessage message { error.code() }; - EventDispatcher::send_message(message); - } - ); - } - - update_status_display(); + if (filename_date_frequency) { + rtcGetTime(&RTCD1, &datetime); + + // ISO 8601 + std::string date_time = to_string_dec_uint(datetime.year(), 4, '0') + + to_string_dec_uint(datetime.month(), 2, '0') + + to_string_dec_uint(datetime.day(), 2, '0') + "T" + + to_string_dec_uint(datetime.hour()) + + to_string_dec_uint(datetime.minute()) + + to_string_dec_uint(datetime.second()); + + base_path = filename_stem_pattern.string() + "_" + date_time + "_" + + trim(to_string_freq(receiver_model.tuning_frequency())) + "Hz"; + base_path = folder / base_path; + } else { + base_path = next_filename_matching_pattern(folder / filename_stem_pattern); + } + + if (base_path.empty()) { + return; + } + + std::unique_ptr writer; + switch (file_type) { + case FileType::WAV: { + auto p = std::make_unique(); + auto create_error = p->create( + base_path.replace_extension(u".WAV"), + sampling_rate, + to_string_dec_uint(receiver_model.tuning_frequency()) + "Hz"); + if (create_error.is_valid()) { + handle_error(create_error.value()); + } else { + writer = std::move(p); + } + } break; + + case FileType::RawS16: { + const auto metadata_file_error = write_metadata_file(base_path.replace_extension(u".TXT")); + if (metadata_file_error.is_valid()) { + handle_error(metadata_file_error.value()); + return; + } + + auto p = std::make_unique(); + auto create_error = p->create(base_path.replace_extension(u".C16")); + if (create_error.is_valid()) { + handle_error(create_error.value()); + } else { + writer = std::move(p); + } + } break; + + default: + break; + }; + + if (writer) { + text_record_filename.set(truncate(base_path.filename().string(), 8)); + button_record.set_bitmap(&bitmap_stop); + capture_thread = std::make_unique( + std::move(writer), + write_size, buffer_count, + []() { + CaptureThreadDoneMessage message{}; + EventDispatcher::send_message(message); + }, + [](File::Error error) { + CaptureThreadDoneMessage message{error.code()}; + EventDispatcher::send_message(message); + }); + } + + update_status_display(); } void RecordView::on_hide() { - stop(); // Stop current recording - View::on_hide(); + stop(); // Stop current recording + View::on_hide(); } void RecordView::stop() { - if( is_active() ) { - capture_thread.reset(); - button_record.set_bitmap(&bitmap_record); - } + if (is_active()) { + capture_thread.reset(); + button_record.set_bitmap(&bitmap_record); + } - update_status_display(); + update_status_display(); } Optional RecordView::write_metadata_file(const std::filesystem::path& filename) { - File file; - const auto create_error = file.create(filename); - if( create_error.is_valid() ) { - return create_error; - } else { - const auto error_line1 = file.write_line("sample_rate=" + to_string_dec_uint(sampling_rate / 8)); - if( error_line1.is_valid() ) { - return error_line1; - } - const auto error_line2 = file.write_line("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency())); - if( error_line2.is_valid() ) { - return error_line2; - } - return { }; - } + File file; + const auto create_error = file.create(filename); + if (create_error.is_valid()) { + return create_error; + } else { + const auto error_line1 = file.write_line("sample_rate=" + to_string_dec_uint(sampling_rate / 8)); + if (error_line1.is_valid()) { + return error_line1; + } + const auto error_line2 = file.write_line("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency())); + if (error_line2.is_valid()) { + return error_line2; + } + return {}; + } } void RecordView::on_tick_second() { - update_status_display(); + update_status_display(); } void RecordView::update_status_display() { - if( is_active() ) { - const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent()); - const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "%"; - text_record_dropped.set(s); - } - - /*if (pitch_rssi_enabled) { - button_pitch_rssi.invert_colors(); - }*/ - - if( sampling_rate ) { - const auto space_info = std::filesystem::space(u""); - const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate / 8 * 4); - const uint32_t available_seconds = space_info.free / bytes_per_second; - const uint32_t seconds = available_seconds % 60; - const uint32_t available_minutes = available_seconds / 60; - const uint32_t minutes = available_minutes % 60; - const uint32_t hours = available_minutes / 60; - const std::string available_time = - to_string_dec_uint(hours, 3, ' ') + ":" + - to_string_dec_uint(minutes, 2, '0') + ":" + - to_string_dec_uint(seconds, 2, '0'); - text_time_available.set(available_time); - } + if (is_active()) { + const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent()); + const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "%"; + text_record_dropped.set(s); + } + + /*if (pitch_rssi_enabled) { + button_pitch_rssi.invert_colors(); + }*/ + + if (sampling_rate) { + const auto space_info = std::filesystem::space(u""); + const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate / 8 * 4); + const uint32_t available_seconds = space_info.free / bytes_per_second; + const uint32_t seconds = available_seconds % 60; + const uint32_t available_minutes = available_seconds / 60; + const uint32_t minutes = available_minutes % 60; + const uint32_t hours = available_minutes / 60; + const std::string available_time = + to_string_dec_uint(hours, 3, ' ') + ":" + + to_string_dec_uint(minutes, 2, '0') + ":" + + to_string_dec_uint(seconds, 2, '0'); + text_time_available.set(available_time); + } } void RecordView::handle_capture_thread_done(const File::Error error) { - stop(); - if( error.code() ) { - handle_error(error); - } + stop(); + if (error.code()) { + handle_error(error); + } } void RecordView::handle_error(const File::Error error) { - if( on_error ) { - on_error(error.what()); - } + if (on_error) { + on_error(error.what()); + } } } /* namespace ui */ diff --git a/firmware/application/ui_record_view.hpp b/firmware/application/ui_record_view.hpp index 241e8d559..ec3b56e95 100644 --- a/firmware/application/ui_record_view.hpp +++ b/firmware/application/ui_record_view.hpp @@ -36,106 +36,102 @@ namespace ui { class RecordView : public View { -public: - std::function on_error { }; - - enum FileType { - RawS16 = 2, - WAV = 3, - }; - - RecordView( - const Rect parent_rect, - const std::filesystem::path& filename_stem_pattern, - const std::filesystem::path& folder, - FileType file_type, - const size_t write_size, - const size_t buffer_count - ); - ~RecordView(); - - void focus() override; - - void set_sampling_rate(const size_t new_sampling_rate); - - void start(); - void stop(); - void on_hide() override; - - bool is_active() const; - - void set_filename_date_frequency(bool set); - -private: - void toggle(); - //void toggle_pitch_rssi(); - Optional write_metadata_file(const std::filesystem::path& filename); - - void on_tick_second(); - void update_status_display(); - - void handle_capture_thread_done(const File::Error error); - void handle_error(const File::Error error); - - //bool pitch_rssi_enabled = false; - - // Time Stamp - bool filename_date_frequency = false; - rtc::RTC datetime { }; - - const std::filesystem::path filename_stem_pattern; - const std::filesystem::path folder; - const FileType file_type; - const size_t write_size; - const size_t buffer_count; - size_t sampling_rate { 0 }; - SignalToken signal_token_tick_second { }; - - Rectangle rect_background { - Color::black() - }; - - /*ImageButton button_pitch_rssi { - { 2, 0 * 16, 3 * 8, 1 * 16 }, - &bitmap_rssipwm, - Color::orange(), - Color::black() - };*/ - - ImageButton button_record { - //{ 4 * 8, 0 * 16, 2 * 8, 1 * 16 }, - { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, - &bitmap_record, - Color::red(), - Color::black() - }; - - Text text_record_filename { - { 7 * 8, 0 * 16, 8 * 8, 16 }, - "", - }; - - Text text_record_dropped { - { 16 * 8, 0 * 16, 3 * 8, 16 }, - "", - }; - - Text text_time_available { - { 21 * 8, 0 * 16, 9 * 8, 16 }, - "", - }; - - std::unique_ptr capture_thread { }; - - MessageHandlerRegistration message_handler_capture_thread_error { - Message::ID::CaptureThreadDone, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_capture_thread_done(message.error); - } - }; + public: + std::function on_error{}; + + enum FileType { + RawS16 = 2, + WAV = 3, + }; + + RecordView( + const Rect parent_rect, + const std::filesystem::path& filename_stem_pattern, + const std::filesystem::path& folder, + FileType file_type, + const size_t write_size, + const size_t buffer_count); + ~RecordView(); + + void focus() override; + + void set_sampling_rate(const size_t new_sampling_rate); + + void start(); + void stop(); + void on_hide() override; + + bool is_active() const; + + void set_filename_date_frequency(bool set); + + private: + void toggle(); + // void toggle_pitch_rssi(); + Optional write_metadata_file(const std::filesystem::path& filename); + + void on_tick_second(); + void update_status_display(); + + void handle_capture_thread_done(const File::Error error); + void handle_error(const File::Error error); + + // bool pitch_rssi_enabled = false; + + // Time Stamp + bool filename_date_frequency = false; + rtc::RTC datetime{}; + + const std::filesystem::path filename_stem_pattern; + const std::filesystem::path folder; + const FileType file_type; + const size_t write_size; + const size_t buffer_count; + size_t sampling_rate{0}; + SignalToken signal_token_tick_second{}; + + Rectangle rect_background{ + Color::black()}; + + /*ImageButton button_pitch_rssi { + { 2, 0 * 16, 3 * 8, 1 * 16 }, + &bitmap_rssipwm, + Color::orange(), + Color::black() + };*/ + + ImageButton button_record{ + //{ 4 * 8, 0 * 16, 2 * 8, 1 * 16 }, + {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + &bitmap_record, + Color::red(), + Color::black()}; + + Text text_record_filename{ + {7 * 8, 0 * 16, 8 * 8, 16}, + "", + }; + + Text text_record_dropped{ + {16 * 8, 0 * 16, 3 * 8, 16}, + "", + }; + + Text text_time_available{ + {21 * 8, 0 * 16, 9 * 8, 16}, + "", + }; + + std::unique_ptr capture_thread{}; + + MessageHandlerRegistration message_handler_capture_thread_error{ + Message::ID::CaptureThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_capture_thread_done(message.error); + }}; }; } /* namespace ui */ -#endif/*__UI_RECORD_VIEW_H__*/ +#endif /*__UI_RECORD_VIEW_H__*/ diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index 4c54ff9c2..13363727c 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -33,410 +33,418 @@ #include "hal.h" class SDCardTestThread { -public: - enum Result { - FailCompare = -8, - FailReadIncomplete = -7, - FailWriteIncomplete = -6, - FailAbort = -5, - FailFileOpenRead = -4, - FailFileOpenWrite = -3, - FailHeap = -2, - FailThread = -1, - Incomplete = 0, - OK = 1, - }; - std::string ResultStr[10] = { - "Compare", - "Read incomplete", - "Write incomplete", - "Abort", - "File Open Read", - "File Open Write", - "Heap", - "Thread", - "Incomplete", - "OK", - }; - - struct Stats { - halrtcnt_t write_duration_min { 0 }; - halrtcnt_t write_duration_max { 0 }; - halrtcnt_t write_test_duration { 0 }; - File::Size write_bytes { 0 }; - size_t write_count { 0 }; - - halrtcnt_t read_duration_min { 0 }; - halrtcnt_t read_duration_max { 0 }; - halrtcnt_t read_test_duration { 0 }; - File::Size read_bytes { 0 }; - size_t read_count { 0 }; - }; - - SDCardTestThread( - ) { - thread = chThdCreateFromHeap(NULL, 3072, NORMALPRIO + 10, SDCardTestThread::static_fn, this); - } - - Result result() const { - return _result; - } - - const Stats& stats() const { - return _stats; - } - - ~SDCardTestThread() { - chThdTerminate(thread); - chThdWait(thread); - } - -private: - static constexpr File::Size write_size = 16384; - static constexpr File::Size bytes_to_write = 16 * 1024 * 1024; - static constexpr File::Size bytes_to_read = bytes_to_write; - - static Thread* thread; - volatile Result _result { Result::Incomplete }; - Stats _stats { }; - - static msg_t static_fn(void* arg) { - auto obj = static_cast(arg); - obj->_result = obj->run(); - return 0; - } - - Result run() { - const std::filesystem::path filename { u"_PPTEST_.DAT" }; - - const auto write_result = write(filename); - if( write_result != Result::OK ) { - return write_result; - } - - if( _stats.write_bytes < bytes_to_write ) { - return Result::FailWriteIncomplete; - } - - if( chThdShouldTerminate() ) { - return Result::FailAbort; - } - - const auto read_result = read(filename); - if( read_result != Result::OK ) { - return read_result; - } - - f_unlink(reinterpret_cast(filename.c_str())); - - if( _stats.read_bytes < bytes_to_read ) { - return Result::FailReadIncomplete; - } - - if( chThdShouldTerminate() ) { - return Result::FailAbort; - } - - return Result::OK; - } - - Result write(const std::filesystem::path& filename) { - const auto buffer = std::make_unique>(); - if( !buffer ) { - return Result::FailHeap; - } - - File file; - auto file_create_error = file.create(filename); - if( file_create_error.is_valid() ) { - return Result::FailFileOpenWrite; - } - - lfsr_word_t v = 1; - - const halrtcnt_t test_start = halGetCounterValue(); - while( !chThdShouldTerminate() && (_stats.write_bytes < bytes_to_write) ) { - lfsr_fill(v, - reinterpret_cast(buffer->data()), - sizeof(*buffer.get()) / sizeof(lfsr_word_t) - ); - - const halrtcnt_t write_start = halGetCounterValue(); - const auto result_write = file.write(buffer->data(), buffer->size()); - if( result_write.is_error() ) { - break; - } - const halrtcnt_t write_end = halGetCounterValue(); - _stats.write_bytes += buffer->size(); - _stats.write_count++; - - const halrtcnt_t write_duration = write_end - write_start; - if( (_stats.write_duration_min == 0) || (write_duration < _stats.write_duration_min) ) { - _stats.write_duration_min = write_duration; - } - if( write_duration > _stats.write_duration_max ) { - _stats.write_duration_max = write_duration; - } - } - - file.sync(); - - const halrtcnt_t test_end = halGetCounterValue(); - _stats.write_test_duration = test_end - test_start; - - return Result::OK; - } - - Result read(const std::filesystem::path& filename) { - const auto buffer = std::make_unique>(); - if( !buffer ) { - return Result::FailHeap; - } - - File file; - auto file_open_error = file.open(filename); - if( file_open_error.is_valid() ) { - return Result::FailFileOpenRead; - } - - lfsr_word_t v = 1; - - const halrtcnt_t test_start = halGetCounterValue(); - while( !chThdShouldTerminate() && (_stats.read_bytes < bytes_to_read) ) { - const halrtcnt_t read_start = halGetCounterValue(); - const auto result_read = file.read(buffer->data(), buffer->size()); - if( result_read.is_error() ) { - break; - } - const halrtcnt_t read_end = halGetCounterValue(); - _stats.read_bytes += buffer->size(); - _stats.read_count++; - - const halrtcnt_t read_duration = read_end - read_start; - if( (_stats.read_duration_min == 0) || (read_duration < _stats.read_duration_min) ) { - _stats.read_duration_min = read_duration; - } - if( read_duration > _stats.read_duration_max ) { - _stats.read_duration_max = read_duration; - } - - if( !lfsr_compare(v, - reinterpret_cast(buffer->data()), - sizeof(*buffer.get()) / sizeof(lfsr_word_t)) - ) { - return Result::FailCompare; - } - } - - file.sync(); - - const halrtcnt_t test_end = halGetCounterValue(); - _stats.read_test_duration = test_end - test_start; - - return Result::OK; - } + public: + enum Result { + FailCompare = -8, + FailReadIncomplete = -7, + FailWriteIncomplete = -6, + FailAbort = -5, + FailFileOpenRead = -4, + FailFileOpenWrite = -3, + FailHeap = -2, + FailThread = -1, + Incomplete = 0, + OK = 1, + }; + std::string ResultStr[10] = { + "Compare", + "Read incomplete", + "Write incomplete", + "Abort", + "File Open Read", + "File Open Write", + "Heap", + "Thread", + "Incomplete", + "OK", + }; + + struct Stats { + halrtcnt_t write_duration_min{0}; + halrtcnt_t write_duration_max{0}; + halrtcnt_t write_test_duration{0}; + File::Size write_bytes{0}; + size_t write_count{0}; + + halrtcnt_t read_duration_min{0}; + halrtcnt_t read_duration_max{0}; + halrtcnt_t read_test_duration{0}; + File::Size read_bytes{0}; + size_t read_count{0}; + }; + + SDCardTestThread() { + thread = chThdCreateFromHeap(NULL, 3072, NORMALPRIO + 10, SDCardTestThread::static_fn, this); + } + + Result result() const { + return _result; + } + + const Stats& stats() const { + return _stats; + } + + ~SDCardTestThread() { + chThdTerminate(thread); + chThdWait(thread); + } + + private: + static constexpr File::Size write_size = 16384; + static constexpr File::Size bytes_to_write = 16 * 1024 * 1024; + static constexpr File::Size bytes_to_read = bytes_to_write; + + static Thread* thread; + volatile Result _result{Result::Incomplete}; + Stats _stats{}; + + static msg_t static_fn(void* arg) { + auto obj = static_cast(arg); + obj->_result = obj->run(); + return 0; + } + + Result run() { + const std::filesystem::path filename{u"_PPTEST_.DAT"}; + + const auto write_result = write(filename); + if (write_result != Result::OK) { + return write_result; + } + + if (_stats.write_bytes < bytes_to_write) { + return Result::FailWriteIncomplete; + } + + if (chThdShouldTerminate()) { + return Result::FailAbort; + } + + const auto read_result = read(filename); + if (read_result != Result::OK) { + return read_result; + } + + f_unlink(reinterpret_cast(filename.c_str())); + + if (_stats.read_bytes < bytes_to_read) { + return Result::FailReadIncomplete; + } + + if (chThdShouldTerminate()) { + return Result::FailAbort; + } + + return Result::OK; + } + + Result write(const std::filesystem::path& filename) { + const auto buffer = std::make_unique>(); + if (!buffer) { + return Result::FailHeap; + } + + File file; + auto file_create_error = file.create(filename); + if (file_create_error.is_valid()) { + return Result::FailFileOpenWrite; + } + + lfsr_word_t v = 1; + + const halrtcnt_t test_start = halGetCounterValue(); + while (!chThdShouldTerminate() && (_stats.write_bytes < bytes_to_write)) { + lfsr_fill(v, + reinterpret_cast(buffer->data()), + sizeof(*buffer.get()) / sizeof(lfsr_word_t)); + + const halrtcnt_t write_start = halGetCounterValue(); + const auto result_write = file.write(buffer->data(), buffer->size()); + if (result_write.is_error()) { + break; + } + const halrtcnt_t write_end = halGetCounterValue(); + _stats.write_bytes += buffer->size(); + _stats.write_count++; + + const halrtcnt_t write_duration = write_end - write_start; + if ((_stats.write_duration_min == 0) || (write_duration < _stats.write_duration_min)) { + _stats.write_duration_min = write_duration; + } + if (write_duration > _stats.write_duration_max) { + _stats.write_duration_max = write_duration; + } + } + + file.sync(); + + const halrtcnt_t test_end = halGetCounterValue(); + _stats.write_test_duration = test_end - test_start; + + return Result::OK; + } + + Result read(const std::filesystem::path& filename) { + const auto buffer = std::make_unique>(); + if (!buffer) { + return Result::FailHeap; + } + + File file; + auto file_open_error = file.open(filename); + if (file_open_error.is_valid()) { + return Result::FailFileOpenRead; + } + + lfsr_word_t v = 1; + + const halrtcnt_t test_start = halGetCounterValue(); + while (!chThdShouldTerminate() && (_stats.read_bytes < bytes_to_read)) { + const halrtcnt_t read_start = halGetCounterValue(); + const auto result_read = file.read(buffer->data(), buffer->size()); + if (result_read.is_error()) { + break; + } + const halrtcnt_t read_end = halGetCounterValue(); + _stats.read_bytes += buffer->size(); + _stats.read_count++; + + const halrtcnt_t read_duration = read_end - read_start; + if ((_stats.read_duration_min == 0) || (read_duration < _stats.read_duration_min)) { + _stats.read_duration_min = read_duration; + } + if (read_duration > _stats.read_duration_max) { + _stats.read_duration_max = read_duration; + } + + if (!lfsr_compare(v, + reinterpret_cast(buffer->data()), + sizeof(*buffer.get()) / sizeof(lfsr_word_t))) { + return Result::FailCompare; + } + } + + file.sync(); + + const halrtcnt_t test_end = halGetCounterValue(); + _stats.read_test_duration = test_end - test_start; + + return Result::OK; + } }; -Thread* SDCardTestThread::thread { nullptr }; +Thread* SDCardTestThread::thread{nullptr}; namespace ui { SDCardDebugView::SDCardDebugView(NavigationView& nav) { - add_children({ - &text_title, - &text_csd_title, - &text_csd_value_3, - &text_csd_value_2, - &text_csd_value_1, - &text_csd_value_0, - &text_bus_width_title, - &text_bus_width_value, - &text_card_type_title, - &text_card_type_value, - &text_block_size_title, - &text_block_size_value, - &text_block_count_title, - &text_block_count_value, - &text_capacity_title, - &text_capacity_value, - &text_test_write_time_title, - &text_test_write_time_value, - &text_test_write_rate_title, - &text_test_write_rate_value, - &text_test_read_time_title, - &text_test_read_time_value, - &text_test_read_rate_title, - &text_test_read_rate_value, - &button_test, - &button_ok, - }); - - button_test.on_select = [this](Button&){ this->on_test(); }; - button_ok.on_select = [&nav](Button&){ nav.pop(); }; + add_children({ + &text_title, + &text_csd_title, + &text_csd_value_3, + &text_csd_value_2, + &text_csd_value_1, + &text_csd_value_0, + &text_bus_width_title, + &text_bus_width_value, + &text_card_type_title, + &text_card_type_value, + &text_block_size_title, + &text_block_size_value, + &text_block_count_title, + &text_block_count_value, + &text_capacity_title, + &text_capacity_value, + &text_test_write_time_title, + &text_test_write_time_value, + &text_test_write_rate_title, + &text_test_write_rate_value, + &text_test_read_time_title, + &text_test_read_time_value, + &text_test_read_rate_title, + &text_test_read_rate_value, + &button_test, + &button_ok, + }); + + button_test.on_select = [this](Button&) { this->on_test(); }; + button_ok.on_select = [&nav](Button&) { nav.pop(); }; } void SDCardDebugView::on_show() { - sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) { - this->on_status(status); - }; - on_status(sd_card::status()); + sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) { + this->on_status(status); + }; + on_status(sd_card::status()); } void SDCardDebugView::on_hide() { - sd_card::status_signal -= sd_card_status_signal_token; + sd_card::status_signal -= sd_card_status_signal_token; } void SDCardDebugView::focus() { - button_ok.focus(); + button_ok.focus(); } static std::string format_3dot3_string(const uint32_t value_in_thousandths) { - if( value_in_thousandths < 1000000U ) { - const uint32_t thousandths_part = value_in_thousandths % 1000; - const uint32_t integer_part = value_in_thousandths / 1000U; - return to_string_dec_uint(integer_part, 3) + "." + to_string_dec_uint(thousandths_part, 3, '0'); - } else { - return "HHH.HHH"; - } + if (value_in_thousandths < 1000000U) { + const uint32_t thousandths_part = value_in_thousandths % 1000; + const uint32_t integer_part = value_in_thousandths / 1000U; + return to_string_dec_uint(integer_part, 3) + "." + to_string_dec_uint(thousandths_part, 3, '0'); + } else { + return "HHH.HHH"; + } } static std::string format_bytes_size_string(uint64_t value) { - static const std::array suffix { { ' ', 'K', 'M', 'G', 'T' } }; - size_t suffix_index = 1; - while( (value >= 1000000U) && (suffix_index < suffix.size()) ) { - value /= 1000U; - suffix_index++; - } - return format_3dot3_string(value) + " " + suffix[suffix_index] + "B"; + static const std::array suffix{{' ', 'K', 'M', 'G', 'T'}}; + size_t suffix_index = 1; + while ((value >= 1000000U) && (suffix_index < suffix.size())) { + value /= 1000U; + suffix_index++; + } + return format_3dot3_string(value) + " " + suffix[suffix_index] + "B"; } void SDCardDebugView::on_status(const sd_card::Status) { - text_bus_width_value.set(""); - text_card_type_value.set(""); - text_csd_value_0.set(""); - text_csd_value_1.set(""); - text_csd_value_2.set(""); - text_csd_value_3.set(""); - text_block_size_value.set(""); - text_block_count_value.set(""); - text_capacity_value.set(""); - text_test_write_time_value.set(""); - text_test_write_rate_value.set(""); - text_test_read_time_value.set(""); - text_test_read_rate_value.set(""); - - const bool is_inserted = sdcIsCardInserted(&SDCD1); - if( is_inserted ) { - const auto card_width_flags = LPC_SDMMC->CTYPE & 0x10001; - size_t card_width = 0; - switch(card_width_flags) { - case 0x00000: card_width = 1; break; - case 0x00001: card_width = 4; break; - case 0x10001: card_width = 8; break; - default: break; - } - - text_bus_width_value.set(card_width ? to_string_dec_uint(card_width, 1) : "X"); - - // TODO: Implement Text class right-justify! - BYTE card_type; - disk_ioctl(0, MMC_GET_TYPE, &card_type); - - std::string formatted_card_type; - switch(card_type & SDC_MODE_CARDTYPE_MASK) { - case SDC_MODE_CARDTYPE_SDV11: formatted_card_type = "SD V1.1"; break; - case SDC_MODE_CARDTYPE_SDV20: formatted_card_type = "SD V2.0"; break; - case SDC_MODE_CARDTYPE_MMC: formatted_card_type = "MMC"; break; - default: formatted_card_type = "???"; break; - } - - if( card_type & SDC_MODE_HIGH_CAPACITY ) { - formatted_card_type += ", SDHC"; - } - text_card_type_value.set(formatted_card_type); - - std::array csd; - disk_ioctl(0, MMC_GET_CSD, csd.data()); - text_csd_value_3.set(to_string_hex(csd[3], 8)); - text_csd_value_2.set(to_string_hex(csd[2], 8)); - text_csd_value_1.set(to_string_hex(csd[1], 8)); - text_csd_value_0.set(to_string_hex(csd[0], 8)); - - BlockDeviceInfo block_device_info; - if( sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS ) { - text_block_size_value.set(to_string_dec_uint(block_device_info.blk_size, 5)); - text_block_count_value.set(to_string_dec_uint(block_device_info.blk_num, 9)); - const uint64_t capacity = block_device_info.blk_size * uint64_t(block_device_info.blk_num); - text_capacity_value.set(format_bytes_size_string(capacity)); - } - } + text_bus_width_value.set(""); + text_card_type_value.set(""); + text_csd_value_0.set(""); + text_csd_value_1.set(""); + text_csd_value_2.set(""); + text_csd_value_3.set(""); + text_block_size_value.set(""); + text_block_count_value.set(""); + text_capacity_value.set(""); + text_test_write_time_value.set(""); + text_test_write_rate_value.set(""); + text_test_read_time_value.set(""); + text_test_read_rate_value.set(""); + + const bool is_inserted = sdcIsCardInserted(&SDCD1); + if (is_inserted) { + const auto card_width_flags = LPC_SDMMC->CTYPE & 0x10001; + size_t card_width = 0; + switch (card_width_flags) { + case 0x00000: + card_width = 1; + break; + case 0x00001: + card_width = 4; + break; + case 0x10001: + card_width = 8; + break; + default: + break; + } + + text_bus_width_value.set(card_width ? to_string_dec_uint(card_width, 1) : "X"); + + // TODO: Implement Text class right-justify! + BYTE card_type; + disk_ioctl(0, MMC_GET_TYPE, &card_type); + + std::string formatted_card_type; + switch (card_type & SDC_MODE_CARDTYPE_MASK) { + case SDC_MODE_CARDTYPE_SDV11: + formatted_card_type = "SD V1.1"; + break; + case SDC_MODE_CARDTYPE_SDV20: + formatted_card_type = "SD V2.0"; + break; + case SDC_MODE_CARDTYPE_MMC: + formatted_card_type = "MMC"; + break; + default: + formatted_card_type = "???"; + break; + } + + if (card_type & SDC_MODE_HIGH_CAPACITY) { + formatted_card_type += ", SDHC"; + } + text_card_type_value.set(formatted_card_type); + + std::array csd; + disk_ioctl(0, MMC_GET_CSD, csd.data()); + text_csd_value_3.set(to_string_hex(csd[3], 8)); + text_csd_value_2.set(to_string_hex(csd[2], 8)); + text_csd_value_1.set(to_string_hex(csd[1], 8)); + text_csd_value_0.set(to_string_hex(csd[0], 8)); + + BlockDeviceInfo block_device_info; + if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) { + text_block_size_value.set(to_string_dec_uint(block_device_info.blk_size, 5)); + text_block_count_value.set(to_string_dec_uint(block_device_info.blk_num, 9)); + const uint64_t capacity = block_device_info.blk_size * uint64_t(block_device_info.blk_num); + text_capacity_value.set(format_bytes_size_string(capacity)); + } + } } static std::string format_ticks_as_ms(const halrtcnt_t value) { - const uint32_t us = uint64_t(value) * 1000000U / halGetCounterFrequency(); - return format_3dot3_string(us); + const uint32_t us = uint64_t(value) * 1000000U / halGetCounterFrequency(); + return format_3dot3_string(us); } static std::string format_bytes_per_ticks_as_mib(const File::Size bytes, const halrtcnt_t ticks) { - const uint32_t bps = uint64_t(bytes) * halGetCounterFrequency() / ticks; - const uint32_t kbps = bps / 1000U; - return format_3dot3_string(kbps); + const uint32_t bps = uint64_t(bytes) * halGetCounterFrequency() / ticks; + const uint32_t kbps = bps / 1000U; + return format_3dot3_string(kbps); } void SDCardDebugView::on_test() { - text_test_write_time_value.set(""); - text_test_write_rate_value.set(""); - text_test_read_time_value.set(""); - text_test_read_rate_value.set(""); - - SDCardTestThread thread; - - // uint32_t spinner_phase = 0; - while( thread.result() == SDCardTestThread::Result::Incomplete ) { - chThdSleepMilliseconds(100); - - // spinner_phase += 1; - // char c = '*'; - // switch(spinner_phase % 4) { - // case 0: c = '-'; break; - // case 1: c = '\\'; break; - // case 2: c = '|'; break; - // case 3: c = '/'; break; - // default: c = '*'; break; - // } - // text_test_write_value.set({ c }); - } - - if( thread.result() == SDCardTestThread::Result::OK ) { - const auto stats = thread.stats(); - const auto write_duration_avg = stats.write_test_duration / stats.write_count; - - text_test_write_time_value.set( - format_ticks_as_ms(stats.write_duration_min) + "/" + - format_ticks_as_ms(write_duration_avg) + "/" + - format_ticks_as_ms(stats.write_duration_max) - ); - - text_test_write_rate_value.set( - format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_duration_min * stats.write_count) + " " + - format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_test_duration) - ); - - const auto read_duration_avg = stats.read_test_duration / stats.read_count; - - text_test_read_time_value.set( - format_ticks_as_ms(stats.read_duration_min) + "/" + - format_ticks_as_ms(read_duration_avg) + "/" + - format_ticks_as_ms(stats.read_duration_max) - ); - - text_test_read_rate_value.set( - format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_duration_min * stats.read_count) + " " + - format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_test_duration) - ); - } else { - text_test_write_time_value.set("Fail: " + thread.ResultStr[thread.result() + 8]); - } + text_test_write_time_value.set(""); + text_test_write_rate_value.set(""); + text_test_read_time_value.set(""); + text_test_read_rate_value.set(""); + + SDCardTestThread thread; + + // uint32_t spinner_phase = 0; + while (thread.result() == SDCardTestThread::Result::Incomplete) { + chThdSleepMilliseconds(100); + + // spinner_phase += 1; + // char c = '*'; + // switch(spinner_phase % 4) { + // case 0: c = '-'; break; + // case 1: c = '\\'; break; + // case 2: c = '|'; break; + // case 3: c = '/'; break; + // default: c = '*'; break; + // } + // text_test_write_value.set({ c }); + } + + if (thread.result() == SDCardTestThread::Result::OK) { + const auto stats = thread.stats(); + const auto write_duration_avg = stats.write_test_duration / stats.write_count; + + text_test_write_time_value.set( + format_ticks_as_ms(stats.write_duration_min) + "/" + + format_ticks_as_ms(write_duration_avg) + "/" + + format_ticks_as_ms(stats.write_duration_max)); + + text_test_write_rate_value.set( + format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_duration_min * stats.write_count) + " " + + format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_test_duration)); + + const auto read_duration_avg = stats.read_test_duration / stats.read_count; + + text_test_read_time_value.set( + format_ticks_as_ms(stats.read_duration_min) + "/" + + format_ticks_as_ms(read_duration_avg) + "/" + + format_ticks_as_ms(stats.read_duration_max)); + + text_test_read_rate_value.set( + format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_duration_min * stats.read_count) + " " + + format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_test_duration)); + } else { + text_test_write_time_value.set("Fail: " + thread.ResultStr[thread.result() + 8]); + } } } /* namespace ui */ diff --git a/firmware/application/ui_sd_card_debug.hpp b/firmware/application/ui_sd_card_debug.hpp index 56253c533..4590418e2 100644 --- a/firmware/application/ui_sd_card_debug.hpp +++ b/firmware/application/ui_sd_card_debug.hpp @@ -30,177 +30,175 @@ namespace ui { class SDCardDebugView : public View { -public: - SDCardDebugView(NavigationView& nav); + public: + SDCardDebugView(NavigationView& nav); - void on_show() override; - void on_hide() override; + void on_show() override; + void on_hide() override; - void focus() override; + void focus() override; - std::string title() const override { return "SD Card"; }; + std::string title() const override { return "SD Card"; }; -private: - SignalToken sd_card_status_signal_token { }; + private: + SignalToken sd_card_status_signal_token{}; - void on_status(const sd_card::Status status); - void on_test(); + void on_status(const sd_card::Status status); + void on_test(); - Text text_title { - { (240 - (7 * 8)) / 2, 1 * 16, (7 * 8), 16 }, - "SD Card", - }; + Text text_title{ + {(240 - (7 * 8)) / 2, 1 * 16, (7 * 8), 16}, + "SD Card", + }; - Text text_csd_title { - { 0, 3 * 16, (8 * 8), 16 }, - "CSD", - }; + Text text_csd_title{ + {0, 3 * 16, (8 * 8), 16}, + "CSD", + }; - Text text_csd_value_3 { - { 240 - ((8 + 1 + 8) * 8), 3 * 16, (8 * 8), 16 }, - "", - }; + Text text_csd_value_3{ + {240 - ((8 + 1 + 8) * 8), 3 * 16, (8 * 8), 16}, + "", + }; - Text text_csd_value_2 { - { 240 - (8 * 8), 3 * 16, (8 * 8), 16 }, - "", - }; + Text text_csd_value_2{ + {240 - (8 * 8), 3 * 16, (8 * 8), 16}, + "", + }; - Text text_csd_value_1 { - { 240 - ((8 + 1 + 8) * 8), 4 * 16, (8 * 8), 16 }, - "", - }; + Text text_csd_value_1{ + {240 - ((8 + 1 + 8) * 8), 4 * 16, (8 * 8), 16}, + "", + }; - Text text_csd_value_0 { - { 240 - (8 * 8), 4 * 16, (8 * 8), 16 }, - "", - }; + Text text_csd_value_0{ + {240 - (8 * 8), 4 * 16, (8 * 8), 16}, + "", + }; - static constexpr size_t bus_width_characters = 1; + static constexpr size_t bus_width_characters = 1; - Text text_bus_width_title { - { 0, 5 * 16, (9 * 8), 16 }, - "Bus width", - }; + Text text_bus_width_title{ + {0, 5 * 16, (9 * 8), 16}, + "Bus width", + }; - Text text_bus_width_value { - { 240 - (bus_width_characters * 8), 5 * 16, (bus_width_characters * 8), 16 }, - "", - }; + Text text_bus_width_value{ + {240 - (bus_width_characters * 8), 5 * 16, (bus_width_characters * 8), 16}, + "", + }; - static constexpr size_t card_type_characters = 13; + static constexpr size_t card_type_characters = 13; - Text text_card_type_title { - { 0, 6 * 16, (9 * 8), 16 }, - "Card type", - }; + Text text_card_type_title{ + {0, 6 * 16, (9 * 8), 16}, + "Card type", + }; - Text text_card_type_value { - { 240 - (card_type_characters * 8), 6 * 16, (card_type_characters * 8), 16 }, - "", - }; + Text text_card_type_value{ + {240 - (card_type_characters * 8), 6 * 16, (card_type_characters * 8), 16}, + "", + }; - static constexpr size_t block_size_characters = 5; + static constexpr size_t block_size_characters = 5; - Text text_block_size_title { - { 0, 8 * 16, (10 * 8), 16 }, - "Block size", - }; + Text text_block_size_title{ + {0, 8 * 16, (10 * 8), 16}, + "Block size", + }; - Text text_block_size_value { - { 240 - (block_size_characters * 8), 8 * 16, (block_size_characters * 8), 16 }, - "", - }; + Text text_block_size_value{ + {240 - (block_size_characters * 8), 8 * 16, (block_size_characters * 8), 16}, + "", + }; - static constexpr size_t block_count_characters = 9; + static constexpr size_t block_count_characters = 9; - Text text_block_count_title { - { 0, 9 * 16, (11 * 8), 16 }, - "Block count", - }; + Text text_block_count_title{ + {0, 9 * 16, (11 * 8), 16}, + "Block count", + }; - Text text_block_count_value { - { 240 - (block_count_characters * 8), 9 * 16, (block_count_characters * 8), 16 }, - "", - }; + Text text_block_count_value{ + {240 - (block_count_characters * 8), 9 * 16, (block_count_characters * 8), 16}, + "", + }; - static constexpr size_t capacity_characters = 10; + static constexpr size_t capacity_characters = 10; - Text text_capacity_title { - { 0, 10 * 16, (8 * 8), 16 }, - "Capacity", - }; + Text text_capacity_title{ + {0, 10 * 16, (8 * 8), 16}, + "Capacity", + }; - Text text_capacity_value { - { 240 - (capacity_characters * 8), 10 * 16, (capacity_characters * 8), 16 }, - "", - }; + Text text_capacity_value{ + {240 - (capacity_characters * 8), 10 * 16, (capacity_characters * 8), 16}, + "", + }; - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// - static constexpr size_t test_write_time_characters = 23; + static constexpr size_t test_write_time_characters = 23; - Text text_test_write_time_title { - { 0, 12 * 16, (4 * 8), 16 }, - "W ms", - }; + Text text_test_write_time_title{ + {0, 12 * 16, (4 * 8), 16}, + "W ms", + }; - Text text_test_write_time_value { - { 240 - (test_write_time_characters * 8), 12 * 16, (test_write_time_characters * 8), 16 }, - "", - }; + Text text_test_write_time_value{ + {240 - (test_write_time_characters * 8), 12 * 16, (test_write_time_characters * 8), 16}, + "", + }; - static constexpr size_t test_write_rate_characters = 23; - - Text text_test_write_rate_title { - { 0, 13 * 16, (6 * 8), 16 }, - "W MB/s", - }; + static constexpr size_t test_write_rate_characters = 23; - Text text_test_write_rate_value { - { 240 - (test_write_rate_characters * 8), 13 * 16, (test_write_rate_characters * 8), 16 }, - "", - }; + Text text_test_write_rate_title{ + {0, 13 * 16, (6 * 8), 16}, + "W MB/s", + }; - /////////////////////////////////////////////////////////////////////// + Text text_test_write_rate_value{ + {240 - (test_write_rate_characters * 8), 13 * 16, (test_write_rate_characters * 8), 16}, + "", + }; - static constexpr size_t test_read_time_characters = 23; + /////////////////////////////////////////////////////////////////////// - Text text_test_read_time_title { - { 0, 14 * 16, (4 * 8), 16 }, - "R ms", - }; + static constexpr size_t test_read_time_characters = 23; - Text text_test_read_time_value { - { 240 - (test_read_time_characters * 8), 14 * 16, (test_read_time_characters * 8), 16 }, - "", - }; + Text text_test_read_time_title{ + {0, 14 * 16, (4 * 8), 16}, + "R ms", + }; - static constexpr size_t test_read_rate_characters = 23; + Text text_test_read_time_value{ + {240 - (test_read_time_characters * 8), 14 * 16, (test_read_time_characters * 8), 16}, + "", + }; - Text text_test_read_rate_title { - { 0, 15 * 16, (6 * 8), 16 }, - "R MB/s", - }; + static constexpr size_t test_read_rate_characters = 23; - Text text_test_read_rate_value { - { 240 - (test_read_rate_characters * 8), 15 * 16, (test_read_rate_characters * 8), 16 }, - "", - }; + Text text_test_read_rate_title{ + {0, 15 * 16, (6 * 8), 16}, + "R MB/s", + }; - /////////////////////////////////////////////////////////////////////// + Text text_test_read_rate_value{ + {240 - (test_read_rate_characters * 8), 15 * 16, (test_read_rate_characters * 8), 16}, + "", + }; - Button button_test { - { 16, 17 * 16, 96, 24 }, - "Test" - }; + /////////////////////////////////////////////////////////////////////// - Button button_ok { - { 240 - 96 - 16, 17 * 16, 96, 24 }, - "OK" - }; + Button button_test{ + {16, 17 * 16, 96, 24}, + "Test"}; + + Button button_ok{ + {240 - 96 - 16, 17 * 16, 96, 24}, + "OK"}; }; } /* namespace ui */ -#endif/*__UI_SD_CARD_DEBUG_H__*/ +#endif /*__UI_SD_CARD_DEBUG_H__*/ diff --git a/firmware/application/ui_sd_card_status_view.cpp b/firmware/application/ui_sd_card_status_view.cpp index 252b3d272..5efe8dae9 100644 --- a/firmware/application/ui_sd_card_status_view.cpp +++ b/firmware/application/ui_sd_card_status_view.cpp @@ -34,24 +34,24 @@ namespace ui { namespace detail { const Bitmap& bitmap_sd_card(const sd_card::Status status) { - switch(status) { - case sd_card::Status::IOError: - case sd_card::Status::MountError: - case sd_card::Status::ConnectError: - return bitmap_sd_card_error; + switch (status) { + case sd_card::Status::IOError: + case sd_card::Status::MountError: + case sd_card::Status::ConnectError: + return bitmap_sd_card_error; - case sd_card::Status::NotPresent: - return bitmap_sd_card_unknown; + case sd_card::Status::NotPresent: + return bitmap_sd_card_unknown; - case sd_card::Status::Present: - return bitmap_sd_card_unknown; + case sd_card::Status::Present: + return bitmap_sd_card_unknown; - case sd_card::Status::Mounted: - return bitmap_sd_card_ok; + case sd_card::Status::Mounted: + return bitmap_sd_card_ok; - default: - return bitmap_sd_card_unknown; - } + default: + return bitmap_sd_card_unknown; + } } static constexpr Color color_sd_card_error = Color::red(); @@ -59,55 +59,54 @@ static constexpr Color color_sd_card_unknown = Color::yellow(); static constexpr Color color_sd_card_ok = Color::green(); const Color color_sd_card(const sd_card::Status status) { - switch(status) { - case sd_card::Status::IOError: - case sd_card::Status::MountError: - case sd_card::Status::ConnectError: - return color_sd_card_error; + switch (status) { + case sd_card::Status::IOError: + case sd_card::Status::MountError: + case sd_card::Status::ConnectError: + return color_sd_card_error; - case sd_card::Status::NotPresent: - return color_sd_card_unknown; + case sd_card::Status::NotPresent: + return color_sd_card_unknown; - case sd_card::Status::Present: - return color_sd_card_unknown; + case sd_card::Status::Present: + return color_sd_card_unknown; - case sd_card::Status::Mounted: - return color_sd_card_ok; + case sd_card::Status::Mounted: + return color_sd_card_ok; - default: - return color_sd_card_unknown; - } + default: + return color_sd_card_unknown; + } } } /* namespace detail */ SDCardStatusView::SDCardStatusView( - const Rect parent_rect -) : Image { parent_rect, &bitmap_sd_card_unknown, detail::color_sd_card_unknown, Color::dark_grey() } -{ + const Rect parent_rect) + : Image{parent_rect, &bitmap_sd_card_unknown, detail::color_sd_card_unknown, Color::dark_grey()} { } void SDCardStatusView::on_show() { - sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) { - this->on_status(status); - }; + sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) { + this->on_status(status); + }; } void SDCardStatusView::on_hide() { - sd_card::status_signal -= sd_card_status_signal_token; + sd_card::status_signal -= sd_card_status_signal_token; } void SDCardStatusView::paint(Painter& painter) { - const auto status = sd_card::status(); - set_bitmap(&detail::bitmap_sd_card(status)); - set_foreground(detail::color_sd_card(status)); + const auto status = sd_card::status(); + set_bitmap(&detail::bitmap_sd_card(status)); + set_foreground(detail::color_sd_card(status)); - Image::paint(painter); + Image::paint(painter); } void SDCardStatusView::on_status(const sd_card::Status) { - // Don't update image properties here, they might change. Wait until paint. - set_dirty(); + // Don't update image properties here, they might change. Wait until paint. + set_dirty(); } } /* namespace ui */ diff --git a/firmware/application/ui_sd_card_status_view.hpp b/firmware/application/ui_sd_card_status_view.hpp index 757cff7b0..a24c65350 100644 --- a/firmware/application/ui_sd_card_status_view.hpp +++ b/firmware/application/ui_sd_card_status_view.hpp @@ -29,20 +29,20 @@ namespace ui { class SDCardStatusView : public Image { -public: - SDCardStatusView(const Rect parent_rect); - - void on_show() override; - void on_hide() override; + public: + SDCardStatusView(const Rect parent_rect); - void paint(Painter& painter) override; + void on_show() override; + void on_hide() override; -private: - SignalToken sd_card_status_signal_token { }; + void paint(Painter& painter) override; - void on_status(const sd_card::Status status); + private: + SignalToken sd_card_status_signal_token{}; + + void on_status(const sd_card::Status status); }; } /* namespace ui */ -#endif/*__UI_SD_CARD_STATUS_VIEW_H__*/ +#endif /*__UI_SD_CARD_STATUS_VIEW_H__*/ diff --git a/firmware/application/unistroke.hpp b/firmware/application/unistroke.hpp index a654f541d..b1dbc41f9 100644 --- a/firmware/application/unistroke.hpp +++ b/firmware/application/unistroke.hpp @@ -21,70 +21,69 @@ */ enum Condition { - cond_empty = -1, - stroke_a = 0, - stroke_b = 1, - stroke_c = 2, - stroke_d = 3, - last = 8 + cond_empty = -1, + stroke_a = 0, + stroke_b = 1, + stroke_c = 2, + stroke_d = 3, + last = 8 }; enum Direction { - dir_empty = -1, - Uw = 0x0F, // Wildcards - Dw = 0x1F, - Lw = 0xF0, - Rw = 0xF1, - U = 0x02, // Orthos - D = 0x12, - L = 0x20, - R = 0x21, - UL = 0x00, // Diagonals - DL = 0x10, - UR = 0x01, - DR = 0x11 + dir_empty = -1, + Uw = 0x0F, // Wildcards + Dw = 0x1F, + Lw = 0xF0, + Rw = 0xF1, + U = 0x02, // Orthos + D = 0x12, + L = 0x20, + R = 0x21, + UL = 0x00, // Diagonals + DL = 0x10, + UR = 0x01, + DR = 0x11 }; struct HandWriting { - uint8_t letter_count; - struct HandWritingLetter { - struct HandWritingMatch { - Condition cond; - Direction dir; - } match[3]; - uint8_t count; - } letter[32]; + uint8_t letter_count; + struct HandWritingLetter { + struct HandWritingMatch { + Condition cond; + Direction dir; + } match[3]; + uint8_t count; + } letter[32]; }; const HandWriting handwriting_unistroke = { - 27, - { - {{{stroke_a, UL}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // BS< 0=UL MI=1 - {{{stroke_a, U}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // A 0=U MI=1 - {{{stroke_a, DR}, {stroke_b, DL}, {cond_empty, dir_empty}}, 2}, // B 0=DR 1=DL MI=2 - {{{stroke_a, UL}, {stroke_b, UR}, {cond_empty, dir_empty}}, 0}, // C 0=UL 1=UR - {{{stroke_a, DL}, {stroke_b, DR}, {cond_empty, dir_empty}}, 0}, // D 0=DL 1=DR - {{{stroke_a, L}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // E 0=L MI=1 - {{{stroke_a, U}, {stroke_b, R}, {cond_empty, dir_empty}}, 0}, // F 0=U 1=R - {{{stroke_a, R}, {stroke_b, U}, {cond_empty, dir_empty}}, 0}, // G 0=R 1=U - {{{stroke_a, R}, {stroke_b, D}, {cond_empty, dir_empty}}, 2}, // H 0=R 1=D MI=2 - {{{stroke_a, D}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // I 0=D MI=1 - {{{stroke_a, D}, {stroke_b, L}, {cond_empty, dir_empty}}, 2}, // J 0=D 1=L MI=2 - {{{stroke_a, UR}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // K 0=UR MI=1 - {{{stroke_a, D}, {stroke_b, R}, {cond_empty, dir_empty}}, 2}, // L 0=D 1=R MI=2 - {{{stroke_a, UL}, {stroke_b, DL}, {cond_empty, dir_empty}}, 2}, // M 0=UL 1=DL MI=2 - {{{stroke_a, UR}, {stroke_b, DR}, {cond_empty, dir_empty}}, 2}, // N 0=UR 1=DR MI=2 - {{{stroke_a, DL}, {last, Lw}, {cond_empty, dir_empty}}, 3}, // O 0=DL MI>2 -=Uw - {{{stroke_a, DR}, {last, Dw}, {cond_empty, dir_empty}}, 3}, // P 0=DR MI>2 -=Dw - {{{stroke_a, DL}, {last, Dw}, {cond_empty, dir_empty}}, 3}, // Q 0=DL MI>2 -=Dw - {{{stroke_a, DR}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // R 0=DR MI=1 - {{{stroke_a, Lw}, {stroke_b, DR}, {cond_empty, dir_empty}}, 0}, // S 0=Lw 1=DR - {{{stroke_a, R}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // T 0=R MI=1 - {{{stroke_a, DL}, {stroke_b, UL}, {cond_empty, dir_empty}}, 2}, // U 0=DL 1=UL - {{{stroke_a, DR}, {stroke_b, UR}, {cond_empty, dir_empty}}, 2}, // V 0=DR 1=UR - {{{stroke_a, D}, {stroke_b, UR}, {stroke_c, D}}, 0}, // W 0=D 1=UR 2=D - {{{stroke_a, DR}, {last, Uw}, {cond_empty, dir_empty}}, 3}, // X 0=DR MI>2 -=Uw - {{{stroke_a, DL}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // Y 0=DL MI=1 - {{{stroke_a, Rw}, {stroke_b, DL}, {cond_empty, dir_empty}}, 0}, // Z 0=Rw 1=DL - } -}; + 27, + { + {{{stroke_a, UL}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // BS< 0=UL MI=1 + {{{stroke_a, U}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // A 0=U MI=1 + {{{stroke_a, DR}, {stroke_b, DL}, {cond_empty, dir_empty}}, 2}, // B 0=DR 1=DL MI=2 + {{{stroke_a, UL}, {stroke_b, UR}, {cond_empty, dir_empty}}, 0}, // C 0=UL 1=UR + {{{stroke_a, DL}, {stroke_b, DR}, {cond_empty, dir_empty}}, 0}, // D 0=DL 1=DR + {{{stroke_a, L}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // E 0=L MI=1 + {{{stroke_a, U}, {stroke_b, R}, {cond_empty, dir_empty}}, 0}, // F 0=U 1=R + {{{stroke_a, R}, {stroke_b, U}, {cond_empty, dir_empty}}, 0}, // G 0=R 1=U + {{{stroke_a, R}, {stroke_b, D}, {cond_empty, dir_empty}}, 2}, // H 0=R 1=D MI=2 + {{{stroke_a, D}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // I 0=D MI=1 + {{{stroke_a, D}, {stroke_b, L}, {cond_empty, dir_empty}}, 2}, // J 0=D 1=L MI=2 + {{{stroke_a, UR}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // K 0=UR MI=1 + {{{stroke_a, D}, {stroke_b, R}, {cond_empty, dir_empty}}, 2}, // L 0=D 1=R MI=2 + {{{stroke_a, UL}, {stroke_b, DL}, {cond_empty, dir_empty}}, 2}, // M 0=UL 1=DL MI=2 + {{{stroke_a, UR}, {stroke_b, DR}, {cond_empty, dir_empty}}, 2}, // N 0=UR 1=DR MI=2 + {{{stroke_a, DL}, {last, Lw}, {cond_empty, dir_empty}}, 3}, // O 0=DL MI>2 -=Uw + {{{stroke_a, DR}, {last, Dw}, {cond_empty, dir_empty}}, 3}, // P 0=DR MI>2 -=Dw + {{{stroke_a, DL}, {last, Dw}, {cond_empty, dir_empty}}, 3}, // Q 0=DL MI>2 -=Dw + {{{stroke_a, DR}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // R 0=DR MI=1 + {{{stroke_a, Lw}, {stroke_b, DR}, {cond_empty, dir_empty}}, 0}, // S 0=Lw 1=DR + {{{stroke_a, R}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // T 0=R MI=1 + {{{stroke_a, DL}, {stroke_b, UL}, {cond_empty, dir_empty}}, 2}, // U 0=DL 1=UL + {{{stroke_a, DR}, {stroke_b, UR}, {cond_empty, dir_empty}}, 2}, // V 0=DR 1=UR + {{{stroke_a, D}, {stroke_b, UR}, {stroke_c, D}}, 0}, // W 0=D 1=UR 2=D + {{{stroke_a, DR}, {last, Uw}, {cond_empty, dir_empty}}, 3}, // X 0=DR MI>2 -=Uw + {{{stroke_a, DL}, {cond_empty, dir_empty}, {cond_empty, dir_empty}}, 1}, // Y 0=DL MI=1 + {{{stroke_a, Rw}, {stroke_b, DL}, {cond_empty, dir_empty}}, 0}, // Z 0=Rw 1=DL + }}; diff --git a/firmware/application/ymdata.hpp b/firmware/application/ymdata.hpp index a44e6f31f..a506777af 100644 --- a/firmware/application/ymdata.hpp +++ b/firmware/application/ymdata.hpp @@ -1,379 +1,378 @@ const uint8_t ymdata_bin[] = { - 0x00, 0x03, 0x1e, 0x00, 0xb4, 0x01, 0x3e, 0x02, 0x61, 0x05, 0xf7, 0x06, - 0x6d, 0x08, 0xc2, 0x09, 0x68, 0x0a, 0x8d, 0x0c, 0x43, 0x0d, 0x9e, 0x0f, - 0xef, 0x10, 0x01, 0x11, 0x0f, 0x11, 0x01, 0x64, 0x98, 0x3d, 0x82, 0xc8, - 0x82, 0xa8, 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, - 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, - 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, - 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x8c, 0x3d, 0x82, 0xc8, 0x82, 0xb2, - 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, - 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, - 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, - 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, - 0x86, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x8c, 0x3d, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0x70, 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, - 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0x3d, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0xad, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, 0x82, 0x77, - 0x82, 0x64, 0x92, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x98, 0x3d, 0x82, 0xc8, 0x82, 0xa8, - 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x92, 0x3d, - 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x8c, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, - 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, - 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x8c, 0x3d, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0x70, 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x92, 0x3d, - 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0x3d, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0xad, 0x82, 0x96, - 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, - 0x92, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, - 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, - 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, - 0x82, 0x77, 0x01, 0x64, 0x01, 0x30, 0x98, 0x36, 0x86, 0x30, 0x92, 0x36, - 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, 0x8c, 0x30, 0x86, 0x36, - 0x86, 0x30, 0x8c, 0x36, 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, - 0xa4, 0x30, 0x86, 0x35, 0x86, 0x30, 0x8c, 0x35, 0x86, 0x30, 0x92, 0x35, - 0x86, 0x30, 0x92, 0x35, 0x86, 0x30, 0x86, 0x35, 0x8c, 0x30, 0x86, 0x34, - 0x86, 0x30, 0x8c, 0x34, 0x86, 0x30, 0x92, 0x34, 0x86, 0x30, 0x8c, 0x34, - 0x9e, 0x30, 0x98, 0x36, 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x92, 0x36, - 0x86, 0x30, 0x86, 0x36, 0x8c, 0x30, 0x86, 0x36, 0x86, 0x30, 0x8c, 0x36, - 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, 0xa4, 0x30, 0x86, 0x35, - 0x86, 0x30, 0x8c, 0x35, 0x86, 0x30, 0x92, 0x35, 0x86, 0x30, 0x92, 0x35, - 0x86, 0x30, 0x86, 0x35, 0x8c, 0x30, 0x86, 0x34, 0x86, 0x30, 0x8c, 0x34, - 0x86, 0x30, 0x92, 0x34, 0x86, 0x30, 0x8c, 0x34, 0x9d, 0x30, 0x82, 0xfb, - 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, - 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xa8, 0x82, - 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, - 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, - 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, - 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, - 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, - 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, - 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, - 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, - 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, - 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, - 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, - 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, - 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x06, 0x5a, 0x23, 0x30, - 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, - 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x04, 0xfb, 0x86, - 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x06, 0x5a, - 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x04, - 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, - 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, - 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0x96, 0x82, 0x77, 0x82, - 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, - 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0x96, 0x82, 0x77, - 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x04, 0xfb, 0x86, 0xf4, - 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, - 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, - 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, - 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, - 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, - 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, - 0xfb, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, - 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, - 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, - 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, - 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, - 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x06, 0x5a, 0x23, - 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, - 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, - 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, - 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, - 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x83, - 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, - 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, - 0xfb, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, - 0x70, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, - 0x82, 0x70, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, - 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, - 0x86, 0x82, 0x70, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, - 0x82, 0x86, 0x82, 0x70, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, - 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, - 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, - 0x96, 0x82, 0x77, 0x82, 0x64, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, - 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, - 0xfb, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, - 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, - 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0b, 0x5a, 0x23, - 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0xfb, 0x82, 0x04, 0x82, - 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, - 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, - 0x03, 0x04, 0x05, 0x07, 0x09, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, - 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, - 0x04, 0x8c, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, - 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, - 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, 0x07, - 0x09, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, - 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, - 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, - 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, - 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, - 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, - 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, - 0x04, 0x8c, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, - 0x04, 0x05, 0x07, 0x09, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, - 0x04, 0x05, 0x07, 0x09, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, - 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, - 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, - 0x05, 0x07, 0x09, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, - 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, - 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, - 0x82, 0x03, 0x83, 0x04, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, - 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x04, - 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, - 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, - 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, - 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x86, 0x00, - 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, - 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, 0x03, 0x04, - 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, - 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, - 0x07, 0x09, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, - 0x07, 0x09, 0x04, 0x82, 0x03, 0x82, 0x04, 0x01, 0xee, 0x86, 0x0c, 0x86, - 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, - 0xc3, 0x86, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, - 0xc3, 0x86, 0xe1, 0x86, 0xc3, 0x86, 0x65, 0x86, 0x0c, 0x86, 0x65, 0x86, - 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x8c, 0xee, 0x06, 0xec, 0xea, - 0xe8, 0xe6, 0xe4, 0xe2, 0x86, 0xe1, 0x06, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb, - 0xed, 0x8c, 0xee, 0x92, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, - 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0x0c, 0x86, 0x65, 0x86, - 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0xc3, 0x86, - 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, - 0xee, 0x86, 0x65, 0x86, 0x3e, 0x06, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, - 0x8c, 0x2d, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, - 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, 0x06, 0x29, - 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x86, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, - 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0x0c, 0x86, - 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, - 0xc3, 0x86, 0x86, 0x86, 0xb2, 0x86, 0xe1, 0x86, 0x70, 0x86, 0xb2, 0x86, - 0xe1, 0x86, 0x77, 0x86, 0xb2, 0x86, 0x86, 0x86, 0xb2, 0x86, 0xe1, 0x86, - 0x77, 0x86, 0xb2, 0x86, 0xe1, 0x86, 0x70, 0x86, 0xb2, 0x86, 0x96, 0x12, - 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, - 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x8c, 0x86, 0x07, 0x83, 0x86, 0x89, - 0x8c, 0x89, 0x86, 0x83, 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, - 0x86, 0x83, 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, 0x86, 0x83, - 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, 0x86, 0x83, 0x82, 0x80, - 0x8c, 0xe1, 0x0c, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xeb, 0xec, 0xed, 0x8c, 0xee, 0x07, 0xec, 0xee, 0xf0, 0xf2, 0xf0, 0xee, - 0xec, 0x82, 0xea, 0x08, 0xec, 0xee, 0xf0, 0xf1, 0xf4, 0xf1, 0xee, 0xeb, - 0x82, 0xe8, 0x07, 0xeb, 0xee, 0xf1, 0xf4, 0xf1, 0xee, 0xeb, 0x82, 0xe8, - 0x01, 0xeb, 0x87, 0xee, 0x8c, 0x2d, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, - 0x2d, 0x29, 0x82, 0x25, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, - 0x82, 0x25, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, - 0x08, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x25, 0x02, 0xe0, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, - 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, - 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, - 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, - 0xa3, 0xe0, 0x8c, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0xaf, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, - 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0xc7, 0xe0, 0x01, 0xf0, 0xc7, 0xe0, - 0xaf, 0xe1, 0x01, 0x49, 0x8c, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, - 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, 0x8c, 0x69, 0x86, 0x89, - 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, - 0x92, 0x69, 0x86, 0x89, 0x82, 0x49, 0x84, 0x4c, 0x86, 0x69, 0x86, 0x89, - 0xa0, 0x49, 0x84, 0x4c, 0x86, 0x49, 0x98, 0x69, 0x86, 0x89, 0x86, 0x49, - 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x92, 0x69, 0x86, 0x89, - 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, - 0x8c, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, - 0x86, 0x49, 0xa0, 0x69, 0x84, 0x6c, 0x88, 0x69, 0x84, 0x6c, 0x92, 0x69, - 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, - 0x92, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, - 0x86, 0x49, 0x86, 0x89, 0x9a, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, - 0xac, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, - 0x92, 0x49, 0x9a, 0x69, 0x84, 0x6c, 0x88, 0x69, 0x84, 0x6c, 0x85, 0x69, - 0x02, 0xf8, 0xea, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, - 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, - 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xea, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xf0, 0x83, - 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, - 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, - 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, - 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x84, 0xf8, 0x01, 0x0d, 0x98, - 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, - 0x10, 0x86, 0x0a, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, - 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0a, 0x86, 0x09, 0x86, - 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, - 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0b, 0x86, - 0x10, 0x86, 0x09, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, - 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x86, 0x0b, 0x86, - 0x0a, 0x86, 0x09, 0x86, 0x0d, 0x98, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, - 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0a, 0x86, 0x0d, 0x86, - 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, - 0x10, 0x86, 0x0a, 0x86, 0x09, 0x86, 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, - 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, - 0x0d, 0x92, 0x10, 0x86, 0x0b, 0x86, 0x10, 0x86, 0x09, 0x86, 0x0d, 0x86, - 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x8c, - 0x10, 0x86, 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x85, 0x0d, 0x02, - 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0d, 0x86, 0x0b, - 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, - 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, - 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, - 0x0c, 0x0a, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, - 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, - 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, - 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, - 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, - 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, - 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, - 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, - 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, - 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, - 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, - 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, - 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x01, 0x0f, 0x82, 0x0e, 0x02, 0x0d, - 0x0c, 0x87, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, - 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, - 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, 0x0a, - 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, 0x0d, - 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, - 0x86, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, - 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, - 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, - 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0d, 0x86, - 0x0b, 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, - 0x0b, 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, - 0x00, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x01, 0x0f, 0x82, 0x0e, 0x03, - 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, - 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, - 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, - 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, - 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, - 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, - 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, - 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, - 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, - 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, - 0x0b, 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, - 0x0b, 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, - 0x00, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x01, 0x0f, 0x82, 0x0e, 0x02, - 0x0d, 0x0c, 0x87, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, - 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, - 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, - 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, - 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, - 0x0b, 0x86, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, - 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, - 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x02, - 0x0c, 0x09, 0x02, 0x0e, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xaf, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xaf, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, - 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xc7, 0x0e, 0x01, - 0x0f, 0xf6, 0x0e, 0x01, 0x4b, 0xff, 0x64, 0xc1, 0x64, 0xe0, 0x54, 0xe0, - 0x4b, 0xff, 0x64, 0xc1, 0x64, 0xe0, 0x54, 0xdf, 0x4b, 0xff, 0x00, 0xff, - 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x86, 0x00, 0x02, - 0xff, 0x0e, 0x9d, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, - 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, - 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x8b, 0xff, - 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, - 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, - 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x9d, 0xff, - 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x91, 0xff, - 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, - 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, - 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x91, 0xff, - 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, - 0x01, 0x0e, 0xa8, 0xff -}; + 0x00, 0x03, 0x1e, 0x00, 0xb4, 0x01, 0x3e, 0x02, 0x61, 0x05, 0xf7, 0x06, + 0x6d, 0x08, 0xc2, 0x09, 0x68, 0x0a, 0x8d, 0x0c, 0x43, 0x0d, 0x9e, 0x0f, + 0xef, 0x10, 0x01, 0x11, 0x0f, 0x11, 0x01, 0x64, 0x98, 0x3d, 0x82, 0xc8, + 0x82, 0xa8, 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, + 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, + 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, + 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x8c, 0x3d, 0x82, 0xc8, 0x82, 0xb2, + 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, + 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, + 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, + 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, + 0x86, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x8c, 0x3d, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0x70, 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, + 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0x3d, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0xad, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, 0x82, 0x77, + 0x82, 0x64, 0x92, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x98, 0x3d, 0x82, 0xc8, 0x82, 0xa8, + 0x82, 0x86, 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x92, 0x3d, + 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x8c, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, + 0x92, 0x3d, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x86, 0x3d, + 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x8c, 0x3d, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0x70, 0x92, 0x3d, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x92, 0x3d, + 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0x3d, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x86, 0xad, 0x82, 0x96, + 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, + 0x92, 0xad, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x8c, 0xad, 0x82, 0x96, + 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, + 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, + 0x82, 0x77, 0x01, 0x64, 0x01, 0x30, 0x98, 0x36, 0x86, 0x30, 0x92, 0x36, + 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, 0x8c, 0x30, 0x86, 0x36, + 0x86, 0x30, 0x8c, 0x36, 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, + 0xa4, 0x30, 0x86, 0x35, 0x86, 0x30, 0x8c, 0x35, 0x86, 0x30, 0x92, 0x35, + 0x86, 0x30, 0x92, 0x35, 0x86, 0x30, 0x86, 0x35, 0x8c, 0x30, 0x86, 0x34, + 0x86, 0x30, 0x8c, 0x34, 0x86, 0x30, 0x92, 0x34, 0x86, 0x30, 0x8c, 0x34, + 0x9e, 0x30, 0x98, 0x36, 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x92, 0x36, + 0x86, 0x30, 0x86, 0x36, 0x8c, 0x30, 0x86, 0x36, 0x86, 0x30, 0x8c, 0x36, + 0x86, 0x30, 0x92, 0x36, 0x86, 0x30, 0x86, 0x36, 0xa4, 0x30, 0x86, 0x35, + 0x86, 0x30, 0x8c, 0x35, 0x86, 0x30, 0x92, 0x35, 0x86, 0x30, 0x92, 0x35, + 0x86, 0x30, 0x86, 0x35, 0x8c, 0x30, 0x86, 0x34, 0x86, 0x30, 0x8c, 0x34, + 0x86, 0x30, 0x92, 0x34, 0x86, 0x30, 0x8c, 0x34, 0x9d, 0x30, 0x82, 0xfb, + 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, + 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xa8, 0x82, + 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, + 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, + 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, + 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, + 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, + 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, + 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, + 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, + 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, + 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, + 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, + 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, + 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x06, 0x5a, 0x23, 0x30, + 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, + 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x04, 0xfb, 0x86, + 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x06, 0x5a, + 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x04, + 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, + 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, + 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0x96, 0x82, 0x77, 0x82, + 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, + 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0x96, 0x82, 0x77, + 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x04, 0xfb, 0x86, 0xf4, + 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, + 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, + 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, + 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, + 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, + 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, + 0xfb, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, + 0x86, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x06, 0x5a, 0x23, 0x30, 0x97, + 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xa8, 0x82, 0x86, 0x04, 0xfb, 0x86, 0xf4, + 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, + 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, + 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x06, 0x5a, 0x23, + 0x30, 0x97, 0x78, 0xf6, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, + 0x82, 0xb2, 0x82, 0x86, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, 0xfb, + 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xc8, 0x82, 0xb2, 0x82, 0x86, 0x04, + 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, 0xfb, + 0x0a, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x83, + 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, + 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x82, + 0xfb, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, + 0x70, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, + 0x82, 0x70, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0xa8, 0x82, + 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, 0x86, 0x82, 0x70, 0x82, 0xa8, 0x82, + 0x86, 0x82, 0x70, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, 0x82, 0xa8, + 0x82, 0x86, 0x82, 0x70, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, + 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, 0x86, 0xf4, 0x70, 0x83, 0xfb, 0x03, + 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, + 0x96, 0x82, 0x77, 0x82, 0x64, 0x06, 0x5a, 0x23, 0x30, 0x97, 0x78, 0xf6, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, + 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, + 0xfb, 0x82, 0x96, 0x82, 0x77, 0x82, 0x64, 0x82, 0x96, 0x82, 0x77, 0x82, + 0x64, 0x04, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0a, 0x5a, 0x23, 0x30, + 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0x82, 0xfb, 0x0b, 0x5a, 0x23, + 0x30, 0x97, 0x78, 0xf6, 0xfb, 0x86, 0xf4, 0x70, 0xfb, 0x82, 0x04, 0x82, + 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, + 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, + 0x03, 0x04, 0x05, 0x07, 0x09, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, + 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, + 0x04, 0x8c, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, + 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, + 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, 0x07, + 0x09, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, + 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, + 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, + 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, + 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, + 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, + 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, + 0x04, 0x8c, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, + 0x04, 0x05, 0x07, 0x09, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, + 0x04, 0x05, 0x07, 0x09, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, + 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, + 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, + 0x05, 0x07, 0x09, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, + 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, + 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, + 0x82, 0x03, 0x83, 0x04, 0x86, 0x00, 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, + 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x04, + 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, + 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, + 0x03, 0x04, 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, + 0x04, 0x92, 0x00, 0x06, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x86, 0x00, + 0x01, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, 0x84, 0x04, 0x82, 0x03, + 0x84, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, 0x00, 0x06, 0x02, 0x03, 0x04, + 0x05, 0x07, 0x09, 0x92, 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x8c, + 0x00, 0x01, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, + 0x07, 0x09, 0x04, 0x82, 0x03, 0x83, 0x04, 0x07, 0x02, 0x03, 0x04, 0x05, + 0x07, 0x09, 0x04, 0x82, 0x03, 0x82, 0x04, 0x01, 0xee, 0x86, 0x0c, 0x86, + 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, + 0xc3, 0x86, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, + 0xc3, 0x86, 0xe1, 0x86, 0xc3, 0x86, 0x65, 0x86, 0x0c, 0x86, 0x65, 0x86, + 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x8c, 0xee, 0x06, 0xec, 0xea, + 0xe8, 0xe6, 0xe4, 0xe2, 0x86, 0xe1, 0x06, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb, + 0xed, 0x8c, 0xee, 0x92, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, + 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0x0c, 0x86, 0x65, 0x86, + 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0xc3, 0x86, + 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, + 0xee, 0x86, 0x65, 0x86, 0x3e, 0x06, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, + 0x8c, 0x2d, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, + 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, 0x06, 0x29, + 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x86, 0x0c, 0x86, 0x65, 0x86, 0xc3, 0x86, + 0xe1, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0x0c, 0x86, + 0x65, 0x86, 0xc3, 0x86, 0xee, 0x86, 0x65, 0x86, 0xc3, 0x86, 0xe1, 0x86, + 0xc3, 0x86, 0x86, 0x86, 0xb2, 0x86, 0xe1, 0x86, 0x70, 0x86, 0xb2, 0x86, + 0xe1, 0x86, 0x77, 0x86, 0xb2, 0x86, 0x86, 0x86, 0xb2, 0x86, 0xe1, 0x86, + 0x77, 0x86, 0xb2, 0x86, 0xe1, 0x86, 0x70, 0x86, 0xb2, 0x86, 0x96, 0x12, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, + 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x8c, 0x86, 0x07, 0x83, 0x86, 0x89, + 0x8c, 0x89, 0x86, 0x83, 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, + 0x86, 0x83, 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, 0x86, 0x83, + 0x82, 0x80, 0x07, 0x83, 0x86, 0x89, 0x8c, 0x89, 0x86, 0x83, 0x82, 0x80, + 0x8c, 0xe1, 0x0c, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xeb, 0xec, 0xed, 0x8c, 0xee, 0x07, 0xec, 0xee, 0xf0, 0xf2, 0xf0, 0xee, + 0xec, 0x82, 0xea, 0x08, 0xec, 0xee, 0xf0, 0xf1, 0xf4, 0xf1, 0xee, 0xeb, + 0x82, 0xe8, 0x07, 0xeb, 0xee, 0xf1, 0xf4, 0xf1, 0xee, 0xeb, 0x82, 0xe8, + 0x01, 0xeb, 0x87, 0xee, 0x8c, 0x2d, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, + 0x2d, 0x29, 0x82, 0x25, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, + 0x82, 0x25, 0x07, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x82, 0x25, + 0x08, 0x29, 0x2d, 0x31, 0x35, 0x31, 0x2d, 0x29, 0x25, 0x02, 0xe0, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, + 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, + 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, + 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, + 0xa3, 0xe0, 0x8c, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0xaf, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf1, 0x85, 0xe1, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf1, 0x85, 0xe1, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0x85, 0xe0, + 0x01, 0xf0, 0x85, 0xe0, 0x01, 0xf0, 0xc7, 0xe0, 0x01, 0xf0, 0xc7, 0xe0, + 0xaf, 0xe1, 0x01, 0x49, 0x8c, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, + 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, 0x8c, 0x69, 0x86, 0x89, + 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, + 0x92, 0x69, 0x86, 0x89, 0x82, 0x49, 0x84, 0x4c, 0x86, 0x69, 0x86, 0x89, + 0xa0, 0x49, 0x84, 0x4c, 0x86, 0x49, 0x98, 0x69, 0x86, 0x89, 0x86, 0x49, + 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x92, 0x69, 0x86, 0x89, + 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, 0x86, 0x89, + 0x8c, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, + 0x86, 0x49, 0xa0, 0x69, 0x84, 0x6c, 0x88, 0x69, 0x84, 0x6c, 0x92, 0x69, + 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, 0x86, 0x49, + 0x92, 0x69, 0x86, 0x89, 0x86, 0x49, 0x82, 0x69, 0x84, 0x6c, 0x86, 0x89, + 0x86, 0x49, 0x86, 0x89, 0x9a, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, + 0xac, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, 0xac, 0x49, 0x84, 0x4c, + 0x92, 0x49, 0x9a, 0x69, 0x84, 0x6c, 0x88, 0x69, 0x84, 0x6c, 0x85, 0x69, + 0x02, 0xf8, 0xea, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, + 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, + 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xea, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xf0, 0x83, + 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xe2, 0x85, 0xf8, 0x01, 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xea, 0x85, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, + 0xe8, 0x85, 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe8, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe8, 0x85, 0xf8, 0x01, 0xe2, 0x85, + 0xf8, 0x01, 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x85, 0xf8, 0x01, + 0xf0, 0x83, 0xe8, 0x82, 0xf8, 0x01, 0xe2, 0x84, 0xf8, 0x01, 0x0d, 0x98, + 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, + 0x10, 0x86, 0x0a, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, + 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0a, 0x86, 0x09, 0x86, + 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, + 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0b, 0x86, + 0x10, 0x86, 0x09, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, + 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x86, 0x0b, 0x86, + 0x0a, 0x86, 0x09, 0x86, 0x0d, 0x98, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, + 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, 0x10, 0x86, 0x0a, 0x86, 0x0d, 0x86, + 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x86, + 0x10, 0x86, 0x0a, 0x86, 0x09, 0x86, 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, + 0x0d, 0x86, 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, + 0x0d, 0x92, 0x10, 0x86, 0x0b, 0x86, 0x10, 0x86, 0x09, 0x86, 0x0d, 0x86, + 0x10, 0x86, 0x0d, 0x8c, 0x10, 0x86, 0x0d, 0x92, 0x10, 0x86, 0x0d, 0x8c, + 0x10, 0x86, 0x0d, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x85, 0x0d, 0x02, + 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0d, 0x86, 0x0b, + 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, + 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, + 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, + 0x0c, 0x0a, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, + 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, + 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, + 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, + 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, + 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, + 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, + 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, + 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, + 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, + 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, + 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, + 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x01, 0x0f, 0x82, 0x0e, 0x02, 0x0d, + 0x0c, 0x87, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, + 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, + 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, 0x0a, + 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, 0x0d, + 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, + 0x86, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, + 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, + 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, + 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0d, 0x86, + 0x0b, 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, + 0x0b, 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, + 0x00, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x09, 0x01, 0x0f, 0x82, 0x0e, 0x03, + 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, + 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, + 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, + 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, + 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, + 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, + 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, + 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, + 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, + 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, + 0x0b, 0x86, 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, + 0x0b, 0x86, 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, + 0x00, 0x86, 0x0b, 0x86, 0x0a, 0x86, 0x0d, 0x01, 0x0f, 0x82, 0x0e, 0x02, + 0x0d, 0x0c, 0x87, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, + 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x04, 0x0c, + 0x09, 0x00, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, 0x0b, 0x86, + 0x0a, 0x01, 0x0f, 0x82, 0x0e, 0x03, 0x0d, 0x0c, 0x0a, 0x86, 0x0b, 0x86, + 0x0d, 0x86, 0x0b, 0x01, 0x2c, 0x82, 0x0e, 0x03, 0x0c, 0x09, 0x00, 0x86, + 0x0b, 0x86, 0x0a, 0x01, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, 0x00, 0x0f, + 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x04, 0x0c, 0x09, + 0x00, 0x0f, 0x82, 0x0e, 0x04, 0x0d, 0x0c, 0x0a, 0x2c, 0x82, 0x0e, 0x02, + 0x0c, 0x09, 0x02, 0x0e, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xaf, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xaf, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, + 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0x85, 0x0e, 0x01, 0x0f, 0xc7, 0x0e, 0x01, + 0x0f, 0xf6, 0x0e, 0x01, 0x4b, 0xff, 0x64, 0xc1, 0x64, 0xe0, 0x54, 0xe0, + 0x4b, 0xff, 0x64, 0xc1, 0x64, 0xe0, 0x54, 0xdf, 0x4b, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x86, 0x00, 0x02, + 0xff, 0x0e, 0x9d, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, + 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, + 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x8b, 0xff, + 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, + 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, + 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x9d, 0xff, + 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x91, 0xff, + 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, + 0x01, 0x0e, 0xa9, 0xff, 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, + 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x97, 0xff, 0x01, 0x0e, 0x91, 0xff, + 0x01, 0x0e, 0x8b, 0xff, 0x01, 0x0e, 0x91, 0xff, 0x01, 0x0e, 0x97, 0xff, + 0x01, 0x0e, 0xa8, 0xff}; unsigned int cut_rle_bin_len = 4504; diff --git a/firmware/baseband/audio_compressor.cpp b/firmware/baseband/audio_compressor.cpp index 95bbaee30..7b7c69858 100644 --- a/firmware/baseband/audio_compressor.cpp +++ b/firmware/baseband/audio_compressor.cpp @@ -22,31 +22,31 @@ #include "audio_compressor.hpp" float GainComputer::operator()(const float x) const { - const auto abs_x = std::abs(x); - const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x); - const auto overshoot_db = db - threshold_db; - if( knee_width_db > 0.0f ) { - const auto w2 = knee_width_db / 2.0f; - const auto a = w2 / (knee_width_db * knee_width_db); - const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2); - const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f); - return rectified_overshoot * slope; - } else { - const auto rectified_overshoot = std::max(overshoot_db, 0.0f); - return rectified_overshoot * slope; - } + const auto abs_x = std::abs(x); + const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x); + const auto overshoot_db = db - threshold_db; + if (knee_width_db > 0.0f) { + const auto w2 = knee_width_db / 2.0f; + const auto a = w2 / (knee_width_db * knee_width_db); + const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2); + const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f); + return rectified_overshoot * slope; + } else { + const auto rectified_overshoot = std::max(overshoot_db, 0.0f); + return rectified_overshoot * slope; + } } void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) { - constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f); - for(size_t i=0; i state) ? att_a : rel_a; - state = db + a * (state - db); - return state; - } - -private: - float state { 0.0f }; - const float att_a; - const float rel_a; + public: + constexpr PeakDetectorBranchingSmooth( + float att_a, + float rel_a) + : att_a{att_a}, + rel_a{rel_a} { + } + + float operator()(const float db) { + const auto a = (db > state) ? att_a : rel_a; + state = db + a * (state - db); + return state; + } + + private: + float state{0.0f}; + const float att_a; + const float rel_a; }; class FeedForwardCompressor { -public: - void execute_in_place(const buffer_f32_t& buffer); + public: + void execute_in_place(const buffer_f32_t& buffer); -private: - static constexpr float fs = 12000.0f; - static constexpr float ratio = 10.0f; - static constexpr float threshold = -30.0f; + private: + static constexpr float fs = 12000.0f; + static constexpr float ratio = 10.0f; + static constexpr float threshold = -30.0f; - GainComputer gain_computer { ratio, threshold }; - PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) }; + GainComputer gain_computer{ratio, threshold}; + PeakDetectorBranchingSmooth peak_detector{tau_alpha(0.010f, fs), tau_alpha(0.300f, fs)}; - float execute_once(const float x); + float execute_once(const float x); - static constexpr float tau_alpha(const float tau, const float fs) { - return std::exp(-1.0f / (tau * fs)); - } + static constexpr float tau_alpha(const float tau, const float fs) { + return std::exp(-1.0f / (tau * fs)); + } }; -#endif/*__AUDIO_COMPRESSOR_H__*/ +#endif /*__AUDIO_COMPRESSOR_H__*/ diff --git a/firmware/baseband/audio_dma.cpp b/firmware/baseband/audio_dma.cpp index 1f2828558..4430a08f2 100644 --- a/firmware/baseband/audio_dma.cpp +++ b/firmware/baseband/audio_dma.cpp @@ -40,84 +40,84 @@ constexpr uint32_t gpdma_ahb_master_peripheral = 1; constexpr uint32_t gpdma_ahb_master_memory = 0; constexpr uint32_t gpdma_ahb_master_lli_fetch = 0; -constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */ +constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */ constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral; constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral; -constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */ +constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */ constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral; constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral; constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) { - return { - .lm = gpdma_ahb_master_lli_fetch, - .r = 0, - .lli = reinterpret_cast(lli), - }; + return { + .lm = gpdma_ahb_master_lli_fetch, + .r = 0, + .lli = reinterpret_cast(lli), + }; } constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) { - return { - .transfersize = gpdma::buffer_words(transfer_bytes, 4), - .sbsize = 4, /* Burst size: 32 */ - .dbsize = 4, /* Burst size: 32 */ - .swidth = 2, /* Source transfer width: word (32 bits) */ - .dwidth = 2, /* Destination transfer width: word (32 bits) */ - .s = gpdma_ahb_master_memory, - .d = gpdma_ahb_master_peripheral, - .si = 1, - .di = 0, - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = 1, - }; + return { + .transfersize = gpdma::buffer_words(transfer_bytes, 4), + .sbsize = 4, /* Burst size: 32 */ + .dbsize = 4, /* Burst size: 32 */ + .swidth = 2, /* Source transfer width: word (32 bits) */ + .dwidth = 2, /* Destination transfer width: word (32 bits) */ + .s = gpdma_ahb_master_memory, + .d = gpdma_ahb_master_peripheral, + .si = 1, + .di = 0, + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = 1, + }; } constexpr gpdma::channel::Config config_tx() { - return { - .e = 0, - .srcperipheral = gpdma_tx_src_peripheral, - .destperipheral = gpdma_tx_dest_peripheral, - .flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl, - .ie = 1, - .itc = 1, - .l = 0, - .a = 0, - .h = 0, - }; + return { + .e = 0, + .srcperipheral = gpdma_tx_src_peripheral, + .destperipheral = gpdma_tx_dest_peripheral, + .flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl, + .ie = 1, + .itc = 1, + .l = 0, + .a = 0, + .h = 0, + }; } constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) { - return { - .transfersize = gpdma::buffer_words(transfer_bytes, 4), - .sbsize = 4, /* Burst size: 32 */ - .dbsize = 4, /* Burst size: 32 */ - .swidth = 2, /* Source transfer width: word (32 bits) */ - .dwidth = 2, /* Destination transfer width: word (32 bits) */ - .s = gpdma_ahb_master_peripheral, - .d = gpdma_ahb_master_memory, - .si = 0, - .di = 1, - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = 1, - }; + return { + .transfersize = gpdma::buffer_words(transfer_bytes, 4), + .sbsize = 4, /* Burst size: 32 */ + .dbsize = 4, /* Burst size: 32 */ + .swidth = 2, /* Source transfer width: word (32 bits) */ + .dwidth = 2, /* Destination transfer width: word (32 bits) */ + .s = gpdma_ahb_master_peripheral, + .d = gpdma_ahb_master_memory, + .si = 0, + .di = 1, + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = 1, + }; } constexpr gpdma::channel::Config config_rx() { - return { - .e = 0, - .srcperipheral = gpdma_rx_src_peripheral, - .destperipheral = gpdma_rx_dest_peripheral, - .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, - .ie = 1, - .itc = 1, - .l = 0, - .a = 0, - .h = 0, - }; + return { + .e = 0, + .srcperipheral = gpdma_rx_src_peripheral, + .destperipheral = gpdma_rx_dest_peripheral, + .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, + .ie = 1, + .itc = 1, + .l = 0, + .a = 0, + .h = 0, + }; } /* TODO: Clean up terminology around "buffer", "transfer", "samples" */ @@ -145,94 +145,94 @@ static volatile const gpdma::channel::LLI* tx_next_lli = nullptr; static volatile const gpdma::channel::LLI* rx_next_lli = nullptr; static void tx_transfer_complete() { - tx_next_lli = gpdma_channel_i2s0_tx.next_lli(); + tx_next_lli = gpdma_channel_i2s0_tx.next_lli(); } static void tx_error() { - disable(); + disable(); } static void rx_transfer_complete() { - rx_next_lli = gpdma_channel_i2s0_rx.next_lli(); + rx_next_lli = gpdma_channel_i2s0_rx.next_lli(); } static void rx_error() { - disable(); + disable(); } void init() { - gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error); - gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error); + gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error); + gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error); - // LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral); - // LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral); + // LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral); + // LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral); } static void configure_tx() { - const auto peripheral = reinterpret_cast(&LPC_I2S0->TXFIFO); - const auto control_value = control_tx(transfer_bytes); - for(size_t i=0; i(&buffer_tx[i * transfer_samples]); - lli_tx_loop[i].srcaddr = memory; - lli_tx_loop[i].destaddr = peripheral; - lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]); - lli_tx_loop[i].control = control_value; - } + const auto peripheral = reinterpret_cast(&LPC_I2S0->TXFIFO); + const auto control_value = control_tx(transfer_bytes); + for (size_t i = 0; i < lli_tx_loop.size(); i++) { + const auto memory = reinterpret_cast(&buffer_tx[i * transfer_samples]); + lli_tx_loop[i].srcaddr = memory; + lli_tx_loop[i].destaddr = peripheral; + lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]); + lli_tx_loop[i].control = control_value; + } } static void configure_rx() { - const auto peripheral = reinterpret_cast(&LPC_I2S0->RXFIFO); - const auto control_value = control_rx(transfer_bytes); - for(size_t i=0; i(&buffer_rx[i * transfer_samples]); - lli_rx_loop[i].srcaddr = peripheral; - lli_rx_loop[i].destaddr = memory; - lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]); - lli_rx_loop[i].control = control_value; - } + const auto peripheral = reinterpret_cast(&LPC_I2S0->RXFIFO); + const auto control_value = control_rx(transfer_bytes); + for (size_t i = 0; i < lli_rx_loop.size(); i++) { + const auto memory = reinterpret_cast(&buffer_rx[i * transfer_samples]); + lli_rx_loop[i].srcaddr = peripheral; + lli_rx_loop[i].destaddr = memory; + lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]); + lli_rx_loop[i].control = control_value; + } } void configure() { - configure_tx(); - configure_rx(); + configure_tx(); + configure_rx(); } void enable() { - const auto gpdma_config_tx = config_tx(); - const auto gpdma_config_rx = config_rx(); + const auto gpdma_config_tx = config_tx(); + const auto gpdma_config_rx = config_rx(); - gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx); - gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx); + gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx); + gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx); - gpdma_channel_i2s0_tx.enable(); - gpdma_channel_i2s0_rx.enable(); + gpdma_channel_i2s0_tx.enable(); + gpdma_channel_i2s0_rx.enable(); } void disable() { - gpdma_channel_i2s0_tx.disable(); - gpdma_channel_i2s0_rx.disable(); + gpdma_channel_i2s0_tx.disable(); + gpdma_channel_i2s0_rx.disable(); } buffer_t tx_empty_buffer() { - const auto next_lli = tx_next_lli; - if( next_lli ) { - const size_t next_index = next_lli - &lli_tx_loop[0]; - const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; - return { reinterpret_cast(lli_tx_loop[free_index].srcaddr), transfer_samples }; - } else { - return { nullptr, 0 }; - } + const auto next_lli = tx_next_lli; + if (next_lli) { + const size_t next_index = next_lli - &lli_tx_loop[0]; + const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; + return {reinterpret_cast(lli_tx_loop[free_index].srcaddr), transfer_samples}; + } else { + return {nullptr, 0}; + } } buffer_t rx_empty_buffer() { - const auto next_lli = rx_next_lli; - if( next_lli ) { - const size_t next_index = next_lli - &lli_rx_loop[0]; - const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; - return { reinterpret_cast(lli_rx_loop[free_index].destaddr), transfer_samples }; - } else { - return { nullptr, 0 }; - } + const auto next_lli = rx_next_lli; + if (next_lli) { + const size_t next_index = next_lli - &lli_rx_loop[0]; + const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; + return {reinterpret_cast(lli_rx_loop[free_index].destaddr), transfer_samples}; + } else { + return {nullptr, 0}; + } } } /* namespace dma */ diff --git a/firmware/baseband/audio_dma.hpp b/firmware/baseband/audio_dma.hpp index 5fe16a27c..e4720001f 100644 --- a/firmware/baseband/audio_dma.hpp +++ b/firmware/baseband/audio_dma.hpp @@ -30,13 +30,13 @@ namespace audio { struct sample_t { - union { - struct { - int16_t left; - int16_t right; - }; - uint32_t raw; - }; + union { + struct { + int16_t left; + int16_t right; + }; + uint32_t raw; + }; }; using buffer_t = buffer_t; @@ -54,4 +54,4 @@ audio::buffer_t rx_empty_buffer(); } /* namespace dma */ } /* namespace audio */ -#endif/*__AUDIO_DMA_H__*/ +#endif /*__AUDIO_DMA_H__*/ diff --git a/firmware/baseband/audio_input.cpp b/firmware/baseband/audio_input.cpp index 85b697a75..11b0016df 100644 --- a/firmware/baseband/audio_input.cpp +++ b/firmware/baseband/audio_input.cpp @@ -33,8 +33,8 @@ #include void AudioInput::read_audio_buffer(buffer_s16_t& audio) { - auto audio_buffer = audio::dma::rx_empty_buffer(); - - for (size_t i=0; i class AudioInput { -public: - void read_audio_buffer(buffer_s16_t& audio); + public: + void read_audio_buffer(buffer_s16_t& audio); -private: - /*static constexpr float k = 32768.0f; - static constexpr float ki = 1.0f / k; + private: + /*static constexpr float k = 32768.0f; + static constexpr float ki = 1.0f / k; - IIRBiquadFilter hpf { };*/ + IIRBiquadFilter hpf { };*/ }; -#endif/*__AUDIO_INPUT_H__*/ +#endif /*__AUDIO_INPUT_H__*/ diff --git a/firmware/baseband/audio_output.cpp b/firmware/baseband/audio_output.cpp index 42854b7d7..8a895a87b 100644 --- a/firmware/baseband/audio_output.cpp +++ b/firmware/baseband/audio_output.cpp @@ -33,96 +33,88 @@ #include void AudioOutput::configure( - const bool do_proc -) { - do_processing = do_proc; + const bool do_proc) { + do_processing = do_proc; } void AudioOutput::configure( - const iir_biquad_config_t& hpf_config, - const iir_biquad_config_t& deemph_config, - const float squelch_threshold -) { - hpf.configure(hpf_config); - deemph.configure(deemph_config); - squelch.set_threshold(squelch_threshold); + const iir_biquad_config_t& hpf_config, + const iir_biquad_config_t& deemph_config, + const float squelch_threshold) { + hpf.configure(hpf_config); + deemph.configure(deemph_config); + squelch.set_threshold(squelch_threshold); } void AudioOutput::write( - const buffer_s16_t& audio -) { - std::array audio_f; - for(size_t i=0; i audio_f; + for (size_t i = 0; i < audio.count; i++) { + audio_f[i] = audio.p[i] * ki; + } + write(buffer_f32_t{ + audio_f.data(), + audio.count, + audio.sampling_rate}); } void AudioOutput::write( - const buffer_f32_t& audio -) { - block_buffer.feed( - audio, - [this](const buffer_f32_t& buffer) { - this->on_block(buffer); - } - ); + const buffer_f32_t& audio) { + block_buffer.feed( + audio, + [this](const buffer_f32_t& buffer) { + this->on_block(buffer); + }); } void AudioOutput::on_block( - const buffer_f32_t& audio -) { - if (do_processing) { - const auto audio_present_now = squelch.execute(audio); - - hpf.execute_in_place(audio); - deemph.execute_in_place(audio); - - audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0); - audio_present = (audio_present_history != 0); - - if( !audio_present ) { - for(size_t i=0; i audio_int; - - auto audio_buffer = audio::dma::tx_empty_buffer(); - for(size_t i=0; iwrite(audio_int.data(), audio_buffer.count * sizeof(audio_int[0])); - } - - feed_audio_stats(audio); + std::array audio_int; + + auto audio_buffer = audio::dma::tx_empty_buffer(); + for (size_t i = 0; i < audio_buffer.count; i++) { + const int32_t sample_int = audio.p[i] * k; + const int32_t sample_saturated = __SSAT(sample_int, 16); + audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated; + audio_int[i] = sample_saturated; + } + if (stream && send_to_fifo) { + stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0])); + } + + feed_audio_stats(audio); } void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) { - audio_stats.feed( - audio, - [](const AudioStatistics& statistics) { - const AudioStatisticsMessage audio_stats_message { statistics }; - shared_memory.application_queue.push(audio_stats_message); - } - ); + audio_stats.feed( + audio, + [](const AudioStatistics& statistics) { + const AudioStatisticsMessage audio_stats_message{statistics}; + shared_memory.application_queue.push(audio_stats_message); + }); } diff --git a/firmware/baseband/audio_output.hpp b/firmware/baseband/audio_output.hpp index 517f63409..84104920d 100644 --- a/firmware/baseband/audio_output.hpp +++ b/firmware/baseband/audio_output.hpp @@ -36,46 +36,45 @@ #include class AudioOutput { -public: - void configure(const bool do_proc); - - void configure( - const iir_biquad_config_t& hpf_config, - const iir_biquad_config_t& deemph_config = iir_config_passthrough, - const float squelch_threshold = 0.0f - ); - - void write(const buffer_s16_t& audio); - void write(const buffer_f32_t& audio); - - void set_stream(std::unique_ptr new_stream) { - stream = std::move(new_stream); - } - - bool is_squelched(); - -private: - static constexpr float k = 32768.0f; - static constexpr float ki = 1.0f / k; - - BlockDecimator block_buffer { 1 }; - - IIRBiquadFilter hpf { }; - IIRBiquadFilter deemph { }; - FMSquelch squelch { }; - - std::unique_ptr stream { }; - - AudioStatsCollector audio_stats { }; - - uint64_t audio_present_history = 0; - - bool audio_present = false; - bool do_processing = true; - - void on_block(const buffer_f32_t& audio); - void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo); - void feed_audio_stats(const buffer_f32_t& audio); + public: + void configure(const bool do_proc); + + void configure( + const iir_biquad_config_t& hpf_config, + const iir_biquad_config_t& deemph_config = iir_config_passthrough, + const float squelch_threshold = 0.0f); + + void write(const buffer_s16_t& audio); + void write(const buffer_f32_t& audio); + + void set_stream(std::unique_ptr new_stream) { + stream = std::move(new_stream); + } + + bool is_squelched(); + + private: + static constexpr float k = 32768.0f; + static constexpr float ki = 1.0f / k; + + BlockDecimator block_buffer{1}; + + IIRBiquadFilter hpf{}; + IIRBiquadFilter deemph{}; + FMSquelch squelch{}; + + std::unique_ptr stream{}; + + AudioStatsCollector audio_stats{}; + + uint64_t audio_present_history = 0; + + bool audio_present = false; + bool do_processing = true; + + void on_block(const buffer_f32_t& audio); + void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo); + void feed_audio_stats(const buffer_f32_t& audio); }; -#endif/*__AUDIO_OUTPUT_H__*/ +#endif /*__AUDIO_OUTPUT_H__*/ diff --git a/firmware/baseband/audio_stats_collector.cpp b/firmware/baseband/audio_stats_collector.cpp index 53fceea80..5ed983912 100644 --- a/firmware/baseband/audio_stats_collector.cpp +++ b/firmware/baseband/audio_stats_collector.cpp @@ -24,44 +24,44 @@ #include "utility.hpp" void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) { - auto src_p = src.p; - const auto src_end = &src.p[src.count]; - while(src_p < src_end) { - const auto sample = *(src_p++); - const auto sample_squared = sample * sample; - squared_sum += sample_squared; - if( sample_squared > max_squared ) { - max_squared = sample_squared; - } - } + auto src_p = src.p; + const auto src_end = &src.p[src.count]; + while (src_p < src_end) { + const auto sample = *(src_p++); + const auto sample_squared = sample * sample; + squared_sum += sample_squared; + if (sample_squared > max_squared) { + max_squared = sample_squared; + } + } } bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) { - count += sample_count; + count += sample_count; - const size_t samples_per_update = sampling_rate * update_interval; + const size_t samples_per_update = sampling_rate * update_interval; - if( count >= samples_per_update ) { - statistics.rms_db = mag2_to_dbv_norm(squared_sum / count); - statistics.max_db = mag2_to_dbv_norm(max_squared); - statistics.count = count; + if (count >= samples_per_update) { + statistics.rms_db = mag2_to_dbv_norm(squared_sum / count); + statistics.max_db = mag2_to_dbv_norm(max_squared); + statistics.count = count; - squared_sum = 0; - max_squared = 0; - count = 0; + squared_sum = 0; + max_squared = 0; + count = 0; - return true; - } else { - return false; - } + return true; + } else { + return false; + } } bool AudioStatsCollector::feed(const buffer_f32_t& src) { - consume_audio_buffer(src); + consume_audio_buffer(src); - return update_stats(src.count, src.sampling_rate); + return update_stats(src.count, src.sampling_rate); } bool AudioStatsCollector::mute(const size_t sample_count, const size_t sampling_rate) { - return update_stats(sample_count, sampling_rate); + return update_stats(sample_count, sampling_rate); } diff --git a/firmware/baseband/audio_stats_collector.hpp b/firmware/baseband/audio_stats_collector.hpp index 9eddfd885..527323d34 100644 --- a/firmware/baseband/audio_stats_collector.hpp +++ b/firmware/baseband/audio_stats_collector.hpp @@ -29,35 +29,35 @@ #include class AudioStatsCollector { -public: - template - void feed(const buffer_f32_t& src, Callback callback) { - if( feed(src) ) { - callback(statistics); - } - } - - template - void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) { - if( mute(sample_count, sampling_rate) ) { - callback(statistics); - } - } - -private: - static constexpr float update_interval { 0.1f }; - float squared_sum { 0 }; - float max_squared { 0 }; - size_t count { 0 }; - - AudioStatistics statistics { }; - - void consume_audio_buffer(const buffer_f32_t& src); - - bool update_stats(const size_t sample_count, const size_t sampling_rate); - - bool feed(const buffer_f32_t& src); - bool mute(const size_t sample_count, const size_t sampling_rate); + public: + template + void feed(const buffer_f32_t& src, Callback callback) { + if (feed(src)) { + callback(statistics); + } + } + + template + void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) { + if (mute(sample_count, sampling_rate)) { + callback(statistics); + } + } + + private: + static constexpr float update_interval{0.1f}; + float squared_sum{0}; + float max_squared{0}; + size_t count{0}; + + AudioStatistics statistics{}; + + void consume_audio_buffer(const buffer_f32_t& src); + + bool update_stats(const size_t sample_count, const size_t sampling_rate); + + bool feed(const buffer_f32_t& src); + bool mute(const size_t sample_count, const size_t sampling_rate); }; -#endif/*__AUDIO_STATS_COLLECTOR_H__*/ +#endif /*__AUDIO_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband/baseband.cpp b/firmware/baseband/baseband.cpp index 198f1087f..5de5ec464 100644 --- a/firmware/baseband/baseband.cpp +++ b/firmware/baseband/baseband.cpp @@ -31,57 +31,56 @@ #include "audio_dma.hpp" static void init() { - audio::dma::init(); - audio::dma::configure(); - audio::dma::enable(); + audio::dma::init(); + audio::dma::configure(); + audio::dma::enable(); - nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); + nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); } static void halt() { - port_disable(); - while(true) { - port_wait_for_interrupt(); - } + port_disable(); + while (true) { + port_wait_for_interrupt(); + } } extern "C" { void __late_init(void) { - /* - * System initializations. - * - HAL initialization, this also initializes the configured device drivers - * and performs the board-specific initializations. - * - Kernel initialization, the main() function becomes a thread and the - * RTOS is active. - */ - halInit(); - - /* After this call, scheduler, systick, heap, etc. are available. */ - /* By doing chSysInit() here, it runs before C++ constructors, which may - * require the heap. - */ - chSysInit(); - - /* Baseband initialization */ - init(); + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + + /* After this call, scheduler, systick, heap, etc. are available. */ + /* By doing chSysInit() here, it runs before C++ constructors, which may + * require the heap. + */ + chSysInit(); + + /* Baseband initialization */ + init(); } void _default_exit(void) { - // TODO: Is this complete? - - nvicDisableVector(DMA_IRQn); - - chSysDisable(); + // TODO: Is this complete? - systick_stop(); + nvicDisableVector(DMA_IRQn); - ShutdownMessage shutdown_message; - shared_memory.application_queue.push(shutdown_message); + chSysDisable(); - shared_memory.baseband_message = nullptr; + systick_stop(); - halt(); -} + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); + + shared_memory.baseband_message = nullptr; + halt(); +} } diff --git a/firmware/baseband/baseband_dma.cpp b/firmware/baseband/baseband_dma.cpp index de083c00d..412942661 100644 --- a/firmware/baseband/baseband_dma.cpp +++ b/firmware/baseband/baseband_dma.cpp @@ -46,45 +46,45 @@ constexpr uint32_t gpdma_src_peripheral = 0x0; constexpr uint32_t gpdma_dest_peripheral = 0x0; constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) { - return { - .lm = gpdma_ahb_master_lli_fetch, - .r = 0, - .lli = reinterpret_cast(lli), - }; + return { + .lm = gpdma_ahb_master_lli_fetch, + .r = 0, + .lli = reinterpret_cast(lli), + }; } constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) { - return { - .transfersize = buffer_words, - .sbsize = 0, /* Burst size: 1 */ - .dbsize = 0, /* Burst size: 1 */ - .swidth = 2, /* Source transfer width: word (32 bits) */ - .dwidth = 2, /* Destination transfer width: word (32 bits) */ - .s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio, - .d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory, - .si = (direction == baseband::Direction::Transmit) ? 1U : 0U, - .di = (direction == baseband::Direction::Transmit) ? 0U : 1U, - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = 1, - }; + return { + .transfersize = buffer_words, + .sbsize = 0, /* Burst size: 1 */ + .dbsize = 0, /* Burst size: 1 */ + .swidth = 2, /* Source transfer width: word (32 bits) */ + .dwidth = 2, /* Destination transfer width: word (32 bits) */ + .s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio, + .d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory, + .si = (direction == baseband::Direction::Transmit) ? 1U : 0U, + .di = (direction == baseband::Direction::Transmit) ? 0U : 1U, + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = 1, + }; } constexpr gpdma::channel::Config config(const baseband::Direction direction) { - return { - .e = 0, - .srcperipheral = gpdma_src_peripheral, - .destperipheral = gpdma_dest_peripheral, - .flowcntrl = (direction == baseband::Direction::Transmit) - ? gpdma::FlowControl::MemoryToPeripheral_DMAControl - : gpdma::FlowControl::PeripheralToMemory_DMAControl, - .ie = 1, - .itc = 1, - .l = 0, - .a = 0, - .h = 0, - }; + return { + .e = 0, + .srcperipheral = gpdma_src_peripheral, + .destperipheral = gpdma_dest_peripheral, + .flowcntrl = (direction == baseband::Direction::Transmit) + ? gpdma::FlowControl::MemoryToPeripheral_DMAControl + : gpdma::FlowControl::PeripheralToMemory_DMAControl, + .ie = 1, + .itc = 1, + .l = 0, + .a = 0, + .h = 0, + }; } constexpr size_t buffer_samples_log2n = 13; @@ -108,75 +108,74 @@ volatile uint32_t buffer_transfered = 0; volatile uint32_t buffer_handled = 0; static void transfer_complete() { - const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0]; - buffer_transfered++; - thread_wait.wake_from_interrupt(next_lli_index); + const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0]; + buffer_transfered++; + thread_wait.wake_from_interrupt(next_lli_index); } static void dma_error() { - thread_wait.wake_from_interrupt(-1); - disable(); + thread_wait.wake_from_interrupt(-1); + disable(); } void init() { - gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error); + gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error); #if defined(PORTAPACK_BASEBAND_DMA_NO_SYNC) - /* Disable synchronization logic to improve(?) DMA response time. - * SGPIO (peripheral) must be on same clock as GPDMA peripheral. - * SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal - * operation, same as the M4 and M0 cores. Memory, of course, is - * running from the same clock as the cores. - */ - LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral); - LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral); + /* Disable synchronization logic to improve(?) DMA response time. + * SGPIO (peripheral) must be on same clock as GPDMA peripheral. + * SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal + * operation, same as the M4 and M0 cores. Memory, of course, is + * running from the same clock as the cores. + */ + LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral); + LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral); #endif } void configure( - baseband::sample_t* const buffer_base, - const baseband::Direction direction -) { - const auto peripheral = reinterpret_cast(&LPC_SGPIO->REG_SS[0]); - const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4)); - for(size_t i=0; i(&buffer_base[i * transfer_samples]); - lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral; - lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory; - lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]); - lli_loop[i].control = control_value; - } + baseband::sample_t* const buffer_base, + const baseband::Direction direction) { + const auto peripheral = reinterpret_cast(&LPC_SGPIO->REG_SS[0]); + const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4)); + for (size_t i = 0; i < lli_loop.size(); i++) { + const auto memory = reinterpret_cast(&buffer_base[i * transfer_samples]); + lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral; + lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory; + lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]); + lli_loop[i].control = control_value; + } } void enable(const baseband::Direction direction) { - const auto gpdma_config = config(direction); - gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config); - gpdma_channel_sgpio.enable(); + const auto gpdma_config = config(direction); + gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config); + gpdma_channel_sgpio.enable(); } bool is_enabled() { - return gpdma_channel_sgpio.is_enabled(); + return gpdma_channel_sgpio.is_enabled(); } void disable() { - gpdma_channel_sgpio.disable(); + gpdma_channel_sgpio.disable(); } baseband::buffer_t wait_for_buffer() { - const auto next_index = thread_wait.sleep(); - buffer_handled++; - - auto buffer_missed = buffer_transfered - buffer_handled; - shared_memory.m4_buffer_missed = buffer_missed; - - if( next_index >= 0 ) { - const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; - const auto src = lli_loop[free_index].srcaddr; - const auto dst = lli_loop[free_index].destaddr; - const auto p = (src == reinterpret_cast(&LPC_SGPIO->REG_SS[0])) ? dst : src; - return { reinterpret_cast(p), transfer_samples }; - } else { - return { }; - } + const auto next_index = thread_wait.sleep(); + buffer_handled++; + + auto buffer_missed = buffer_transfered - buffer_handled; + shared_memory.m4_buffer_missed = buffer_missed; + + if (next_index >= 0) { + const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; + const auto src = lli_loop[free_index].srcaddr; + const auto dst = lli_loop[free_index].destaddr; + const auto p = (src == reinterpret_cast(&LPC_SGPIO->REG_SS[0])) ? dst : src; + return {reinterpret_cast(p), transfer_samples}; + } else { + return {}; + } } } /* namespace dma */ diff --git a/firmware/baseband/baseband_dma.hpp b/firmware/baseband/baseband_dma.hpp index 56752ddcc..97c30d45a 100644 --- a/firmware/baseband/baseband_dma.hpp +++ b/firmware/baseband/baseband_dma.hpp @@ -33,9 +33,8 @@ namespace dma { void init(); void configure( - baseband::sample_t* const buffer_base, - const baseband::Direction direction -); + baseband::sample_t* const buffer_base, + const baseband::Direction direction); void enable(const baseband::Direction direction); bool is_enabled(); @@ -47,4 +46,4 @@ baseband::buffer_t wait_for_buffer(); } /* namespace dma */ } /* namespace baseband */ -#endif/*__BASEBAND_DMA_H__*/ +#endif /*__BASEBAND_DMA_H__*/ diff --git a/firmware/baseband/baseband_processor.cpp b/firmware/baseband/baseband_processor.cpp index 4113dec5b..19004cc30 100644 --- a/firmware/baseband/baseband_processor.cpp +++ b/firmware/baseband/baseband_processor.cpp @@ -26,11 +26,10 @@ #include "message.hpp" void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) { - channel_stats.feed( - channel, - [](const ChannelStatistics& statistics) { - const ChannelStatisticsMessage channel_stats_message { statistics }; - shared_memory.application_queue.push(channel_stats_message); - } - ); + channel_stats.feed( + channel, + [](const ChannelStatistics& statistics) { + const ChannelStatisticsMessage channel_stats_message{statistics}; + shared_memory.application_queue.push(channel_stats_message); + }); } diff --git a/firmware/baseband/baseband_processor.hpp b/firmware/baseband/baseband_processor.hpp index 074853b27..7f8ea6f04 100644 --- a/firmware/baseband/baseband_processor.hpp +++ b/firmware/baseband/baseband_processor.hpp @@ -29,18 +29,18 @@ #include "message.hpp" class BasebandProcessor { -public: - virtual ~BasebandProcessor() = default; + public: + virtual ~BasebandProcessor() = default; - virtual void execute(const buffer_c8_t& buffer) = 0; + virtual void execute(const buffer_c8_t& buffer) = 0; - virtual void on_message(const Message* const) { }; + virtual void on_message(const Message* const){}; -protected: - void feed_channel_stats(const buffer_c16_t& channel); + protected: + void feed_channel_stats(const buffer_c16_t& channel); -private: - ChannelStatsCollector channel_stats { }; + private: + ChannelStatsCollector channel_stats{}; }; -#endif/*__BASEBAND_PROCESSOR_H__*/ +#endif /*__BASEBAND_PROCESSOR_H__*/ diff --git a/firmware/baseband/baseband_stats_collector.cpp b/firmware/baseband/baseband_stats_collector.cpp index 8eec12c5e..255c43637 100644 --- a/firmware/baseband/baseband_stats_collector.cpp +++ b/firmware/baseband/baseband_stats_collector.cpp @@ -24,36 +24,36 @@ #include "lpc43xx_cpp.hpp" bool BasebandStatsCollector::process(const buffer_c8_t& buffer) { - samples += buffer.count; + samples += buffer.count; - const size_t report_samples = buffer.sampling_rate * report_interval; - const auto report_delta = samples - samples_last_report; - return report_delta >= report_samples; + const size_t report_samples = buffer.sampling_rate * report_interval; + const auto report_delta = samples - samples_last_report; + return report_delta >= report_samples; } BasebandStatistics BasebandStatsCollector::capture_statistics() { - BasebandStatistics statistics; + BasebandStatistics statistics; - const auto idle_ticks = thread_idle->total_ticks; - statistics.idle_ticks = (idle_ticks - last_idle_ticks); - last_idle_ticks = idle_ticks; + const auto idle_ticks = thread_idle->total_ticks; + statistics.idle_ticks = (idle_ticks - last_idle_ticks); + last_idle_ticks = idle_ticks; - const auto main_ticks = thread_main->total_ticks; - statistics.main_ticks = (main_ticks - last_main_ticks); - last_main_ticks = main_ticks; + const auto main_ticks = thread_main->total_ticks; + statistics.main_ticks = (main_ticks - last_main_ticks); + last_main_ticks = main_ticks; - const auto rssi_ticks = thread_rssi->total_ticks; - statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks); - last_rssi_ticks = rssi_ticks; + const auto rssi_ticks = thread_rssi->total_ticks; + statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks); + last_rssi_ticks = rssi_ticks; - const auto baseband_ticks = thread_baseband->total_ticks; - statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks); - last_baseband_ticks = baseband_ticks; + const auto baseband_ticks = thread_baseband->total_ticks; + statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks); + last_baseband_ticks = baseband_ticks; - statistics.saturation = lpc43xx::m4::flag_saturation(); - lpc43xx::m4::clear_flag_saturation(); + statistics.saturation = lpc43xx::m4::flag_saturation(); + lpc43xx::m4::clear_flag_saturation(); - samples_last_report = samples; + samples_last_report = samples; - return statistics; + return statistics; } diff --git a/firmware/baseband/baseband_stats_collector.hpp b/firmware/baseband/baseband_stats_collector.hpp index b628a8b2b..82082123b 100644 --- a/firmware/baseband/baseband_stats_collector.hpp +++ b/firmware/baseband/baseband_stats_collector.hpp @@ -31,41 +31,40 @@ #include class BasebandStatsCollector { -public: - BasebandStatsCollector( - const Thread* const thread_idle, - const Thread* const thread_main, - const Thread* const thread_rssi, - const Thread* const thread_baseband - ) : thread_idle { thread_idle }, - thread_main { thread_main }, - thread_rssi { thread_rssi }, - thread_baseband { thread_baseband } - { - } + public: + BasebandStatsCollector( + const Thread* const thread_idle, + const Thread* const thread_main, + const Thread* const thread_rssi, + const Thread* const thread_baseband) + : thread_idle{thread_idle}, + thread_main{thread_main}, + thread_rssi{thread_rssi}, + thread_baseband{thread_baseband} { + } - template - void process(const buffer_c8_t& buffer, Callback callback) { - if( process(buffer) ) { - callback(capture_statistics()); - } - } + template + void process(const buffer_c8_t& buffer, Callback callback) { + if (process(buffer)) { + callback(capture_statistics()); + } + } -private: - static constexpr float report_interval { 1.0f }; - size_t samples { 0 }; - size_t samples_last_report { 0 }; - const Thread* const thread_idle; - uint32_t last_idle_ticks { 0 }; - const Thread* const thread_main; - uint32_t last_main_ticks { 0 }; - const Thread* const thread_rssi; - uint32_t last_rssi_ticks { 0 }; - const Thread* const thread_baseband; - uint32_t last_baseband_ticks { 0 }; + private: + static constexpr float report_interval{1.0f}; + size_t samples{0}; + size_t samples_last_report{0}; + const Thread* const thread_idle; + uint32_t last_idle_ticks{0}; + const Thread* const thread_main; + uint32_t last_main_ticks{0}; + const Thread* const thread_rssi; + uint32_t last_rssi_ticks{0}; + const Thread* const thread_baseband; + uint32_t last_baseband_ticks{0}; - bool process(const buffer_c8_t& buffer); - BasebandStatistics capture_statistics(); + bool process(const buffer_c8_t& buffer); + BasebandStatistics capture_statistics(); }; -#endif/*__BASEBAND_STATS_COLLECTOR_H__*/ +#endif /*__BASEBAND_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband/baseband_thread.cpp b/firmware/baseband/baseband_thread.cpp index e6d7d0343..cc0431629 100644 --- a/firmware/baseband/baseband_thread.cpp +++ b/firmware/baseband/baseband_thread.cpp @@ -44,60 +44,56 @@ WORKING_AREA(baseband_thread_wa, 4096); Thread* BasebandThread::thread = nullptr; BasebandThread::BasebandThread( - uint32_t sampling_rate, - BasebandProcessor* const baseband_processor, - const tprio_t priority, - baseband::Direction direction -) : baseband_processor { baseband_processor }, - _direction { direction }, - sampling_rate { sampling_rate } -{ - thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), - priority, ThreadBase::fn, - this - ); + uint32_t sampling_rate, + BasebandProcessor* const baseband_processor, + const tprio_t priority, + baseband::Direction direction) + : baseband_processor{baseband_processor}, + _direction{direction}, + sampling_rate{sampling_rate} { + thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), + priority, ThreadBase::fn, + this); } BasebandThread::~BasebandThread() { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; } void BasebandThread::set_sampling_rate(uint32_t new_sampling_rate) { - sampling_rate = new_sampling_rate; + sampling_rate = new_sampling_rate; } void BasebandThread::run() { - baseband_sgpio.init(); - baseband::dma::init(); - - const auto baseband_buffer = std::make_unique>(); - baseband::dma::configure( - baseband_buffer->data(), - direction() - ); - //baseband::dma::allocate(4, 2048); - - baseband_sgpio.configure(direction()); - baseband::dma::enable(direction()); - baseband_sgpio.streaming_enable(); - - while( !chThdShouldTerminate() ) { - // TODO: Place correct sampling rate into buffer returned here: - const auto buffer_tmp = baseband::dma::wait_for_buffer(); - if( buffer_tmp ) { - buffer_c8_t buffer { - buffer_tmp.p, buffer_tmp.count, sampling_rate - }; - - if( baseband_processor ) { - baseband_processor->execute(buffer); - } - } - } - - i2s::i2s0::tx_mute(); - baseband::dma::disable(); - baseband_sgpio.streaming_disable(); + baseband_sgpio.init(); + baseband::dma::init(); + + const auto baseband_buffer = std::make_unique>(); + baseband::dma::configure( + baseband_buffer->data(), + direction()); + // baseband::dma::allocate(4, 2048); + + baseband_sgpio.configure(direction()); + baseband::dma::enable(direction()); + baseband_sgpio.streaming_enable(); + + while (!chThdShouldTerminate()) { + // TODO: Place correct sampling rate into buffer returned here: + const auto buffer_tmp = baseband::dma::wait_for_buffer(); + if (buffer_tmp) { + buffer_c8_t buffer{ + buffer_tmp.p, buffer_tmp.count, sampling_rate}; + + if (baseband_processor) { + baseband_processor->execute(buffer); + } + } + } + + i2s::i2s0::tx_mute(); + baseband::dma::disable(); + baseband_sgpio.streaming_disable(); } diff --git a/firmware/baseband/baseband_thread.hpp b/firmware/baseband/baseband_thread.hpp index a168d0453..5f9287009 100644 --- a/firmware/baseband/baseband_thread.hpp +++ b/firmware/baseband/baseband_thread.hpp @@ -29,36 +29,35 @@ #include class BasebandThread : public ThreadBase { -public: - BasebandThread( - uint32_t sampling_rate, - BasebandProcessor* const baseband_processor, - const tprio_t priority, - const baseband::Direction direction = baseband::Direction::Receive - ); - ~BasebandThread(); - - BasebandThread(const BasebandThread&) = delete; - BasebandThread(BasebandThread&&) = delete; - BasebandThread& operator=(const BasebandThread&) = delete; - BasebandThread& operator=(BasebandThread&&) = delete; - - // This getter should die, it's just here to leak information to code that - // isn't in the right place to begin with. - baseband::Direction direction() const { - return _direction; - } - - void set_sampling_rate(uint32_t new_sampling_rate); - -private: - static Thread* thread; - - BasebandProcessor* baseband_processor { nullptr }; - baseband::Direction _direction { baseband::Direction::Receive }; - uint32_t sampling_rate { 0 }; - - void run() override; + public: + BasebandThread( + uint32_t sampling_rate, + BasebandProcessor* const baseband_processor, + const tprio_t priority, + const baseband::Direction direction = baseband::Direction::Receive); + ~BasebandThread(); + + BasebandThread(const BasebandThread&) = delete; + BasebandThread(BasebandThread&&) = delete; + BasebandThread& operator=(const BasebandThread&) = delete; + BasebandThread& operator=(BasebandThread&&) = delete; + + // This getter should die, it's just here to leak information to code that + // isn't in the right place to begin with. + baseband::Direction direction() const { + return _direction; + } + + void set_sampling_rate(uint32_t new_sampling_rate); + + private: + static Thread* thread; + + BasebandProcessor* baseband_processor{nullptr}; + baseband::Direction _direction{baseband::Direction::Receive}; + uint32_t sampling_rate{0}; + + void run() override; }; -#endif/*__BASEBAND_THREAD_H__*/ +#endif /*__BASEBAND_THREAD_H__*/ diff --git a/firmware/baseband/block_decimator.hpp b/firmware/baseband/block_decimator.hpp index a3a771c4f..fb8dd4e94 100644 --- a/firmware/baseband/block_decimator.hpp +++ b/firmware/baseband/block_decimator.hpp @@ -29,72 +29,71 @@ #include "dsp_types.hpp" #include "complex.hpp" -template +template class BlockDecimator { -public: - constexpr BlockDecimator( - const size_t factor - ) : factor_ { factor } - { - } - - void set_input_sampling_rate(const uint32_t new_sampling_rate) { - if( new_sampling_rate != input_sampling_rate() ) { - input_sampling_rate_ = new_sampling_rate; - reset_state(); - } - } - - uint32_t input_sampling_rate() const { - return input_sampling_rate_; - } - - void set_factor(const size_t new_factor) { - if( new_factor != factor() ) { - factor_ = new_factor; - reset_state(); - } - } - - size_t factor() const { - return factor_; - } - - uint32_t output_sampling_rate() const { - return input_sampling_rate() / factor(); - } - - template - void feed(const buffer_t& src, BlockCallback callback) { - /* NOTE: Input block size must be >= factor */ - - set_input_sampling_rate(src.sampling_rate); - - while( src_i < src.count ) { - buffer[dst_i++] = src.p[src_i]; - if( dst_i == buffer.size() ) { - callback({ buffer.data(), buffer.size(), output_sampling_rate() }); - reset_state(); - dst_i = 0; - } - - src_i += factor(); - } - - src_i -= src.count; - } - -private: - std::array buffer { }; - uint32_t input_sampling_rate_ { 0 }; - size_t factor_ { 1 }; - size_t src_i { 0 }; - size_t dst_i { 0 }; - - void reset_state() { - src_i = 0; - dst_i = 0; - } + public: + constexpr BlockDecimator( + const size_t factor) + : factor_{factor} { + } + + void set_input_sampling_rate(const uint32_t new_sampling_rate) { + if (new_sampling_rate != input_sampling_rate()) { + input_sampling_rate_ = new_sampling_rate; + reset_state(); + } + } + + uint32_t input_sampling_rate() const { + return input_sampling_rate_; + } + + void set_factor(const size_t new_factor) { + if (new_factor != factor()) { + factor_ = new_factor; + reset_state(); + } + } + + size_t factor() const { + return factor_; + } + + uint32_t output_sampling_rate() const { + return input_sampling_rate() / factor(); + } + + template + void feed(const buffer_t& src, BlockCallback callback) { + /* NOTE: Input block size must be >= factor */ + + set_input_sampling_rate(src.sampling_rate); + + while (src_i < src.count) { + buffer[dst_i++] = src.p[src_i]; + if (dst_i == buffer.size()) { + callback({buffer.data(), buffer.size(), output_sampling_rate()}); + reset_state(); + dst_i = 0; + } + + src_i += factor(); + } + + src_i -= src.count; + } + + private: + std::array buffer{}; + uint32_t input_sampling_rate_{0}; + size_t factor_{1}; + size_t src_i{0}; + size_t dst_i{0}; + + void reset_state() { + src_i = 0; + dst_i = 0; + } }; -#endif/*__BLOCK_DECIMATOR_H__*/ +#endif /*__BLOCK_DECIMATOR_H__*/ diff --git a/firmware/baseband/channel_decimator.cpp b/firmware/baseband/channel_decimator.cpp index c8a7cf5c8..c5b28e106 100644 --- a/firmware/baseband/channel_decimator.cpp +++ b/firmware/baseband/channel_decimator.cpp @@ -22,70 +22,67 @@ #include "channel_decimator.hpp" buffer_c16_t ChannelDecimator::execute_decimation(const buffer_c8_t& buffer) { - const buffer_c16_t work_baseband_buffer { - work_baseband.data(), - work_baseband.size() - }; + const buffer_c16_t work_baseband_buffer{ + work_baseband.data(), + work_baseband.size()}; - const buffer_s16_t work_audio_buffer { - (int16_t*)work_baseband.data(), - sizeof(work_baseband) / sizeof(int16_t) - }; + const buffer_s16_t work_audio_buffer{ + (int16_t*)work_baseband.data(), + sizeof(work_baseband) / sizeof(int16_t)}; - /* 3.072MHz complex[2048], [-128, 127] - * -> Shift by -fs/4 - * -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs - * -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz - * -> gain of 256 - * -> decimation by 2 - * -> 1.544MHz complex[1024], [-32768, 32512] */ - auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer); - if( decimation_factor == DecimationFactor::By2 ) { - return stage_0_out; - } + /* 3.072MHz complex[2048], [-128, 127] + * -> Shift by -fs/4 + * -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs + * -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz + * -> gain of 256 + * -> decimation by 2 + * -> 1.544MHz complex[1024], [-32768, 32512] */ + auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer); + if (decimation_factor == DecimationFactor::By2) { + return stage_0_out; + } - /* 1.536MHz complex[1024], [-32768, 32512] - * -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs - * -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz - * -> gain of 1 - * -> decimation by 2 - * -> 768kHz complex[512], [-8192, 8128] */ - auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer); - if( decimation_factor == DecimationFactor::By4 ) { - return cic_1_out; - } + /* 1.536MHz complex[1024], [-32768, 32512] + * -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs + * -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz + * -> gain of 1 + * -> decimation by 2 + * -> 768kHz complex[512], [-8192, 8128] */ + auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer); + if (decimation_factor == DecimationFactor::By4) { + return cic_1_out; + } - /* 768kHz complex[512], [-32768, 32512] - * -> 3rd order CIC decimation by 2, gain of 1 - * -> 384kHz complex[256], [-32768, 32512] */ - auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer); - if( decimation_factor == DecimationFactor::By8 ) { - return cic_2_out; - } + /* 768kHz complex[512], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 384kHz complex[256], [-32768, 32512] */ + auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer); + if (decimation_factor == DecimationFactor::By8) { + return cic_2_out; + } - /* 384kHz complex[256], [-32768, 32512] - * -> 3rd order CIC decimation by 2, gain of 1 - * -> 192kHz complex[128], [-32768, 32512] */ - auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer); - if( decimation_factor == DecimationFactor::By16 ) { - return cic_3_out; - } + /* 384kHz complex[256], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 192kHz complex[128], [-32768, 32512] */ + auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer); + if (decimation_factor == DecimationFactor::By16) { + return cic_3_out; + } - /* 192kHz complex[128], [-32768, 32512] - * -> 3rd order CIC decimation by 2, gain of 1 - * -> 96kHz complex[64], [-32768, 32512] */ - auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer); + /* 192kHz complex[128], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 96kHz complex[64], [-32768, 32512] */ + auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer); - return cic_4_out; + return cic_4_out; } buffer_c16_t ChannelDecimator::execute_stage_0( - const buffer_c8_t& buffer, - const buffer_c16_t& work_baseband_buffer -) { - if( fs_over_4_downconvert ) { - return translate.execute(buffer, work_baseband_buffer); - } else { - return cic_0.execute(buffer, work_baseband_buffer); - } + const buffer_c8_t& buffer, + const buffer_c16_t& work_baseband_buffer) { + if (fs_over_4_downconvert) { + return translate.execute(buffer, work_baseband_buffer); + } else { + return cic_0.execute(buffer, work_baseband_buffer); + } } diff --git a/firmware/baseband/channel_decimator.hpp b/firmware/baseband/channel_decimator.hpp index 4b527ee2c..1067b57d4 100644 --- a/firmware/baseband/channel_decimator.hpp +++ b/firmware/baseband/channel_decimator.hpp @@ -30,52 +30,50 @@ #include class ChannelDecimator { -public: - enum class DecimationFactor { - By2, - By4, - By8, - By16, - By32, - }; - - constexpr ChannelDecimator( - const DecimationFactor decimation_factor, - const bool fs_over_4_downconvert = true - ) : decimation_factor { decimation_factor }, - fs_over_4_downconvert { fs_over_4_downconvert } - { - } + public: + enum class DecimationFactor { + By2, + By4, + By8, + By16, + By32, + }; - void set_decimation_factor(const DecimationFactor f) { - decimation_factor = f; - } + constexpr ChannelDecimator( + const DecimationFactor decimation_factor, + const bool fs_over_4_downconvert = true) + : decimation_factor{decimation_factor}, + fs_over_4_downconvert{fs_over_4_downconvert} { + } - buffer_c16_t execute(const buffer_c8_t& buffer) { - auto decimated = execute_decimation(buffer); + void set_decimation_factor(const DecimationFactor f) { + decimation_factor = f; + } - return decimated; - } + buffer_c16_t execute(const buffer_c8_t& buffer) { + auto decimated = execute_decimation(buffer); -private: - std::array work_baseband { }; + return decimated; + } - dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate { }; - dsp::decimate::Complex8DecimateBy2CIC3 cic_0 { }; - dsp::decimate::DecimateBy2CIC3 cic_1 { }; - dsp::decimate::DecimateBy2CIC3 cic_2 { }; - dsp::decimate::DecimateBy2CIC3 cic_3 { }; - dsp::decimate::DecimateBy2CIC3 cic_4 { }; + private: + std::array work_baseband{}; - DecimationFactor decimation_factor { DecimationFactor::By32 }; - const bool fs_over_4_downconvert { true }; + dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate{}; + dsp::decimate::Complex8DecimateBy2CIC3 cic_0{}; + dsp::decimate::DecimateBy2CIC3 cic_1{}; + dsp::decimate::DecimateBy2CIC3 cic_2{}; + dsp::decimate::DecimateBy2CIC3 cic_3{}; + dsp::decimate::DecimateBy2CIC3 cic_4{}; - buffer_c16_t execute_decimation(const buffer_c8_t& buffer); + DecimationFactor decimation_factor{DecimationFactor::By32}; + const bool fs_over_4_downconvert{true}; - buffer_c16_t execute_stage_0( - const buffer_c8_t& buffer, - const buffer_c16_t& work_baseband_buffer - ); + buffer_c16_t execute_decimation(const buffer_c8_t& buffer); + + buffer_c16_t execute_stage_0( + const buffer_c8_t& buffer, + const buffer_c16_t& work_baseband_buffer); }; -#endif/*__CHANNEL_DECIMATOR_H__*/ +#endif /*__CHANNEL_DECIMATOR_H__*/ diff --git a/firmware/baseband/channel_stats_collector.hpp b/firmware/baseband/channel_stats_collector.hpp index f1e9061a1..7ed809e73 100644 --- a/firmware/baseband/channel_stats_collector.hpp +++ b/firmware/baseband/channel_stats_collector.hpp @@ -32,35 +32,35 @@ #include class ChannelStatsCollector { -public: - template - void feed(const buffer_c16_t& src, Callback callback) { - void *src_p = src.p; - while(src_p < &src.p[src.count]) { - const uint32_t sample = *__SIMD32(src_p)++; - const uint32_t mag_sq = __SMUAD(sample, sample); - if( mag_sq > max_squared ) { - max_squared = mag_sq; - } - } - count += src.count; + public: + template + void feed(const buffer_c16_t& src, Callback callback) { + void* src_p = src.p; + while (src_p < &src.p[src.count]) { + const uint32_t sample = *__SIMD32(src_p)++; + const uint32_t mag_sq = __SMUAD(sample, sample); + if (mag_sq > max_squared) { + max_squared = mag_sq; + } + } + count += src.count; - const size_t samples_per_update = src.sampling_rate * update_interval; + const size_t samples_per_update = src.sampling_rate * update_interval; - if( count >= samples_per_update ) { - const float max_squared_f = max_squared; - const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f))); - callback({ max_db, count }); + if (count >= samples_per_update) { + const float max_squared_f = max_squared; + const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f))); + callback({max_db, count}); - max_squared = 0; - count = 0; - } - } + max_squared = 0; + count = 0; + } + } -private: - static constexpr float update_interval { 0.1f }; - uint32_t max_squared { 0 }; - size_t count { 0 }; + private: + static constexpr float update_interval{0.1f}; + uint32_t max_squared{0}; + size_t count{0}; }; -#endif/*__CHANNEL_STATS_COLLECTOR_H__*/ +#endif /*__CHANNEL_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband/chconf.h b/firmware/baseband/chconf.h index f5b451962..5bb8ce7ab 100755 --- a/firmware/baseband/chconf.h +++ b/firmware/baseband/chconf.h @@ -42,7 +42,7 @@ * setting also defines the system tick time unit. */ #if !defined(CH_FREQUENCY) || defined(__DOXYGEN__) -#define CH_FREQUENCY 1000 +#define CH_FREQUENCY 1000 #endif /** @@ -57,7 +57,7 @@ * and generally faster. */ #if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__) -#define CH_TIME_QUANTUM 0 +#define CH_TIME_QUANTUM 0 #endif /** @@ -72,7 +72,7 @@ * @note Requires @p CH_USE_MEMCORE. */ #if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__) -#define CH_MEMCORE_SIZE 0 +#define CH_MEMCORE_SIZE 0 #endif /** @@ -89,7 +89,7 @@ * enter a sleep state. */ #if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__) -#define CH_NO_IDLE_THREAD FALSE +#define CH_NO_IDLE_THREAD FALSE #endif /** @} */ @@ -110,7 +110,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__) -#define CH_OPTIMIZE_SPEED TRUE +#define CH_OPTIMIZE_SPEED TRUE #endif /** @} */ @@ -129,7 +129,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__) -#define CH_USE_REGISTRY FALSE +#define CH_USE_REGISTRY FALSE #endif /** @@ -140,7 +140,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__) -#define CH_USE_WAITEXIT TRUE +#define CH_USE_WAITEXIT TRUE #endif /** @@ -150,7 +150,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__) -#define CH_USE_SEMAPHORES TRUE +#define CH_USE_SEMAPHORES TRUE #endif /** @@ -162,7 +162,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__) -#define CH_USE_SEMAPHORES_PRIORITY FALSE +#define CH_USE_SEMAPHORES_PRIORITY FALSE #endif /** @@ -174,7 +174,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__) -#define CH_USE_SEMSW TRUE +#define CH_USE_SEMSW TRUE #endif /** @@ -184,7 +184,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__) -#define CH_USE_MUTEXES TRUE +#define CH_USE_MUTEXES TRUE #endif /** @@ -196,7 +196,7 @@ * @note Requires @p CH_USE_MUTEXES. */ #if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__) -#define CH_USE_CONDVARS TRUE +#define CH_USE_CONDVARS TRUE #endif /** @@ -208,7 +208,7 @@ * @note Requires @p CH_USE_CONDVARS. */ #if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__) -#define CH_USE_CONDVARS_TIMEOUT TRUE +#define CH_USE_CONDVARS_TIMEOUT TRUE #endif /** @@ -218,7 +218,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__) -#define CH_USE_EVENTS TRUE +#define CH_USE_EVENTS TRUE #endif /** @@ -230,7 +230,7 @@ * @note Requires @p CH_USE_EVENTS. */ #if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) -#define CH_USE_EVENTS_TIMEOUT TRUE +#define CH_USE_EVENTS_TIMEOUT TRUE #endif /** @@ -241,7 +241,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__) -#define CH_USE_MESSAGES TRUE +#define CH_USE_MESSAGES TRUE #endif /** @@ -253,7 +253,7 @@ * @note Requires @p CH_USE_MESSAGES. */ #if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__) -#define CH_USE_MESSAGES_PRIORITY FALSE +#define CH_USE_MESSAGES_PRIORITY FALSE #endif /** @@ -265,7 +265,7 @@ * @note Requires @p CH_USE_SEMAPHORES. */ #if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__) -#define CH_USE_MAILBOXES TRUE +#define CH_USE_MAILBOXES TRUE #endif /** @@ -275,7 +275,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__) -#define CH_USE_QUEUES TRUE +#define CH_USE_QUEUES TRUE #endif /** @@ -286,7 +286,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__) -#define CH_USE_MEMCORE TRUE +#define CH_USE_MEMCORE TRUE #endif /** @@ -300,7 +300,7 @@ * @note Mutexes are recommended. */ #if !defined(CH_USE_HEAP) || defined(__DOXYGEN__) -#define CH_USE_HEAP TRUE +#define CH_USE_HEAP TRUE #endif /** @@ -314,7 +314,7 @@ * appropriate documentation. */ #if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__) -#define CH_USE_MALLOC_HEAP FALSE +#define CH_USE_MALLOC_HEAP FALSE #endif /** @@ -325,7 +325,7 @@ * @note The default is @p TRUE. */ #if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__) -#define CH_USE_MEMPOOLS TRUE +#define CH_USE_MEMPOOLS TRUE #endif /** @@ -338,7 +338,7 @@ * @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS. */ #if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__) -#define CH_USE_DYNAMIC TRUE +#define CH_USE_DYNAMIC TRUE #endif /** @} */ @@ -358,7 +358,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__) -#define CH_DBG_SYSTEM_STATE_CHECK TRUE +#define CH_DBG_SYSTEM_STATE_CHECK TRUE #endif /** @@ -369,7 +369,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_CHECKS TRUE +#define CH_DBG_ENABLE_CHECKS TRUE #endif /** @@ -381,7 +381,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_ASSERTS TRUE +#define CH_DBG_ENABLE_ASSERTS TRUE #endif /** @@ -392,7 +392,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_TRACE FALSE +#define CH_DBG_ENABLE_TRACE FALSE #endif /** @@ -406,7 +406,7 @@ * @p panic_msg variable set to @p NULL. */ #if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__) -#define CH_DBG_ENABLE_STACK_CHECK TRUE +#define CH_DBG_ENABLE_STACK_CHECK TRUE #endif /** @@ -418,7 +418,7 @@ * @note The default is @p FALSE. */ #if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__) -#define CH_DBG_FILL_THREADS TRUE +#define CH_DBG_FILL_THREADS TRUE #endif /** @@ -431,7 +431,7 @@ * some test cases into the test suite. */ #if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) -#define CH_DBG_THREADS_PROFILING TRUE +#define CH_DBG_THREADS_PROFILING TRUE #endif /** @} */ @@ -448,11 +448,11 @@ * @details User fields added to the end of the @p Thread structure. */ #if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__) -#define THREAD_EXT_FIELDS \ - /* Add threads custom fields here.*/ \ - uint32_t switches; \ - uint32_t start_ticks; \ - uint32_t total_ticks; +#define THREAD_EXT_FIELDS \ + /* Add threads custom fields here.*/ \ + uint32_t switches; \ + uint32_t start_ticks; \ + uint32_t total_ticks; #endif /** @@ -463,12 +463,13 @@ * the threads creation APIs. */ #if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__) -#define THREAD_EXT_INIT_HOOK(tp) { \ - /* Add threads initialization code here.*/ \ - tp->switches = 0; \ - tp->start_ticks = 0; \ - tp->total_ticks = 0; \ -} +#define THREAD_EXT_INIT_HOOK(tp) \ + { \ + /* Add threads initialization code here.*/ \ + tp->switches = 0; \ + tp->start_ticks = 0; \ + tp->total_ticks = 0; \ + } #endif /** @@ -480,9 +481,10 @@ * terminate. */ #if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__) -#define THREAD_EXT_EXIT_HOOK(tp) { \ - /* Add threads finalization code here.*/ \ -} +#define THREAD_EXT_EXIT_HOOK(tp) \ + { \ + /* Add threads finalization code here.*/ \ + } #endif /** @@ -490,12 +492,13 @@ * @details This hook is invoked just before switching between threads. */ #if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__) -#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \ - /* System halt code here.*/ \ - otp->switches++; \ - ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \ - otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \ -} +#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) \ + { \ + /* System halt code here.*/ \ + otp->switches++; \ + ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \ + otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \ + } #endif /** @@ -503,24 +506,24 @@ * @details This hook is continuously invoked by the idle thread loop. */ #if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__) -#define IDLE_LOOP_HOOK() { \ - /* Idle loop code here.*/ \ -} +#define IDLE_LOOP_HOOK() \ + { \ + /* Idle loop code here.*/ \ + } #endif - - /** * @brief System tick event hook. * @details This hook is invoked in the system tick handler immediately * after processing the virtual timers queue. */ #if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__) -#define SYSTEM_TICK_EVENT_HOOK() { \ - /* System tick event code here.*/ \ - extern void update_performance_counters(); \ - update_performance_counters(); \ -} +#define SYSTEM_TICK_EVENT_HOOK() \ + { \ + /* System tick event code here.*/ \ + extern void update_performance_counters(); \ + update_performance_counters(); \ + } #endif /** @@ -529,9 +532,10 @@ * the system is halted. */ #if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__) -#define SYSTEM_HALT_HOOK() { \ - /* System halt code here.*/ \ -} +#define SYSTEM_HALT_HOOK() \ + { \ + /* System halt code here.*/ \ + } #endif /** @} */ @@ -542,9 +546,9 @@ /* NOTE: When changing this option you also have to enable or disable the FPU in the project options.*/ -#define CORTEX_USE_FPU TRUE -#define CORTEX_ENABLE_WFI_IDLE TRUE +#define CORTEX_USE_FPU TRUE +#define CORTEX_ENABLE_WFI_IDLE TRUE -#endif /* _CHCONF_H_ */ +#endif /* _CHCONF_H_ */ /** @} */ diff --git a/firmware/baseband/clock_recovery.hpp b/firmware/baseband/clock_recovery.hpp index 70d7f003b..d015f38b6 100644 --- a/firmware/baseband/clock_recovery.hpp +++ b/firmware/baseband/clock_recovery.hpp @@ -31,155 +31,143 @@ namespace clock_recovery { class GardnerTimingErrorDetector { -public: - static constexpr size_t samples_per_symbol { 2 }; - - /* - Expects retimed samples at a rate of twice the expected symbol rate. - Calculates timing error, sends symbol and error to handler. - */ - template - void operator()( - const float in, - SymbolHandler symbol_handler - ) { - /* NOTE: Algorithm is sensitive to input magnitude. Timing error value - * will scale proportionally. Best practice is to use error sign only. - */ - t[2] = t[1]; - t[1] = t[0]; - t[0] = in; - - if( symbol_phase == 0 ) { - const auto symbol = t[0]; - const float lateness = (t[0] - t[2]) * t[1]; - symbol_handler(symbol, lateness); - } - - symbol_phase = (symbol_phase + 1) % samples_per_symbol; - } - -private: - std::array t { }; - size_t symbol_phase { 0 }; + public: + static constexpr size_t samples_per_symbol{2}; + + /* + Expects retimed samples at a rate of twice the expected symbol rate. + Calculates timing error, sends symbol and error to handler. + */ + template + void operator()( + const float in, + SymbolHandler symbol_handler) { + /* NOTE: Algorithm is sensitive to input magnitude. Timing error value + * will scale proportionally. Best practice is to use error sign only. + */ + t[2] = t[1]; + t[1] = t[0]; + t[0] = in; + + if (symbol_phase == 0) { + const auto symbol = t[0]; + const float lateness = (t[0] - t[2]) * t[1]; + symbol_handler(symbol, lateness); + } + + symbol_phase = (symbol_phase + 1) % samples_per_symbol; + } + + private: + std::array t{}; + size_t symbol_phase{0}; }; class LinearErrorFilter { -public: - LinearErrorFilter( - const float filter_alpha = 0.95f, - const float error_weight = -1.0f - ) : filter_alpha { filter_alpha }, - error_weight { error_weight } - { - } - - float operator()( - const float error - ) { - error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error; - return error_filtered * error_weight; - } - -private: - const float filter_alpha; - const float error_weight; - float error_filtered { 0.0f }; + public: + LinearErrorFilter( + const float filter_alpha = 0.95f, + const float error_weight = -1.0f) + : filter_alpha{filter_alpha}, + error_weight{error_weight} { + } + + float operator()( + const float error) { + error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error; + return error_filtered * error_weight; + } + + private: + const float filter_alpha; + const float error_weight; + float error_filtered{0.0f}; }; class FixedErrorFilter { -public: - FixedErrorFilter( - ) { - } - - FixedErrorFilter( - const float weight - ) : weight_ { weight } - { - } - - float operator()( - const float lateness - ) const { - return (lateness < 0.0f) ? weight() : -weight(); - } - - float weight() const { - return weight_; - } - -private: - float weight_ { 1.0f / 16.0f }; + public: + FixedErrorFilter() { + } + + FixedErrorFilter( + const float weight) + : weight_{weight} { + } + + float operator()( + const float lateness) const { + return (lateness < 0.0f) ? weight() : -weight(); + } + + float weight() const { + return weight_; + } + + private: + float weight_{1.0f / 16.0f}; }; -template +template class ClockRecovery { -public: - using SymbolHandler = std::function; - - ClockRecovery( - const float sampling_rate, - const float symbol_rate, - ErrorFilter error_filter, - SymbolHandler symbol_handler - ) : symbol_handler { std::move(symbol_handler) } - { - configure(sampling_rate, symbol_rate, error_filter); - } - - ClockRecovery( - SymbolHandler symbol_handler - ) : symbol_handler { std::move(symbol_handler) } - { - } - - void configure( - const float sampling_rate, - const float symbol_rate, - ErrorFilter error_filter - ) { - resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol); - error_filter = error_filter; - } - - void operator()( - const float baseband_sample - ) { - resampler(baseband_sample, - [this](const float interpolated_sample) { - this->resampler_callback(interpolated_sample); - } - ); - } - -private: - dsp::interpolation::LinearResampler resampler { }; - GardnerTimingErrorDetector timing_error_detector { }; - ErrorFilter error_filter { }; - const SymbolHandler symbol_handler; - - void resampler_callback(const float interpolated_sample) { - timing_error_detector(interpolated_sample, - [this](const float symbol, const float lateness) { - this->symbol_callback(symbol, lateness); - } - ); - } - - void symbol_callback(const float symbol, const float lateness) { - // NOTE: This check is to avoid std::function nullptr check, which - // brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code. - // TODO: Make symbol_handler known at compile time. - if( symbol_handler) { - symbol_handler(symbol); - } - - const float adjustment = error_filter(lateness); - resampler.advance(adjustment); - } + public: + using SymbolHandler = std::function; + + ClockRecovery( + const float sampling_rate, + const float symbol_rate, + ErrorFilter error_filter, + SymbolHandler symbol_handler) + : symbol_handler{std::move(symbol_handler)} { + configure(sampling_rate, symbol_rate, error_filter); + } + + ClockRecovery( + SymbolHandler symbol_handler) + : symbol_handler{std::move(symbol_handler)} { + } + + void configure( + const float sampling_rate, + const float symbol_rate, + ErrorFilter error_filter) { + resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol); + error_filter = error_filter; + } + + void operator()( + const float baseband_sample) { + resampler(baseband_sample, + [this](const float interpolated_sample) { + this->resampler_callback(interpolated_sample); + }); + } + + private: + dsp::interpolation::LinearResampler resampler{}; + GardnerTimingErrorDetector timing_error_detector{}; + ErrorFilter error_filter{}; + const SymbolHandler symbol_handler; + + void resampler_callback(const float interpolated_sample) { + timing_error_detector(interpolated_sample, + [this](const float symbol, const float lateness) { + this->symbol_callback(symbol, lateness); + }); + } + + void symbol_callback(const float symbol, const float lateness) { + // NOTE: This check is to avoid std::function nullptr check, which + // brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code. + // TODO: Make symbol_handler known at compile time. + if (symbol_handler) { + symbol_handler(symbol); + } + + const float adjustment = error_filter(lateness); + resampler.advance(adjustment); + } }; } /* namespace clock_recovery */ -#endif/*__CLOCK_RECOVERY_H__*/ +#endif /*__CLOCK_RECOVERY_H__*/ diff --git a/firmware/baseband/debug.cpp b/firmware/baseband/debug.cpp index 58548e7a7..999d61b22 100644 --- a/firmware/baseband/debug.cpp +++ b/firmware/baseband/debug.cpp @@ -28,29 +28,28 @@ #include "portapack_shared_memory.hpp" #include "performance_counter.hpp" -void write_m4_panic_msg(const char *panic_message, struct extctx *ctxp) { +void write_m4_panic_msg(const char* panic_message, struct extctx* ctxp) { if (ctxp == nullptr) { shared_memory.bb_data.data[0] = 0; - } - else { + } else { shared_memory.bb_data.data[0] = 1; - *((uint32_t *)&shared_memory.bb_data.data[4]) = SCB->CFSR; + *((uint32_t*)&shared_memory.bb_data.data[4]) = SCB->CFSR; memcpy(&shared_memory.bb_data.data[8], ctxp, sizeof(struct extctx)); } - for(size_t i=0; i>10) & 0x01) == 0x01) == last_paint_state) - return; + static bool last_paint_state = false; + if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state) + return; // Idle thread state is sometimes unuseable - if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000) + if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000) return; - last_paint_state = !last_paint_state; + last_paint_state = !last_paint_state; - auto utilisation = get_cpu_utilisation_in_percent(); - auto free_stack = (uint32_t)get_free_stack_space(); - auto free_heap = chCoreStatus(); + auto utilisation = get_cpu_utilisation_in_percent(); + auto free_stack = (uint32_t)get_free_stack_space(); + auto free_heap = chCoreStatus(); - shared_memory.m4_cpu_usage = utilisation; - shared_memory.m4_stack_usage = free_stack; - shared_memory.m4_heap_usage = free_heap; + shared_memory.m4_cpu_usage = utilisation; + shared_memory.m4_stack_usage = free_stack; + shared_memory.m4_heap_usage = free_heap; } } /* extern "C" */ diff --git a/firmware/baseband/debug.hpp b/firmware/baseband/debug.hpp index f49725270..e217539cd 100644 --- a/firmware/baseband/debug.hpp +++ b/firmware/baseband/debug.hpp @@ -26,27 +26,29 @@ extern uint32_t __process_stack_base__; extern uint32_t __process_stack_end__; -#define CRT0_STACKS_FILL_PATTERN 0x55555555 +#define CRT0_STACKS_FILL_PATTERN 0x55555555 -inline uint32_t get_free_stack_space(){ - uint32_t *p; - for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++); +inline uint32_t get_free_stack_space() { + uint32_t* p; + for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++) + ; auto stack_space_left = p - &__process_stack_base__; return stack_space_left; } /* Executes a breakpoint only when a debugger is attached. */ -#define HALT_IF_DEBUGGING() \ - do { \ - if ((*(volatile uint32_t *)0xE000EDF0) & (1 << 0)) { \ - __asm__ __volatile__("bkpt 1"); \ - } \ -} while (0) +#define HALT_IF_DEBUGGING() \ + do { \ + if ((*(volatile uint32_t*)0xE000EDF0) & (1 << 0)) { \ + __asm__ __volatile__("bkpt 1"); \ + } \ + } while (0) /* Stops execution until a debugger is attached. */ #define HALT_UNTIL_DEBUGGING() \ - while (!((*(volatile uint32_t *)0xE000EDF0) & (1 << 0))) {} \ - __asm__ __volatile__("bkpt 1") + while (!((*(volatile uint32_t*)0xE000EDF0) & (1 << 0))) { \ + } \ + __asm__ __volatile__("bkpt 1") -#endif/*__DEBUG_H__*/ +#endif /*__DEBUG_H__*/ diff --git a/firmware/baseband/dsp_decimate.cpp b/firmware/baseband/dsp_decimate.cpp index 12ed083f3..f65881ffc 100644 --- a/firmware/baseband/dsp_decimate.cpp +++ b/firmware/baseband/dsp_decimate.cpp @@ -27,763 +27,737 @@ namespace dsp { namespace decimate { static inline complex32_t mac_fs4_shift( - const vec2_s16* const z, - const vec2_s16* const t, - const size_t index, - const complex32_t accum -) { - /* Accumulate sample * tap results for samples already in z buffer. - * Multiply using swap/negation to achieve Fs/4 shift. - * For iterations where samples are shifting out of z buffer (being discarded). - * Expect negated tap t[2] to accomodate instruction set limitations. - */ - const bool negated_t2 = index & 1; - const auto q1_i0 = z[index*2 + 0]; - const auto i1_q0 = z[index*2 + 1]; - const auto t1_t0 = t[index]; - const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); - const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); - return { real, imag }; + const vec2_s16* const z, + const vec2_s16* const t, + const size_t index, + const complex32_t accum) { + /* Accumulate sample * tap results for samples already in z buffer. + * Multiply using swap/negation to achieve Fs/4 shift. + * For iterations where samples are shifting out of z buffer (being discarded). + * Expect negated tap t[2] to accomodate instruction set limitations. + */ + const bool negated_t2 = index & 1; + const auto q1_i0 = z[index * 2 + 0]; + const auto i1_q0 = z[index * 2 + 1]; + const auto t1_t0 = t[index]; + const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); + const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline complex32_t mac_shift( - const vec2_s16* const z, - const vec2_s16* const t, - const size_t index, - const complex32_t accum -) { - /* Accumulate sample * tap results for samples already in z buffer. - * For iterations where samples are shifting out of z buffer (being discarded). - * real += i1 * t1 + i0 * t0 - * imag += q1 * t1 + q0 * t0 - */ - const auto i1_i0 = z[index*2 + 0]; - const auto q1_q0 = z[index*2 + 1]; - const auto t1_t0 = t[index]; - const auto real = smlad(i1_i0, t1_t0, accum.real()); - const auto imag = smlad(q1_q0, t1_t0, accum.imag()); - return { real, imag }; + const vec2_s16* const z, + const vec2_s16* const t, + const size_t index, + const complex32_t accum) { + /* Accumulate sample * tap results for samples already in z buffer. + * For iterations where samples are shifting out of z buffer (being discarded). + * real += i1 * t1 + i0 * t0 + * imag += q1 * t1 + q0 * t0 + */ + const auto i1_i0 = z[index * 2 + 0]; + const auto q1_q0 = z[index * 2 + 1]; + const auto t1_t0 = t[index]; + const auto real = smlad(i1_i0, t1_t0, accum.real()); + const auto imag = smlad(q1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline complex32_t mac_fs4_shift_and_store( - vec2_s16* const z, - const vec2_s16* const t, - const size_t decimation_factor, - const size_t index, - const complex32_t accum -) { - /* Accumulate sample * tap results for samples already in z buffer. - * Place new samples into z buffer. - * Expect negated tap t[2] to accomodate instruction set limitations. - */ - const bool negated_t2 = index & 1; - const auto q1_i0 = z[decimation_factor + index*2 + 0]; - const auto i1_q0 = z[decimation_factor + index*2 + 1]; - const auto t1_t0 = t[decimation_factor / 2 + index]; - z[index*2 + 0] = q1_i0; - const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); - z[index*2 + 1] = i1_q0; - const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); - return { real, imag }; + vec2_s16* const z, + const vec2_s16* const t, + const size_t decimation_factor, + const size_t index, + const complex32_t accum) { + /* Accumulate sample * tap results for samples already in z buffer. + * Place new samples into z buffer. + * Expect negated tap t[2] to accomodate instruction set limitations. + */ + const bool negated_t2 = index & 1; + const auto q1_i0 = z[decimation_factor + index * 2 + 0]; + const auto i1_q0 = z[decimation_factor + index * 2 + 1]; + const auto t1_t0 = t[decimation_factor / 2 + index]; + z[index * 2 + 0] = q1_i0; + const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); + z[index * 2 + 1] = i1_q0; + const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline complex32_t mac_shift_and_store( - vec2_s16* const z, - const vec2_s16* const t, - const size_t decimation_factor, - const size_t index, - const complex32_t accum -) { - /* Accumulate sample * tap results for samples already in z buffer. - * Place new samples into z buffer. - * Expect negated tap t[2] to accomodate instruction set limitations. - */ - const auto i1_i0 = z[decimation_factor + index*2 + 0]; - const auto q1_q0 = z[decimation_factor + index*2 + 1]; - const auto t1_t0 = t[decimation_factor / 2 + index]; - z[index*2 + 0] = i1_i0; - const auto real = smlad(i1_i0, t1_t0, accum.real()); - z[index*2 + 1] = q1_q0; - const auto imag = smlad(q1_q0, t1_t0, accum.imag()); - return { real, imag }; + vec2_s16* const z, + const vec2_s16* const t, + const size_t decimation_factor, + const size_t index, + const complex32_t accum) { + /* Accumulate sample * tap results for samples already in z buffer. + * Place new samples into z buffer. + * Expect negated tap t[2] to accomodate instruction set limitations. + */ + const auto i1_i0 = z[decimation_factor + index * 2 + 0]; + const auto q1_q0 = z[decimation_factor + index * 2 + 1]; + const auto t1_t0 = t[decimation_factor / 2 + index]; + z[index * 2 + 0] = i1_i0; + const auto real = smlad(i1_i0, t1_t0, accum.real()); + z[index * 2 + 1] = q1_q0; + const auto imag = smlad(q1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline complex32_t mac_fs4_shift_and_store_new_c8_samples( - vec2_s16* const z, - const vec2_s16* const t, - const vec4_s8* const in, - const size_t decimation_factor, - const size_t index, - const size_t length, - const complex32_t accum -) { - /* Accumulate sample * tap results for new samples. - * Place new samples into z buffer. - * Expect negated tap t[2] to accomodate instruction set limitations. - */ - const bool negated_t2 = index & 1; - const auto q1_i1_q0_i0 = in[index]; - const auto t1_t0 = t[(length - decimation_factor) / 2 + index]; - const auto i1_q1_i0_q0 = rev16(q1_i1_q0_i0); - const auto i1_q1_q0_i0 = pkhbt(q1_i1_q0_i0, i1_q1_i0_q0); - const auto q1_i0 = sxtb16(i1_q1_q0_i0); - const auto i1_q0 = sxtb16(i1_q1_q0_i0, 8); - z[length - decimation_factor * 2 + index*2 + 0] = q1_i0; - const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); - z[length - decimation_factor * 2 + index*2 + 1] = i1_q0; - const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); - return { real, imag }; + vec2_s16* const z, + const vec2_s16* const t, + const vec4_s8* const in, + const size_t decimation_factor, + const size_t index, + const size_t length, + const complex32_t accum) { + /* Accumulate sample * tap results for new samples. + * Place new samples into z buffer. + * Expect negated tap t[2] to accomodate instruction set limitations. + */ + const bool negated_t2 = index & 1; + const auto q1_i1_q0_i0 = in[index]; + const auto t1_t0 = t[(length - decimation_factor) / 2 + index]; + const auto i1_q1_i0_q0 = rev16(q1_i1_q0_i0); + const auto i1_q1_q0_i0 = pkhbt(q1_i1_q0_i0, i1_q1_i0_q0); + const auto q1_i0 = sxtb16(i1_q1_q0_i0); + const auto i1_q0 = sxtb16(i1_q1_q0_i0, 8); + z[length - decimation_factor * 2 + index * 2 + 0] = q1_i0; + const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real()); + z[length - decimation_factor * 2 + index * 2 + 1] = i1_q0; + const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline complex32_t mac_shift_and_store_new_c16_samples( - vec2_s16* const z, - const vec2_s16* const t, - const vec2_s16* const in, - const size_t decimation_factor, - const size_t index, - const size_t length, - const complex32_t accum -) { - /* Accumulate sample * tap results for new samples. - * Place new samples into z buffer. - * Expect negated tap t[2] to accomodate instruction set limitations. - */ - const auto q0_i0 = in[index*2+0]; - const auto q1_i1 = in[index*2+1]; - const auto i1_i0 = pkhbt(q0_i0, q1_i1, 16); - const auto q1_q0 = pkhtb(q1_i1, q0_i0, 16); - const auto t1_t0 = t[(length - decimation_factor) / 2 + index]; - z[length - decimation_factor * 2 + index*2 + 0] = i1_i0; - const auto real = smlad(i1_i0, t1_t0, accum.real()); - z[length - decimation_factor * 2 + index*2 + 1] = q1_q0; - const auto imag = smlad(q1_q0, t1_t0, accum.imag()); - return { real, imag }; + vec2_s16* const z, + const vec2_s16* const t, + const vec2_s16* const in, + const size_t decimation_factor, + const size_t index, + const size_t length, + const complex32_t accum) { + /* Accumulate sample * tap results for new samples. + * Place new samples into z buffer. + * Expect negated tap t[2] to accomodate instruction set limitations. + */ + const auto q0_i0 = in[index * 2 + 0]; + const auto q1_i1 = in[index * 2 + 1]; + const auto i1_i0 = pkhbt(q0_i0, q1_i1, 16); + const auto q1_q0 = pkhtb(q1_i1, q0_i0, 16); + const auto t1_t0 = t[(length - decimation_factor) / 2 + index]; + z[length - decimation_factor * 2 + index * 2 + 0] = i1_i0; + const auto real = smlad(i1_i0, t1_t0, accum.real()); + z[length - decimation_factor * 2 + index * 2 + 1] = q1_q0; + const auto imag = smlad(q1_q0, t1_t0, accum.imag()); + return {real, imag}; } static inline uint32_t scale_round_and_pack( - const complex32_t value, - const int32_t scale_factor -) { - /* Multiply 32-bit components of the complex by a scale factor, - * into int64_ts, then round to nearest LSB (1 << 32), saturate to 16 bits, - * and pack into a complex. - */ - const auto scaled_real = __SMMULR(value.real(), scale_factor); - const auto saturated_real = __SSAT(scaled_real, 16); - - const auto scaled_imag = __SMMULR(value.imag(), scale_factor); - const auto saturated_imag = __SSAT(scaled_imag, 16); - - return __PKHBT(saturated_real, saturated_imag, 16); + const complex32_t value, + const int32_t scale_factor) { + /* Multiply 32-bit components of the complex by a scale factor, + * into int64_ts, then round to nearest LSB (1 << 32), saturate to 16 bits, + * and pack into a complex. + */ + const auto scaled_real = __SMMULR(value.real(), scale_factor); + const auto saturated_real = __SSAT(scaled_real, 16); + + const auto scaled_imag = __SMMULR(value.imag(), scale_factor); + const auto saturated_imag = __SSAT(scaled_imag, 16); + + return __PKHBT(saturated_real, saturated_imag, 16); } -template +template static void taps_copy( - const Tap* const source, - Tap* const target, - const size_t count, - const bool shift_up -) { - const uint32_t negate_pattern = shift_up ? 0b1110 : 0b0100; - for(size_t i=0; i> (i & 3)) & 1; - target[i] = negate ? -source[i] : source[i]; - } + const Tap* const source, + Tap* const target, + const size_t count, + const bool shift_up) { + const uint32_t negate_pattern = shift_up ? 0b1110 : 0b0100; + for (size_t i = 0; i < count; i++) { + const bool negate = (negate_pattern >> (i & 3)) & 1; + target[i] = negate ? -source[i] : source[i]; + } } // FIRC8xR16x24FS4Decim4 ////////////////////////////////////////////////// void FIRC8xR16x24FS4Decim4::configure( - const std::array& taps, - const int32_t scale, - const Shift shift -) { - taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up); - output_scale = scale; - z_.fill({}); + const std::array& taps, + const int32_t scale, + const Shift shift) { + taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up); + output_scale = scale; + z_.fill({}); } buffer_c16_t FIRC8xR16x24FS4Decim4::execute( - const buffer_c8_t& src, - const buffer_c16_t& dst -) { - vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); - const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); - uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); - - const auto k = output_scale; - - const size_t count = src.count / decimation_factor; - for(size_t i=0; i(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); - - complex32_t accum; - - // Oldest samples are discarded. - accum = mac_fs4_shift(z, t, 0, accum); - accum = mac_fs4_shift(z, t, 1, accum); - - // Middle samples are shifted earlier in the "z" delay buffer. - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 4, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 5, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 6, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 7, accum); - - // Newest samples come from "in" buffer, are copied to "z" delay buffer. - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum); - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum); - - d[i] = scale_round_and_pack(accum, k); - } - - return { - dst.p, - count, - src.sampling_rate / decimation_factor - }; + const buffer_c8_t& src, + const buffer_c16_t& dst) { + vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); + const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); + uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); + + const auto k = output_scale; + + const size_t count = src.count / decimation_factor; + for (size_t i = 0; i < count; i++) { + const vec4_s8* const in = static_cast(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); + + complex32_t accum; + + // Oldest samples are discarded. + accum = mac_fs4_shift(z, t, 0, accum); + accum = mac_fs4_shift(z, t, 1, accum); + + // Middle samples are shifted earlier in the "z" delay buffer. + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 4, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 5, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 6, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 7, accum); + + // Newest samples come from "in" buffer, are copied to "z" delay buffer. + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum); + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum); + + d[i] = scale_round_and_pack(accum, k); + } + + return { + dst.p, + count, + src.sampling_rate / decimation_factor}; } // FIRC8xR16x24FS4Decim8 ////////////////////////////////////////////////// void FIRC8xR16x24FS4Decim8::configure( - const std::array& taps, - const int32_t scale, - const Shift shift -) { - taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up); - output_scale = scale; - z_.fill({}); + const std::array& taps, + const int32_t scale, + const Shift shift) { + taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up); + output_scale = scale; + z_.fill({}); } buffer_c16_t FIRC8xR16x24FS4Decim8::execute( - const buffer_c8_t& src, - const buffer_c16_t& dst -) { - vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); - const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); - uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); - - const auto k = output_scale; - - const size_t count = src.count / decimation_factor; - for(size_t i=0; i(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); - - complex32_t accum; - - // Oldest samples are discarded. - accum = mac_fs4_shift(z, t, 0, accum); - accum = mac_fs4_shift(z, t, 1, accum); - accum = mac_fs4_shift(z, t, 2, accum); - accum = mac_fs4_shift(z, t, 3, accum); - - // Middle samples are shifted earlier in the "z" delay buffer. - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum); - accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum); - - // Newest samples come from "in" buffer, are copied to "z" delay buffer. - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum); - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum); - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 2, taps_count, accum); - accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 3, taps_count, accum); - - d[i] = scale_round_and_pack(accum, k); - } - - return { - dst.p, - count, - src.sampling_rate / decimation_factor - }; + const buffer_c8_t& src, + const buffer_c16_t& dst) { + vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); + const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); + uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); + + const auto k = output_scale; + + const size_t count = src.count / decimation_factor; + for (size_t i = 0; i < count; i++) { + const vec4_s8* const in = static_cast(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); + + complex32_t accum; + + // Oldest samples are discarded. + accum = mac_fs4_shift(z, t, 0, accum); + accum = mac_fs4_shift(z, t, 1, accum); + accum = mac_fs4_shift(z, t, 2, accum); + accum = mac_fs4_shift(z, t, 3, accum); + + // Middle samples are shifted earlier in the "z" delay buffer. + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum); + accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum); + + // Newest samples come from "in" buffer, are copied to "z" delay buffer. + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum); + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum); + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 2, taps_count, accum); + accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 3, taps_count, accum); + + d[i] = scale_round_and_pack(accum, k); + } + + return { + dst.p, + count, + src.sampling_rate / decimation_factor}; } // FIRC16xR16x16Decim2 //////////////////////////////////////////////////// void FIRC16xR16x16Decim2::configure( - const std::array& taps, - const int32_t scale -) { - std::copy(taps.cbegin(), taps.cend(), taps_.begin()); - output_scale = scale; - z_.fill({}); + const std::array& taps, + const int32_t scale) { + std::copy(taps.cbegin(), taps.cend(), taps_.begin()); + output_scale = scale; + z_.fill({}); } buffer_c16_t FIRC16xR16x16Decim2::execute( - const buffer_c16_t& src, - const buffer_c16_t& dst -) { - vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); - const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); - uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); - - const auto k = output_scale; - - const size_t count = src.count / decimation_factor; - for(size_t i=0; i(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); - - complex32_t accum; - - // Oldest samples are discarded. - accum = mac_shift(z, t, 0, accum); - - // Middle samples are shifted earlier in the "z" delay buffer. - accum = mac_shift_and_store(z, t, decimation_factor, 0, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 1, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 2, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 3, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 4, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 5, accum); - - // Newest samples come from "in" buffer, are copied to "z" delay buffer. - accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum); - - d[i] = scale_round_and_pack(accum, k); - } - - return { - dst.p, - count, - src.sampling_rate / decimation_factor - }; + const buffer_c16_t& src, + const buffer_c16_t& dst) { + vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); + const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); + uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); + + const auto k = output_scale; + + const size_t count = src.count / decimation_factor; + for (size_t i = 0; i < count; i++) { + const vec2_s16* const in = static_cast(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); + + complex32_t accum; + + // Oldest samples are discarded. + accum = mac_shift(z, t, 0, accum); + + // Middle samples are shifted earlier in the "z" delay buffer. + accum = mac_shift_and_store(z, t, decimation_factor, 0, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 1, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 2, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 3, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 4, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 5, accum); + + // Newest samples come from "in" buffer, are copied to "z" delay buffer. + accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum); + + d[i] = scale_round_and_pack(accum, k); + } + + return { + dst.p, + count, + src.sampling_rate / decimation_factor}; } // FIRC16xR16x32Decim8 //////////////////////////////////////////////////// void FIRC16xR16x32Decim8::configure( - const std::array& taps, - const int32_t scale -) { - std::copy(taps.cbegin(), taps.cend(), taps_.begin()); - output_scale = scale; - z_.fill({}); + const std::array& taps, + const int32_t scale) { + std::copy(taps.cbegin(), taps.cend(), taps_.begin()); + output_scale = scale; + z_.fill({}); } buffer_c16_t FIRC16xR16x32Decim8::execute( - const buffer_c16_t& src, - const buffer_c16_t& dst -) { - vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); - const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); - uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); - - const auto k = output_scale; - - const size_t count = src.count / decimation_factor; - for(size_t i=0; i(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); - - complex32_t accum; - - // Oldest samples are discarded. - accum = mac_shift(z, t, 0, accum); - accum = mac_shift(z, t, 1, accum); - accum = mac_shift(z, t, 2, accum); - accum = mac_shift(z, t, 3, accum); - - // Middle samples are shifted earlier in the "z" delay buffer. - accum = mac_shift_and_store(z, t, decimation_factor, 0, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 1, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 2, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 3, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 4, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 5, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 6, accum); - accum = mac_shift_and_store(z, t, decimation_factor, 7, accum); - - // Newest samples come from "in" buffer, are copied to "z" delay buffer. - accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum); - accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 1, taps_count, accum); - accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 2, taps_count, accum); - accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 3, taps_count, accum); - - d[i] = scale_round_and_pack(accum, k); - } - - return { - dst.p, - count, - src.sampling_rate / decimation_factor - }; + const buffer_c16_t& src, + const buffer_c16_t& dst) { + vec2_s16* const z = static_cast(__builtin_assume_aligned(z_.data(), 4)); + const vec2_s16* const t = static_cast(__builtin_assume_aligned(taps_.data(), 4)); + uint32_t* const d = static_cast(__builtin_assume_aligned(dst.p, 4)); + + const auto k = output_scale; + + const size_t count = src.count / decimation_factor; + for (size_t i = 0; i < count; i++) { + const vec2_s16* const in = static_cast(__builtin_assume_aligned(&src.p[i * decimation_factor], 4)); + + complex32_t accum; + + // Oldest samples are discarded. + accum = mac_shift(z, t, 0, accum); + accum = mac_shift(z, t, 1, accum); + accum = mac_shift(z, t, 2, accum); + accum = mac_shift(z, t, 3, accum); + + // Middle samples are shifted earlier in the "z" delay buffer. + accum = mac_shift_and_store(z, t, decimation_factor, 0, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 1, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 2, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 3, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 4, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 5, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 6, accum); + accum = mac_shift_and_store(z, t, decimation_factor, 7, accum); + + // Newest samples come from "in" buffer, are copied to "z" delay buffer. + accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum); + accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 1, taps_count, accum); + accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 2, taps_count, accum); + accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 3, taps_count, accum); + + d[i] = scale_round_and_pack(accum, k); + } + + return { + dst.p, + count, + src.sampling_rate / decimation_factor}; } buffer_c16_t Complex8DecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) { - /* Decimates by two using a non-recursive third-order CIC filter. - */ - - /* CIC filter (decimating by two): - * D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1 - * D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1 - * - * D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1 - * D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1 - */ - - uint32_t i1_i0 = _i1_i0; - uint32_t q1_q0 = _q1_q0; - - /* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */ - constexpr uint32_t scale_factor = 32; - constexpr uint32_t k_3_1 = 0x00030001 * scale_factor; - uint32_t* src_p = reinterpret_cast(&src.p[0]); - uint32_t* const src_end = reinterpret_cast(&src.p[src.count]); - uint32_t* dst_p = reinterpret_cast(&dst.p[0]); - while(src_p < src_end) { - const uint32_t q3_i3_q2_i2 = *(src_p++); // 3 - const uint32_t q5_i5_q4_i4 = *(src_p++); - - const uint32_t d_i0_partial = __SMUAD(k_3_1, i1_i0); // 1: = 3 * i1 + 1 * i0 - const uint32_t i3_i2 = __SXTB16(q3_i3_q2_i2, 0); // 1: (q3_i3_q2_i2 ror 0)[23:16]:(q3_i3_q2_i2 ror 0)[7:0] - const uint32_t d_i0 = __SMLADX(k_3_1, i3_i2, d_i0_partial); // 1: + 3 * i2 + 1 * i3 - - const uint32_t d_q0_partial = __SMUAD(k_3_1, q1_q0); // 1: = 3 * q1 * 1 * q0 - const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0] - const uint32_t d_q0 = __SMLADX(k_3_1, q3_q2, d_q0_partial); // 1: + 3 * q2 + 1 * q3 - - const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] - - const uint32_t d_i1_partial = __SMUAD(k_3_1, i3_i2); // 1: = 3 * i3 + 1 * i2 - const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0] - const uint32_t d_i1 = __SMLADX(k_3_1, i5_i4, d_i1_partial); // 1: + 1 * i5 + 3 * i4 - - const uint32_t d_q1_partial = __SMUAD(k_3_1, q3_q2); // 1: = 3 * q3 * 1 * q2 - const uint32_t q5_q4 = __SXTB16(q5_i5_q4_i4, 8); // 1: (q5_i5_q4_i4 ror 8)[23:16]:(q5_i5_q4_i4 ror 8)[7:0] - const uint32_t d_q1 = __SMLADX(k_3_1, q5_q4, d_q1_partial); // 1: + 1 * q5 + 3 * q4 - - const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] - - *(dst_p++) = d_q0_i0; // 3 - *(dst_p++) = d_q1_i1; - - i1_i0 = i5_i4; - q1_q0 = q5_q4; - } - _i1_i0 = i1_i0; - _q1_q0 = q1_q0; - - return { dst.p, src.count / 2, src.sampling_rate / 2 }; + /* Decimates by two using a non-recursive third-order CIC filter. + */ + + /* CIC filter (decimating by two): + * D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1 + * D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1 + * + * D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1 + * D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1 + */ + + uint32_t i1_i0 = _i1_i0; + uint32_t q1_q0 = _q1_q0; + + /* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */ + constexpr uint32_t scale_factor = 32; + constexpr uint32_t k_3_1 = 0x00030001 * scale_factor; + uint32_t* src_p = reinterpret_cast(&src.p[0]); + uint32_t* const src_end = reinterpret_cast(&src.p[src.count]); + uint32_t* dst_p = reinterpret_cast(&dst.p[0]); + while (src_p < src_end) { + const uint32_t q3_i3_q2_i2 = *(src_p++); // 3 + const uint32_t q5_i5_q4_i4 = *(src_p++); + + const uint32_t d_i0_partial = __SMUAD(k_3_1, i1_i0); // 1: = 3 * i1 + 1 * i0 + const uint32_t i3_i2 = __SXTB16(q3_i3_q2_i2, 0); // 1: (q3_i3_q2_i2 ror 0)[23:16]:(q3_i3_q2_i2 ror 0)[7:0] + const uint32_t d_i0 = __SMLADX(k_3_1, i3_i2, d_i0_partial); // 1: + 3 * i2 + 1 * i3 + + const uint32_t d_q0_partial = __SMUAD(k_3_1, q1_q0); // 1: = 3 * q1 * 1 * q0 + const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0] + const uint32_t d_q0 = __SMLADX(k_3_1, q3_q2, d_q0_partial); // 1: + 3 * q2 + 1 * q3 + + const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] + + const uint32_t d_i1_partial = __SMUAD(k_3_1, i3_i2); // 1: = 3 * i3 + 1 * i2 + const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0] + const uint32_t d_i1 = __SMLADX(k_3_1, i5_i4, d_i1_partial); // 1: + 1 * i5 + 3 * i4 + + const uint32_t d_q1_partial = __SMUAD(k_3_1, q3_q2); // 1: = 3 * q3 * 1 * q2 + const uint32_t q5_q4 = __SXTB16(q5_i5_q4_i4, 8); // 1: (q5_i5_q4_i4 ror 8)[23:16]:(q5_i5_q4_i4 ror 8)[7:0] + const uint32_t d_q1 = __SMLADX(k_3_1, q5_q4, d_q1_partial); // 1: + 1 * q5 + 3 * q4 + + const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] + + *(dst_p++) = d_q0_i0; // 3 + *(dst_p++) = d_q1_i1; + + i1_i0 = i5_i4; + q1_q0 = q5_q4; + } + _i1_i0 = i1_i0; + _q1_q0 = q1_q0; + + return {dst.p, src.count / 2, src.sampling_rate / 2}; } buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) { - /* Translates incoming complex samples by -fs/4, - * decimates by two using a non-recursive third-order CIC filter. - */ - - /* Derivation of algorithm: - * Original CIC filter (decimating by two): - * D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1 - * D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1 - * - * D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1 - * D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1 - * - * Translate -fs/4, phased 180 degrees, accomplished by complex multiplication - * of complex length-4 sequence: - * - * Substitute: - * i0 = -i0, q0 = -q0 - * i1 = -q1, q1 = i1 - * i2 = i2, q2 = q2 - * i3 = q3, q3 = -i3 - * i4 = -i4, q4 = -q4 - * i5 = -q5, q5 = i5 - * - * Resulting taps (with decimation by 2, four samples in, two samples out): - * D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1 - * D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1 - * - * D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1 - * D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1 - */ - - // 6 cycles per complex input sample, not including loop overhead. - uint32_t q1_i0 = _q1_i0; - uint32_t q0_i1 = _q0_i1; - /* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */ - constexpr uint32_t scale_factor = 32; - const uint32_t k_3_1 = 0x00030001 * scale_factor; - uint32_t* src_p = reinterpret_cast(&src.p[0]); - uint32_t* const src_end = reinterpret_cast(&src.p[src.count]); - uint32_t* dst_p = reinterpret_cast(&dst.p[0]); - while(src_p < src_end) { - const uint32_t q3_i3_q2_i2 = *(src_p++); // 3 - const uint32_t q5_i5_q4_i4 = *(src_p++); - - const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0] - const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0] - const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0] - const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0] - - // D_I0 = 3 * (i2 - q1) + (q3 - i0) - const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0] - const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16] - - // D_Q0 = 3 * (q2 + i1) - (i3 + q0) - const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0] - const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]–Rm[31:16]*RsX[15:0] - const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] - - const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0] - const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0] - const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0] - const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] - - // D_I1 = (i2 - q5) + 3 * (q3 - i4) - const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0] - const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0] - - // D_Q1 = (i5 + q2) - 3 * (q4 + i3) - const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0] - const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]–Rm[31:16]*Rs[31:16] - const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] - *(dst_p++) = d_q0_i0; // 3 - *(dst_p++) = d_q1_i1; - - q1_i0 = q5_i4; - q0_i1 = q4_i5; - } - _q1_i0 = q1_i0; - _q0_i1 = q0_i1; - - return { dst.p, src.count / 2, src.sampling_rate / 2 }; + /* Translates incoming complex samples by -fs/4, + * decimates by two using a non-recursive third-order CIC filter. + */ + + /* Derivation of algorithm: + * Original CIC filter (decimating by two): + * D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1 + * D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1 + * + * D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1 + * D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1 + * + * Translate -fs/4, phased 180 degrees, accomplished by complex multiplication + * of complex length-4 sequence: + * + * Substitute: + * i0 = -i0, q0 = -q0 + * i1 = -q1, q1 = i1 + * i2 = i2, q2 = q2 + * i3 = q3, q3 = -i3 + * i4 = -i4, q4 = -q4 + * i5 = -q5, q5 = i5 + * + * Resulting taps (with decimation by 2, four samples in, two samples out): + * D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1 + * D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1 + * + * D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1 + * D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1 + */ + + // 6 cycles per complex input sample, not including loop overhead. + uint32_t q1_i0 = _q1_i0; + uint32_t q0_i1 = _q0_i1; + /* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */ + constexpr uint32_t scale_factor = 32; + const uint32_t k_3_1 = 0x00030001 * scale_factor; + uint32_t* src_p = reinterpret_cast(&src.p[0]); + uint32_t* const src_end = reinterpret_cast(&src.p[src.count]); + uint32_t* dst_p = reinterpret_cast(&dst.p[0]); + while (src_p < src_end) { + const uint32_t q3_i3_q2_i2 = *(src_p++); // 3 + const uint32_t q5_i5_q4_i4 = *(src_p++); + + const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0] + const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0] + const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0] + const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0] + + // D_I0 = 3 * (i2 - q1) + (q3 - i0) + const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0] + const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16] + + // D_Q0 = 3 * (q2 + i1) - (i3 + q0) + const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0] + const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]–Rm[31:16]*RsX[15:0] + const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] + + const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0] + const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0] + const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0] + const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] + + // D_I1 = (i2 - q5) + 3 * (q3 - i4) + const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0] + const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0] + + // D_Q1 = (i5 + q2) - 3 * (q4 + i3) + const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0] + const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]–Rm[31:16]*Rs[31:16] + const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0] + *(dst_p++) = d_q0_i0; // 3 + *(dst_p++) = d_q1_i1; + + q1_i0 = q5_i4; + q0_i1 = q4_i5; + } + _q1_i0 = q1_i0; + _q0_i1 = q0_i1; + + return {dst.p, src.count / 2, src.sampling_rate / 2}; } buffer_c16_t DecimateBy2CIC3::execute( - const buffer_c16_t& src, - const buffer_c16_t& dst -) { - /* Complex non-recursive 3rd-order CIC filter (taps 1,3,3,1). - * Gain of 8. - * Consumes 16 bytes (4 s16:s16 samples) per loop iteration, - * Produces 8 bytes (2 s16:s16 samples) per loop iteration. - */ - uint32_t t1 = _iq0; - uint32_t t2 = _iq1; - const uint32_t taps = 0x00000003; - void* s = src.p; - void* d = dst.p; - const auto d_end = &dst.p[src.count / 2]; - while(d < d_end) { - uint32_t i = __SXTH(t1, 0); /* 1: I0 */ - uint32_t q = __SXTH(t1, 16); /* 1: Q0 */ - i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */ - q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */ - - const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */ - const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */ - - i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */ - q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */ - int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */ - int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */ - i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */ - *__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */ - - i = __SXTH(t3, 0); /* 1: I2 */ - q = __SXTH(t3, 16); /* 1: Q2 */ - i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */ - q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */ - - t1 = *__SIMD32(s)++; /* 3: Q4:I4 */ - t2 = *__SIMD32(s)++; /* Q5:I5 */ - - i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */ - q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */ - int32_t si1 = __SXTAH(i, t2, 0) ; /* 1: I5 + Q4*3 + Q3*3 + Q2 */ - int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */ - i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */ - *__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */ - } - _iq0 = t1; - _iq1 = t2; - - return { dst.p, src.count / 2, src.sampling_rate / 2 }; + const buffer_c16_t& src, + const buffer_c16_t& dst) { + /* Complex non-recursive 3rd-order CIC filter (taps 1,3,3,1). + * Gain of 8. + * Consumes 16 bytes (4 s16:s16 samples) per loop iteration, + * Produces 8 bytes (2 s16:s16 samples) per loop iteration. + */ + uint32_t t1 = _iq0; + uint32_t t2 = _iq1; + const uint32_t taps = 0x00000003; + void* s = src.p; + void* d = dst.p; + const auto d_end = &dst.p[src.count / 2]; + while (d < d_end) { + uint32_t i = __SXTH(t1, 0); /* 1: I0 */ + uint32_t q = __SXTH(t1, 16); /* 1: Q0 */ + i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */ + q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */ + + const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */ + const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */ + + i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */ + q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */ + int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */ + int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */ + i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */ + *__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */ + + i = __SXTH(t3, 0); /* 1: I2 */ + q = __SXTH(t3, 16); /* 1: Q2 */ + i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */ + q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */ + + t1 = *__SIMD32(s)++; /* 3: Q4:I4 */ + t2 = *__SIMD32(s)++; /* Q5:I5 */ + + i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */ + q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */ + int32_t si1 = __SXTAH(i, t2, 0); /* 1: I5 + Q4*3 + Q3*3 + Q2 */ + int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */ + i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */ + *__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */ + } + _iq0 = t1; + _iq1 = t2; + + return {dst.p, src.count / 2, src.sampling_rate / 2}; } void FIR64AndDecimateBy2Real::configure( - const std::array& new_taps -) { - std::copy(new_taps.cbegin(), new_taps.cend(), taps.begin()); + const std::array& new_taps) { + std::copy(new_taps.cbegin(), new_taps.cend(), taps.begin()); } buffer_s16_t FIR64AndDecimateBy2Real::execute( - const buffer_s16_t& src, - const buffer_s16_t& dst -) { - /* int16_t input (sample count "n" must be multiple of 4) - * -> int16_t output, decimated by 2. - * taps are normalized to 1 << 16 == 1.0. - */ - auto src_p = src.p; - auto dst_p = dst.p; - int32_t n = src.count; - for(; n>0; n-=2) { - z[taps_count-2] = *(src_p++); - z[taps_count-1] = *(src_p++); - - int32_t t = 0; - for(size_t j=0; j int16_t output, decimated by 2. + * taps are normalized to 1 << 16 == 1.0. + */ + auto src_p = src.p; + auto dst_p = dst.p; + int32_t n = src.count; + for (; n > 0; n -= 2) { + z[taps_count - 2] = *(src_p++); + z[taps_count - 1] = *(src_p++); + + int32_t t = 0; + for (size_t j = 0; j < taps_count; j += 4) { + t += z[j + 0] * taps[j + 0]; + t += z[j + 1] * taps[j + 1]; + t += z[j + 2] * taps[j + 2]; + t += z[j + 3] * taps[j + 3]; + + z[j + 0] = z[j + 0 + 2]; + z[j + 1] = z[j + 1 + 2]; + z[j + 2] = z[j + 2 + 2]; + z[j + 3] = z[j + 3 + 2]; + } + *(dst_p++) = t / 65536; + } + + return {dst.p, src.count / 2, src.sampling_rate / 2}; } void FIRAndDecimateComplex::configure_common( - const size_t taps_count, const size_t decimation_factor -) { - samples_ = std::make_unique(taps_count); - taps_reversed_ = std::make_unique(taps_count); - taps_count_ = taps_count; - decimation_factor_ = decimation_factor; + const size_t taps_count, + const size_t decimation_factor) { + samples_ = std::make_unique(taps_count); + taps_reversed_ = std::make_unique(taps_count); + taps_count_ = taps_count; + decimation_factor_ = decimation_factor; } buffer_c16_t FIRAndDecimateComplex::execute( - const buffer_c16_t& src, - const buffer_c16_t& dst -) { - /* int16_t input (sample count "n" must be multiple of decimation_factor) - * -> int16_t output, decimated by decimation_factor. - * taps are normalized to 1 << 16 == 1.0. - */ - const auto output_sampling_rate = src.sampling_rate / decimation_factor_; - const size_t output_samples = src.count / decimation_factor_; - - void* dst_p = dst.p; - const buffer_c16_t result { dst.p, output_samples, output_sampling_rate }; - - const void* src_p = src.p; - size_t outer_count = output_samples; - while(outer_count > 0) { - /* Put new samples into delay buffer */ - void* z_new_p = &samples_[taps_count_ - decimation_factor_]; - for(size_t i=0; i 0) { - const auto tap0 = *__SIMD32(t_p)++; - const auto sample0 = *__SIMD32(z_p)++; - const auto tap1 = *__SIMD32(t_p)++; - const auto sample1 = *__SIMD32(z_p)++; - t_real = __SMLSLD(sample0, tap0, t_real); - t_imag = __SMLALDX(sample0, tap0, t_imag); - t_real = __SMLSLD(sample1, tap1, t_real); - t_imag = __SMLALDX(sample1, tap1, t_imag); - - const auto tap2 = *__SIMD32(t_p)++; - const auto sample2 = *__SIMD32(z_p)++; - const auto tap3 = *__SIMD32(t_p)++; - const auto sample3 = *__SIMD32(z_p)++; - t_real = __SMLSLD(sample2, tap2, t_real); - t_imag = __SMLALDX(sample2, tap2, t_imag); - t_real = __SMLSLD(sample3, tap3, t_real); - t_imag = __SMLALDX(sample3, tap3, t_imag); - - const auto tap4 = *__SIMD32(t_p)++; - const auto sample4 = *__SIMD32(z_p)++; - const auto tap5 = *__SIMD32(t_p)++; - const auto sample5 = *__SIMD32(z_p)++; - t_real = __SMLSLD(sample4, tap4, t_real); - t_imag = __SMLALDX(sample4, tap4, t_imag); - t_real = __SMLSLD(sample5, tap5, t_real); - t_imag = __SMLALDX(sample5, tap5, t_imag); - - const auto tap6 = *__SIMD32(t_p)++; - const auto sample6 = *__SIMD32(z_p)++; - const auto tap7 = *__SIMD32(t_p)++; - const auto sample7 = *__SIMD32(z_p)++; - t_real = __SMLSLD(sample6, tap6, t_real); - t_imag = __SMLALDX(sample6, tap6, t_imag); - t_real = __SMLSLD(sample7, tap7, t_real); - t_imag = __SMLALDX(sample7, tap7, t_imag); - - loop_count--; - } - - /* TODO: Re-evaluate whether saturation is performed, normalization, - * all that jazz. - */ - const int32_t r = t_real >> 16; - const int32_t i = t_imag >> 16; - const int32_t r_sat = __SSAT(r, 16); - const int32_t i_sat = __SSAT(i, 16); - *__SIMD32(dst_p)++ = __PKHBT( - r_sat, - i_sat, - 16 - ); - - /* Shift sample buffer left/down by decimation factor. */ - const size_t unroll_factor = 4; - size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor; - - void* t = &samples_[0]; - const void* s = &samples_[decimation_factor_]; - - while(shift_count > 0) { - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; - shift_count--; - } - - shift_count = (taps_count_ - decimation_factor_) % unroll_factor; - while(shift_count > 0) { - *__SIMD32(t)++ = *__SIMD32(s)++; - shift_count--; - } - - outer_count--; - } - - return result; + const buffer_c16_t& src, + const buffer_c16_t& dst) { + /* int16_t input (sample count "n" must be multiple of decimation_factor) + * -> int16_t output, decimated by decimation_factor. + * taps are normalized to 1 << 16 == 1.0. + */ + const auto output_sampling_rate = src.sampling_rate / decimation_factor_; + const size_t output_samples = src.count / decimation_factor_; + + void* dst_p = dst.p; + const buffer_c16_t result{dst.p, output_samples, output_sampling_rate}; + + const void* src_p = src.p; + size_t outer_count = output_samples; + while (outer_count > 0) { + /* Put new samples into delay buffer */ + void* z_new_p = &samples_[taps_count_ - decimation_factor_]; + for (size_t i = 0; i < decimation_factor_; i++) { + *__SIMD32(z_new_p)++ = *__SIMD32(src_p)++; + } + + size_t loop_count = taps_count_ / 8; + void* t_p = &taps_reversed_[0]; + void* z_p = &samples_[0]; + + int64_t t_real = 0; + int64_t t_imag = 0; + + while (loop_count > 0) { + const auto tap0 = *__SIMD32(t_p)++; + const auto sample0 = *__SIMD32(z_p)++; + const auto tap1 = *__SIMD32(t_p)++; + const auto sample1 = *__SIMD32(z_p)++; + t_real = __SMLSLD(sample0, tap0, t_real); + t_imag = __SMLALDX(sample0, tap0, t_imag); + t_real = __SMLSLD(sample1, tap1, t_real); + t_imag = __SMLALDX(sample1, tap1, t_imag); + + const auto tap2 = *__SIMD32(t_p)++; + const auto sample2 = *__SIMD32(z_p)++; + const auto tap3 = *__SIMD32(t_p)++; + const auto sample3 = *__SIMD32(z_p)++; + t_real = __SMLSLD(sample2, tap2, t_real); + t_imag = __SMLALDX(sample2, tap2, t_imag); + t_real = __SMLSLD(sample3, tap3, t_real); + t_imag = __SMLALDX(sample3, tap3, t_imag); + + const auto tap4 = *__SIMD32(t_p)++; + const auto sample4 = *__SIMD32(z_p)++; + const auto tap5 = *__SIMD32(t_p)++; + const auto sample5 = *__SIMD32(z_p)++; + t_real = __SMLSLD(sample4, tap4, t_real); + t_imag = __SMLALDX(sample4, tap4, t_imag); + t_real = __SMLSLD(sample5, tap5, t_real); + t_imag = __SMLALDX(sample5, tap5, t_imag); + + const auto tap6 = *__SIMD32(t_p)++; + const auto sample6 = *__SIMD32(z_p)++; + const auto tap7 = *__SIMD32(t_p)++; + const auto sample7 = *__SIMD32(z_p)++; + t_real = __SMLSLD(sample6, tap6, t_real); + t_imag = __SMLALDX(sample6, tap6, t_imag); + t_real = __SMLSLD(sample7, tap7, t_real); + t_imag = __SMLALDX(sample7, tap7, t_imag); + + loop_count--; + } + + /* TODO: Re-evaluate whether saturation is performed, normalization, + * all that jazz. + */ + const int32_t r = t_real >> 16; + const int32_t i = t_imag >> 16; + const int32_t r_sat = __SSAT(r, 16); + const int32_t i_sat = __SSAT(i, 16); + *__SIMD32(dst_p)++ = __PKHBT( + r_sat, + i_sat, + 16); + + /* Shift sample buffer left/down by decimation factor. */ + const size_t unroll_factor = 4; + size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor; + + void* t = &samples_[0]; + const void* s = &samples_[decimation_factor_]; + + while (shift_count > 0) { + *__SIMD32(t)++ = *__SIMD32(s)++; + *__SIMD32(t)++ = *__SIMD32(s)++; + *__SIMD32(t)++ = *__SIMD32(s)++; + *__SIMD32(t)++ = *__SIMD32(s)++; + shift_count--; + } + + shift_count = (taps_count_ - decimation_factor_) % unroll_factor; + while (shift_count > 0) { + *__SIMD32(t)++ = *__SIMD32(s)++; + shift_count--; + } + + outer_count--; + } + + return result; } buffer_s16_t DecimateBy2CIC4Real::execute( - const buffer_s16_t& src, - const buffer_s16_t& dst -) { - auto src_p = src.p; - auto dst_p = dst.p; - int32_t n = src.count; - for(; n>0; n-=2) { - /* TODO: Probably a lot of room to optimize... */ - z[0] = z[2]; - z[1] = z[3]; - z[2] = z[4]; - z[3] = *(src_p++); - z[4] = *(src_p++); - - int32_t t = z[0] + z[1] * 4 + z[2] * 6 + z[3] * 4 + z[4]; - *(dst_p++) = t / 16; - } - - return { dst.p, src.count / 2, src.sampling_rate / 2 }; + const buffer_s16_t& src, + const buffer_s16_t& dst) { + auto src_p = src.p; + auto dst_p = dst.p; + int32_t n = src.count; + for (; n > 0; n -= 2) { + /* TODO: Probably a lot of room to optimize... */ + z[0] = z[2]; + z[1] = z[3]; + z[2] = z[4]; + z[3] = *(src_p++); + z[4] = *(src_p++); + + int32_t t = z[0] + z[1] * 4 + z[2] * 6 + z[3] * 4 + z[4]; + *(dst_p++) = t / 16; + } + + return {dst.p, src.count / 2, src.sampling_rate / 2}; } } /* namespace decimate */ diff --git a/firmware/baseband/dsp_decimate.hpp b/firmware/baseband/dsp_decimate.hpp index bf8a82bc6..a76c0b034 100644 --- a/firmware/baseband/dsp_decimate.hpp +++ b/firmware/baseband/dsp_decimate.hpp @@ -37,228 +37,210 @@ namespace dsp { namespace decimate { class Complex8DecimateBy2CIC3 { -public: - buffer_c16_t execute( - const buffer_c8_t& src, - const buffer_c16_t& dst - ); - -private: - uint32_t _i1_i0 { 0 }; - uint32_t _q1_q0 { 0 }; + public: + buffer_c16_t execute( + const buffer_c8_t& src, + const buffer_c16_t& dst); + + private: + uint32_t _i1_i0{0}; + uint32_t _q1_q0{0}; }; class TranslateByFSOver4AndDecimateBy2CIC3 { -public: - buffer_c16_t execute( - const buffer_c8_t& src, - const buffer_c16_t& dst - ); - -private: - uint32_t _q1_i0 { 0 }; - uint32_t _q0_i1 { 0 }; + public: + buffer_c16_t execute( + const buffer_c8_t& src, + const buffer_c16_t& dst); + + private: + uint32_t _q1_i0{0}; + uint32_t _q0_i1{0}; }; class DecimateBy2CIC3 { -public: - buffer_c16_t execute( - const buffer_c16_t& src, - const buffer_c16_t& dst - ); - -private: - uint32_t _iq0 { 0 }; - uint32_t _iq1 { 0 }; + public: + buffer_c16_t execute( + const buffer_c16_t& src, + const buffer_c16_t& dst); + + private: + uint32_t _iq0{0}; + uint32_t _iq1{0}; }; class FIR64AndDecimateBy2Real { -public: - static constexpr size_t taps_count = 64; + public: + static constexpr size_t taps_count = 64; - void configure( - const std::array& taps - ); + void configure( + const std::array& taps); - buffer_s16_t execute( - const buffer_s16_t& src, - const buffer_s16_t& dst - ); + buffer_s16_t execute( + const buffer_s16_t& src, + const buffer_s16_t& dst); -private: - std::array z { }; - std::array taps { }; + private: + std::array z{}; + std::array taps{}; }; class FIRC8xR16x24FS4Decim4 { -public: - static constexpr size_t taps_count = 24; - static constexpr size_t decimation_factor = 4; - - using sample_t = complex8_t; - using tap_t = int16_t; - - enum class Shift : bool { - Down = true, - Up = false - }; - - void configure( - const std::array& taps, - const int32_t scale, - const Shift shift = Shift::Down - ); - - buffer_c16_t execute( - const buffer_c8_t& src, - const buffer_c16_t& dst - ); - -private: - std::array z_ { }; - std::array taps_ { }; - int32_t output_scale = 0; + public: + static constexpr size_t taps_count = 24; + static constexpr size_t decimation_factor = 4; + + using sample_t = complex8_t; + using tap_t = int16_t; + + enum class Shift : bool { + Down = true, + Up = false + }; + + void configure( + const std::array& taps, + const int32_t scale, + const Shift shift = Shift::Down); + + buffer_c16_t execute( + const buffer_c8_t& src, + const buffer_c16_t& dst); + + private: + std::array z_{}; + std::array taps_{}; + int32_t output_scale = 0; }; class FIRC8xR16x24FS4Decim8 { -public: - static constexpr size_t taps_count = 24; - static constexpr size_t decimation_factor = 8; - - using sample_t = complex8_t; - using tap_t = int16_t; - - enum class Shift : bool { - Down = true, - Up = false - }; - - void configure( - const std::array& taps, - const int32_t scale, - const Shift shift = Shift::Down - ); - - buffer_c16_t execute( - const buffer_c8_t& src, - const buffer_c16_t& dst - ); - -private: - std::array z_ { }; - std::array taps_ { }; - int32_t output_scale = 0; + public: + static constexpr size_t taps_count = 24; + static constexpr size_t decimation_factor = 8; + + using sample_t = complex8_t; + using tap_t = int16_t; + + enum class Shift : bool { + Down = true, + Up = false + }; + + void configure( + const std::array& taps, + const int32_t scale, + const Shift shift = Shift::Down); + + buffer_c16_t execute( + const buffer_c8_t& src, + const buffer_c16_t& dst); + + private: + std::array z_{}; + std::array taps_{}; + int32_t output_scale = 0; }; class FIRC16xR16x16Decim2 { -public: - static constexpr size_t taps_count = 16; - static constexpr size_t decimation_factor = 2; - - using sample_t = complex16_t; - using tap_t = int16_t; - - void configure( - const std::array& taps, - const int32_t scale - ); - - buffer_c16_t execute( - const buffer_c16_t& src, - const buffer_c16_t& dst - ); - -private: - std::array z_ { }; - std::array taps_ { }; - int32_t output_scale = 0; + public: + static constexpr size_t taps_count = 16; + static constexpr size_t decimation_factor = 2; + + using sample_t = complex16_t; + using tap_t = int16_t; + + void configure( + const std::array& taps, + const int32_t scale); + + buffer_c16_t execute( + const buffer_c16_t& src, + const buffer_c16_t& dst); + + private: + std::array z_{}; + std::array taps_{}; + int32_t output_scale = 0; }; class FIRC16xR16x32Decim8 { -public: - static constexpr size_t taps_count = 32; - static constexpr size_t decimation_factor = 8; - - using sample_t = complex16_t; - using tap_t = int16_t; - - void configure( - const std::array& taps, - const int32_t scale - ); - - buffer_c16_t execute( - const buffer_c16_t& src, - const buffer_c16_t& dst - ); - -private: - std::array z_ { }; - std::array taps_ { }; - int32_t output_scale = 0; + public: + static constexpr size_t taps_count = 32; + static constexpr size_t decimation_factor = 8; + + using sample_t = complex16_t; + using tap_t = int16_t; + + void configure( + const std::array& taps, + const int32_t scale); + + buffer_c16_t execute( + const buffer_c16_t& src, + const buffer_c16_t& dst); + + private: + std::array z_{}; + std::array taps_{}; + int32_t output_scale = 0; }; class FIRAndDecimateComplex { -public: - using sample_t = complex16_t; - using tap_t = complex16_t; - - using taps_t = tap_t[]; - - /* NOTE! Current code makes an assumption that block of samples to be - * processed will be a multiple of the taps_count. - */ - - template - void configure( - const T& taps, - const size_t decimation_factor - ) { - configure(taps.data(), taps.size(), decimation_factor); - } - - buffer_c16_t execute( - const buffer_c16_t& src, - const buffer_c16_t& dst - ); - -private: - using samples_t = sample_t[]; - - std::unique_ptr samples_ { }; - std::unique_ptr taps_reversed_ { }; - size_t taps_count_ { 0 }; - size_t decimation_factor_ { 1 }; - - template - void configure( - const T* const taps, - const size_t taps_count, - const size_t decimation_factor - ) { - configure_common(taps_count, decimation_factor); - std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); - } - - void configure_common( - const size_t taps_count, - const size_t decimation_factor - ); + public: + using sample_t = complex16_t; + using tap_t = complex16_t; + + using taps_t = tap_t[]; + + /* NOTE! Current code makes an assumption that block of samples to be + * processed will be a multiple of the taps_count. + */ + + template + void configure( + const T& taps, + const size_t decimation_factor) { + configure(taps.data(), taps.size(), decimation_factor); + } + + buffer_c16_t execute( + const buffer_c16_t& src, + const buffer_c16_t& dst); + + private: + using samples_t = sample_t[]; + + std::unique_ptr samples_{}; + std::unique_ptr taps_reversed_{}; + size_t taps_count_{0}; + size_t decimation_factor_{1}; + + template + void configure( + const T* const taps, + const size_t taps_count, + const size_t decimation_factor) { + configure_common(taps_count, decimation_factor); + std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); + } + + void configure_common( + const size_t taps_count, + const size_t decimation_factor); }; class DecimateBy2CIC4Real { -public: - buffer_s16_t execute( - const buffer_s16_t& src, - const buffer_s16_t& dst - ); - -private: - int16_t z[5] { }; - int16_t _dummy { }; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0? + public: + buffer_s16_t execute( + const buffer_s16_t& src, + const buffer_s16_t& dst); + + private: + int16_t z[5]{}; + int16_t _dummy{}; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0? }; } /* namespace decimate */ } /* namespace dsp */ -#endif/*__DSP_DECIMATE_H__*/ +#endif /*__DSP_DECIMATE_H__*/ diff --git a/firmware/baseband/dsp_demodulate.cpp b/firmware/baseband/dsp_demodulate.cpp index 9e96998a6..695668a16 100644 --- a/firmware/baseband/dsp_demodulate.cpp +++ b/firmware/baseband/dsp_demodulate.cpp @@ -31,121 +31,116 @@ namespace dsp { namespace demodulate { buffer_f32_t AM::execute( - const buffer_c16_t& src, - const buffer_f32_t& dst -) { - const void* src_p = src.p; - const auto src_end = &src.p[src.count]; - auto dst_p = dst.p; - while(src_p < src_end) { - const uint32_t sample0 = *__SIMD32(src_p)++; - const uint32_t sample1 = *__SIMD32(src_p)++; - const uint32_t mag_sq0 = __SMUAD(sample0, sample0); - const uint32_t mag_sq1 = __SMUAD(sample1, sample1); - *(dst_p++) = __builtin_sqrtf(mag_sq0) * k; - *(dst_p++) = __builtin_sqrtf(mag_sq1) * k; - } - - return { dst.p, src.count, src.sampling_rate }; + const buffer_c16_t& src, + const buffer_f32_t& dst) { + const void* src_p = src.p; + const auto src_end = &src.p[src.count]; + auto dst_p = dst.p; + while (src_p < src_end) { + const uint32_t sample0 = *__SIMD32(src_p)++; + const uint32_t sample1 = *__SIMD32(src_p)++; + const uint32_t mag_sq0 = __SMUAD(sample0, sample0); + const uint32_t mag_sq1 = __SMUAD(sample1, sample1); + *(dst_p++) = __builtin_sqrtf(mag_sq0) * k; + *(dst_p++) = __builtin_sqrtf(mag_sq1) * k; + } + + return {dst.p, src.count, src.sampling_rate}; } buffer_f32_t SSB::execute( - const buffer_c16_t& src, - const buffer_f32_t& dst -) { - const complex16_t* src_p = src.p; - const auto src_end = &src.p[src.count]; - auto dst_p = dst.p; - while(src_p < src_end) { - *(dst_p++) = (src_p++)->real() * k; - *(dst_p++) = (src_p++)->real() * k; - *(dst_p++) = (src_p++)->real() * k; - *(dst_p++) = (src_p++)->real() * k; - } - - return { dst.p, src.count, src.sampling_rate }; + const buffer_c16_t& src, + const buffer_f32_t& dst) { + const complex16_t* src_p = src.p; + const auto src_end = &src.p[src.count]; + auto dst_p = dst.p; + while (src_p < src_end) { + *(dst_p++) = (src_p++)->real() * k; + *(dst_p++) = (src_p++)->real() * k; + *(dst_p++) = (src_p++)->real() * k; + *(dst_p++) = (src_p++)->real() * k; + } + + return {dst.p, src.count, src.sampling_rate}; } /* static inline float angle_approx_4deg0(const complex32_t t) { - const auto x = static_cast(t.imag()) / static_cast(t.real()); - return 16384.0f * x; + const auto x = static_cast(t.imag()) / static_cast(t.real()); + return 16384.0f * x; } */ static inline float angle_approx_0deg27(const complex32_t t) { - if( t.real() ) { - const auto x = static_cast(t.imag()) / static_cast(t.real()); - return x / (1.0f + 0.28086f * x * x); - } else { - return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f; - } + if (t.real()) { + const auto x = static_cast(t.imag()) / static_cast(t.real()); + return x / (1.0f + 0.28086f * x * x); + } else { + return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f; + } } static inline float angle_precise(const complex32_t t) { - return atan2f(t.imag(), t.real()); + return atan2f(t.imag(), t.real()); } buffer_f32_t FM::execute( - const buffer_c16_t& src, - const buffer_f32_t& dst -) { - auto z = z_; - - const void* src_p = src.p; - const auto src_end = &src.p[src.count]; - auto dst_p = dst.p; - while(src_p < src_end) { - const auto s0 = *__SIMD32(src_p)++; - const auto s1 = *__SIMD32(src_p)++; - const auto t0 = multiply_conjugate_s16_s32(s0, z); - const auto t1 = multiply_conjugate_s16_s32(s1, s0); - z = s1; - *(dst_p++) = angle_precise(t0) * kf; - *(dst_p++) = angle_precise(t1) * kf; - } - z_ = z; - - return { dst.p, src.count, src.sampling_rate }; + const buffer_c16_t& src, + const buffer_f32_t& dst) { + auto z = z_; + + const void* src_p = src.p; + const auto src_end = &src.p[src.count]; + auto dst_p = dst.p; + while (src_p < src_end) { + const auto s0 = *__SIMD32(src_p)++; + const auto s1 = *__SIMD32(src_p)++; + const auto t0 = multiply_conjugate_s16_s32(s0, z); + const auto t1 = multiply_conjugate_s16_s32(s1, s0); + z = s1; + *(dst_p++) = angle_precise(t0) * kf; + *(dst_p++) = angle_precise(t1) * kf; + } + z_ = z; + + return {dst.p, src.count, src.sampling_rate}; } buffer_s16_t FM::execute( - const buffer_c16_t& src, - const buffer_s16_t& dst -) { - auto z = z_; - - const void* src_p = src.p; - const auto src_end = &src.p[src.count]; - void* dst_p = dst.p; - while(src_p < src_end) { - const auto s0 = *__SIMD32(src_p)++; - const auto s1 = *__SIMD32(src_p)++; - const auto t0 = multiply_conjugate_s16_s32(s0, z); - const auto t1 = multiply_conjugate_s16_s32(s1, s0); - z = s1; - const int32_t theta0_int = angle_approx_0deg27(t0) * ks16; - const int32_t theta0_sat = __SSAT(theta0_int, 16); - const int32_t theta1_int = angle_approx_0deg27(t1) * ks16; - const int32_t theta1_sat = __SSAT(theta1_int, 16); - *__SIMD32(dst_p)++ = __PKHBT( - theta0_sat, - theta1_sat, - 16 - ); - } - z_ = z; - - return { dst.p, src.count, src.sampling_rate }; + const buffer_c16_t& src, + const buffer_s16_t& dst) { + auto z = z_; + + const void* src_p = src.p; + const auto src_end = &src.p[src.count]; + void* dst_p = dst.p; + while (src_p < src_end) { + const auto s0 = *__SIMD32(src_p)++; + const auto s1 = *__SIMD32(src_p)++; + const auto t0 = multiply_conjugate_s16_s32(s0, z); + const auto t1 = multiply_conjugate_s16_s32(s1, s0); + z = s1; + const int32_t theta0_int = angle_approx_0deg27(t0) * ks16; + const int32_t theta0_sat = __SSAT(theta0_int, 16); + const int32_t theta1_int = angle_approx_0deg27(t1) * ks16; + const int32_t theta1_sat = __SSAT(theta1_int, 16); + *__SIMD32(dst_p)++ = __PKHBT( + theta0_sat, + theta1_sat, + 16); + } + z_ = z; + + return {dst.p, src.count, src.sampling_rate}; } void FM::configure(const float sampling_rate, const float deviation_hz) { - /* - * angle: -pi to pi. output range: -32768 to 32767. - * Maximum delta-theta (output of atan2) at maximum deviation frequency: - * delta_theta_max = 2 * pi * deviation / sampling_rate - */ - kf = static_cast(1.0f / (2.0 * pi * deviation_hz / sampling_rate)); - ks16 = 32767.0f * kf; + /* + * angle: -pi to pi. output range: -32768 to 32767. + * Maximum delta-theta (output of atan2) at maximum deviation frequency: + * delta_theta_max = 2 * pi * deviation / sampling_rate + */ + kf = static_cast(1.0f / (2.0 * pi * deviation_hz / sampling_rate)); + ks16 = 32767.0f * kf; } -} -} +} // namespace demodulate +} // namespace dsp diff --git a/firmware/baseband/dsp_demodulate.hpp b/firmware/baseband/dsp_demodulate.hpp index 727061679..d965ca080 100644 --- a/firmware/baseband/dsp_demodulate.hpp +++ b/firmware/baseband/dsp_demodulate.hpp @@ -28,48 +28,44 @@ namespace dsp { namespace demodulate { class AM { -public: - buffer_f32_t execute( - const buffer_c16_t& src, - const buffer_f32_t& dst - ); + public: + buffer_f32_t execute( + const buffer_c16_t& src, + const buffer_f32_t& dst); -private: - static constexpr float k = 1.0f / 32768.0f; + private: + static constexpr float k = 1.0f / 32768.0f; }; class SSB { -public: - buffer_f32_t execute( - const buffer_c16_t& src, - const buffer_f32_t& dst - ); + public: + buffer_f32_t execute( + const buffer_c16_t& src, + const buffer_f32_t& dst); -private: - static constexpr float k = 1.0f / 32768.0f; + private: + static constexpr float k = 1.0f / 32768.0f; }; class FM { -public: - buffer_f32_t execute( - const buffer_c16_t& src, - const buffer_f32_t& dst - ); + public: + buffer_f32_t execute( + const buffer_c16_t& src, + const buffer_f32_t& dst); - buffer_s16_t execute( - const buffer_c16_t& src, - const buffer_s16_t& dst - ); + buffer_s16_t execute( + const buffer_c16_t& src, + const buffer_s16_t& dst); - void configure(const float sampling_rate, const float deviation_hz); + void configure(const float sampling_rate, const float deviation_hz); -private: - complex16_t::rep_type z_ { 0 }; - float kf { 0 }; - float ks16 { 0 }; + private: + complex16_t::rep_type z_{0}; + float kf{0}; + float ks16{0}; }; } /* namespace demodulate */ } /* namespace dsp */ -#endif/*__DSP_DEMODULATE_H__*/ +#endif /*__DSP_DEMODULATE_H__*/ diff --git a/firmware/baseband/dsp_goertzel.cpp b/firmware/baseband/dsp_goertzel.cpp index eaa374d05..c4e7680f9 100644 --- a/firmware/baseband/dsp_goertzel.cpp +++ b/firmware/baseband/dsp_goertzel.cpp @@ -28,29 +28,26 @@ namespace dsp { GoertzelDetector::GoertzelDetector( - const float frequency, - const uint32_t sample_rate -) { - coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0); + const float frequency, + const uint32_t sample_rate) { + coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0); } float GoertzelDetector::execute( - const buffer_s16_t& src -) { - - const size_t count = src.count; - - for (size_t i = 0; i < count; i++) { - s[2] = s[1]; - s[1] = s[0]; - s[0] = src.p[i] + coefficient * s[1] - s[2]; - } - - const uint32_t sq0 = s[0] * s[0]; - const uint32_t sq1 = s[1] * s[1]; - float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient); - - return magnitude; + const buffer_s16_t& src) { + const size_t count = src.count; + + for (size_t i = 0; i < count; i++) { + s[2] = s[1]; + s[1] = s[0]; + s[0] = src.p[i] + coefficient * s[1] - s[2]; + } + + const uint32_t sq0 = s[0] * s[0]; + const uint32_t sq1 = s[1] * s[1]; + float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient); + + return magnitude; } } /* namespace dsp */ diff --git a/firmware/baseband/dsp_goertzel.hpp b/firmware/baseband/dsp_goertzel.hpp index 7b733a0e7..65cf5b1d1 100644 --- a/firmware/baseband/dsp_goertzel.hpp +++ b/firmware/baseband/dsp_goertzel.hpp @@ -28,16 +28,16 @@ namespace dsp { class GoertzelDetector { -public: - GoertzelDetector(const float frequency, const uint32_t sample_rate); - - float execute(const buffer_s16_t& src); - -private: - float coefficient { }; - int16_t s[2] { 0 }; + public: + GoertzelDetector(const float frequency, const uint32_t sample_rate); + + float execute(const buffer_s16_t& src); + + private: + float coefficient{}; + int16_t s[2]{0}; }; } /* namespace dsp */ -#endif/*__DSP_GOERTZEL_H__*/ +#endif /*__DSP_GOERTZEL_H__*/ diff --git a/firmware/baseband/dsp_hilbert.cpp b/firmware/baseband/dsp_hilbert.cpp index ff9e793c4..b858c8fa6 100644 --- a/firmware/baseband/dsp_hilbert.cpp +++ b/firmware/baseband/dsp_hilbert.cpp @@ -25,33 +25,57 @@ namespace dsp { HilbertTransform::HilbertTransform() { - n = 0; + n = 0; - sos_i.configure(half_band_lpf_config); - sos_q.configure(half_band_lpf_config); + sos_i.configure(half_band_lpf_config); + sos_q.configure(half_band_lpf_config); } -void HilbertTransform::execute(float in, float &out_i, float &out_q) { - float a = 0, b = 0; - - switch (n) { - case 0: a = in; b = 0; break; - case 1: a = 0; b = -in; break; - case 2: a = -in; b = 0; break; - case 3: a = 0; b = in; break; - } - - float i = sos_i.execute(a) * 2.0f; - float q = sos_q.execute(b) * 2.0f; - - switch (n) { - case 0: out_i = i; out_q = q; break; - case 1: out_i = -q; out_q = i; break; - case 2: out_i = -i; out_q = -q; break; - case 3: out_i = q; out_q = -i; break; - } - - n = (n + 1) % 4; +void HilbertTransform::execute(float in, float& out_i, float& out_q) { + float a = 0, b = 0; + + switch (n) { + case 0: + a = in; + b = 0; + break; + case 1: + a = 0; + b = -in; + break; + case 2: + a = -in; + b = 0; + break; + case 3: + a = 0; + b = in; + break; + } + + float i = sos_i.execute(a) * 2.0f; + float q = sos_q.execute(b) * 2.0f; + + switch (n) { + case 0: + out_i = i; + out_q = q; + break; + case 1: + out_i = -q; + out_q = i; + break; + case 2: + out_i = -i; + out_q = -q; + break; + case 3: + out_i = q; + out_q = -i; + break; + } + + n = (n + 1) % 4; } } /* namespace dsp */ diff --git a/firmware/baseband/dsp_hilbert.hpp b/firmware/baseband/dsp_hilbert.hpp index 7687ccb31..e5e15f625 100644 --- a/firmware/baseband/dsp_hilbert.hpp +++ b/firmware/baseband/dsp_hilbert.hpp @@ -28,17 +28,16 @@ namespace dsp { class HilbertTransform { -public: - - HilbertTransform(); - void execute(float in, float &out_i, float &out_q); - -private: - uint8_t n = 0; - SOSFilter<5> sos_i = {}; - SOSFilter<5> sos_q = {}; + public: + HilbertTransform(); + void execute(float in, float& out_i, float& out_q); + + private: + uint8_t n = 0; + SOSFilter<5> sos_i = {}; + SOSFilter<5> sos_q = {}; }; } /* namespace dsp */ -#endif/*__DSP_HILBERT_H__*/ +#endif /*__DSP_HILBERT_H__*/ diff --git a/firmware/baseband/dsp_modulate.cpp b/firmware/baseband/dsp_modulate.cpp index e3272003c..e141f3399 100644 --- a/firmware/baseband/dsp_modulate.cpp +++ b/firmware/baseband/dsp_modulate.cpp @@ -31,207 +31,219 @@ Modulator::~Modulator() { } Mode Modulator::get_mode() { - return mode; + return mode; } void Modulator::set_mode(Mode new_mode) { - mode = new_mode; + mode = new_mode; } void Modulator::set_over(uint32_t new_over) { over = new_over; } -void Modulator::set_gain_shiftbits_vumeter_beep(float new_audio_gain ,uint8_t new_audio_shift_bits_s16, bool new_play_beep ) { - //new_audio_shift_bits_s16 are the direct shift bits (FM mod >>x) , and it is fixed to >>8_FM (AK) or 4,5,6, (WM boost OFF) or 6,7 (WM boost ON) - audio_gain = new_audio_gain ; - audio_shift_bits_s16_FM = new_audio_shift_bits_s16; //FM : >>8(AK) fixed , >>4,5,6 (WM boost OFF) - if (new_audio_shift_bits_s16==8) { //FM : we are in AK codec IC => for AM-SSB-DSB we were using >>2 fixed (wm boost ON) . - audio_shift_bits_s16_AM_DSB_SSB = 2; //AM-DSB-SSB: >>2(AK) fixed , >>0,1,2 (WM boost OFF) - } else { - audio_shift_bits_s16_AM_DSB_SSB = (new_audio_shift_bits_s16-4) ; //AM-DSB-SSB: >>0,1,2 (WM boost OFF), >>2,3 (WM boost ON) - } - play_beep = new_play_beep; +void Modulator::set_gain_shiftbits_vumeter_beep(float new_audio_gain, uint8_t new_audio_shift_bits_s16, bool new_play_beep) { + // new_audio_shift_bits_s16 are the direct shift bits (FM mod >>x) , and it is fixed to >>8_FM (AK) or 4,5,6, (WM boost OFF) or 6,7 (WM boost ON) + audio_gain = new_audio_gain; + audio_shift_bits_s16_FM = new_audio_shift_bits_s16; // FM : >>8(AK) fixed , >>4,5,6 (WM boost OFF) + if (new_audio_shift_bits_s16 == 8) { // FM : we are in AK codec IC => for AM-SSB-DSB we were using >>2 fixed (wm boost ON) . + audio_shift_bits_s16_AM_DSB_SSB = 2; // AM-DSB-SSB: >>2(AK) fixed , >>0,1,2 (WM boost OFF) + } else { + audio_shift_bits_s16_AM_DSB_SSB = (new_audio_shift_bits_s16 - 4); // AM-DSB-SSB: >>0,1,2 (WM boost OFF), >>2,3 (WM boost ON) + } + play_beep = new_play_beep; } -int32_t Modulator::apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message ) { - - if (play_beep) { // We need to add audio beep sample. - if (new_beep_timer) { - new_beep_timer--; - } else { - new_beep_timer = baseband_fs * 0.05; // 50ms - - if (new_beep_index == BEEP_TONES_NB) { - configured_in = false; - shared_memory.application_queue.push(new_txprogress_message); - } else { - beep_gen.configure(beep_deltas[new_beep_index], 1.0); // config sequentially the audio beep tone. - new_beep_index++; - } - } - sample_in = beep_gen.process(0); // Get sample of the selected sequence of 6 beep tones , and overwrite audio sample. Mix 0%. - } - return sample_in; // Return audio mic scaled with gain , 8 bit sample or audio beep sample. +int32_t Modulator::apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message) { + if (play_beep) { // We need to add audio beep sample. + if (new_beep_timer) { + new_beep_timer--; + } else { + new_beep_timer = baseband_fs * 0.05; // 50ms + + if (new_beep_index == BEEP_TONES_NB) { + configured_in = false; + shared_memory.application_queue.push(new_txprogress_message); + } else { + beep_gen.configure(beep_deltas[new_beep_index], 1.0); // config sequentially the audio beep tone. + new_beep_index++; + } + } + sample_in = beep_gen.process(0); // Get sample of the selected sequence of 6 beep tones , and overwrite audio sample. Mix 0%. + } + return sample_in; // Return audio mic scaled with gain , 8 bit sample or audio beep sample. } - /// -SSB::SSB() : hilbert() { - mode = Mode::LSB; +SSB::SSB() + : hilbert() { + mode = Mode::LSB; } -void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer,TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) { - //unused - (void)configured_in ; - (void)new_beep_index ; - (void)new_beep_timer ; - (void)new_txprogress_message ; - - // No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now . - int32_t sample = 0; - int8_t re = 0, im = 0; - - for (size_t counter = 0; counter < buffer.count; counter++) { - if (counter % 128 == 0) { - float i = 0.0, q = 0.0; - - sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off) - sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. - - //switch (mode) { - //case Mode::LSB: - hilbert.execute(sample / 32768.0f, i, q); - //case Mode::USB: hilbert.execute(sample / 32768.0f, q, i); - //default: break; - //} - - i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation) - q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation) - switch (mode) { - case Mode::LSB: re = q; im = i; break; - case Mode::USB: re = i; im = q; break; - default: re = 0; im = 0; break; - } - //re = q; - //im = i; - //break; - - } - - buffer.p[counter] = { re, im }; - - // Update vu-meter bar in the LCD screen. - power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter - - if (new_power_acc_count) { - new_power_acc_count--; - } else { // power_acc_count = 0 - new_power_acc_count = new_divider; - new_level_message.value = power_acc / (new_divider *8); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter . - shared_memory.application_queue.push(new_level_message); - power_acc = 0; - } - - } +void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) { + // unused + (void)configured_in; + (void)new_beep_index; + (void)new_beep_timer; + (void)new_txprogress_message; + + // No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now . + int32_t sample = 0; + int8_t re = 0, im = 0; + + for (size_t counter = 0; counter < buffer.count; counter++) { + if (counter % 128 == 0) { + float i = 0.0, q = 0.0; + + sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off) + sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. + + // switch (mode) { + // case Mode::LSB: + hilbert.execute(sample / 32768.0f, i, q); + // case Mode::USB: hilbert.execute(sample / 32768.0f, q, i); + // default: break; + // } + + i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation) + q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation) + switch (mode) { + case Mode::LSB: + re = q; + im = i; + break; + case Mode::USB: + re = i; + im = q; + break; + default: + re = 0; + im = 0; + break; + } + // re = q; + // im = i; + // break; + } + + buffer.p[counter] = {re, im}; + + // Update vu-meter bar in the LCD screen. + power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter + + if (new_power_acc_count) { + new_power_acc_count--; + } else { // power_acc_count = 0 + new_power_acc_count = new_divider; + new_level_message.value = power_acc / (new_divider * 8); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter . + shared_memory.application_queue.push(new_level_message); + power_acc = 0; + } + } } /// FM::FM() { - mode = Mode::FM; + mode = Mode::FM; } void FM::set_fm_delta(uint32_t new_delta) { - fm_delta = new_delta; + fm_delta = new_delta; } void FM::set_tone_gen_configure(const uint32_t set_delta, const float set_tone_mix_weight) { - tone_gen.configure(set_delta, set_tone_mix_weight); -} - -void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) { - int32_t sample = 0; - int8_t re, im; - - for (size_t counter = 0; counter < buffer.count; counter++) { - - sample = audio.p[counter>>6] >> audio_shift_bits_s16_FM ; // Orig. >>8 , sample = audio.p[counter / over] >> 8; (not enough efficient running code, over = 1536000/240000= 64 ) - sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. - - if (play_beep) { - sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message ); // Apply beep -if selected - atom ,sample by sample. - } else { - // Update vu-meter bar in the LCD screen. - power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter - - if (new_power_acc_count) { - new_power_acc_count--; - } else { // power_acc_count = 0 - new_power_acc_count = new_divider; - new_level_message.value = power_acc / (new_divider / 4); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter . - shared_memory.application_queue.push(new_level_message); - power_acc = 0; - } - // TODO: pending to optimize CPU running code. - // So far , we can not handle all 3 issues at the same time (vu-meter , CTCSS, beep). - sample = tone_gen.process(sample); // Add selected Key_Tone or CTCSS subtone , atom function() , sample by sample. - } - - delta = sample * fm_delta; // Modulate FM - - phase += delta; - sphase = phase >> 24; - - re = (sine_table_i8[(sphase + 64) & 255]); - im = (sine_table_i8[sphase]); - - buffer.p[counter] = { re, im }; - } + tone_gen.configure(set_delta, set_tone_mix_weight); } -AM::AM() { - mode = Mode::AM; +void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) { + int32_t sample = 0; + int8_t re, im; + + for (size_t counter = 0; counter < buffer.count; counter++) { + sample = audio.p[counter >> 6] >> audio_shift_bits_s16_FM; // Orig. >>8 , sample = audio.p[counter / over] >> 8; (not enough efficient running code, over = 1536000/240000= 64 ) + sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. + + if (play_beep) { + sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message); // Apply beep -if selected - atom ,sample by sample. + } else { + // Update vu-meter bar in the LCD screen. + power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter + + if (new_power_acc_count) { + new_power_acc_count--; + } else { // power_acc_count = 0 + new_power_acc_count = new_divider; + new_level_message.value = power_acc / (new_divider / 4); // Why ? . This division is to adj vu-meter sentitivity, to match saturation point to red-muter . + shared_memory.application_queue.push(new_level_message); + power_acc = 0; + } + // TODO: pending to optimize CPU running code. + // So far , we can not handle all 3 issues at the same time (vu-meter , CTCSS, beep). + sample = tone_gen.process(sample); // Add selected Key_Tone or CTCSS subtone , atom function() , sample by sample. + } + + delta = sample * fm_delta; // Modulate FM + + phase += delta; + sphase = phase >> 24; + + re = (sine_table_i8[(sphase + 64) & 255]); + im = (sine_table_i8[sphase]); + + buffer.p[counter] = {re, im}; + } } -void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) { - int32_t sample = 0; - int8_t re = 0, im = 0; - float q = 0.0; - - for (size_t counter = 0; counter < buffer.count; counter++) { - if (counter % 128 == 0) { - sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off) - sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. - } - - if (play_beep) { - sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message )<<5; // Apply beep -if selected - atom sample by sample. - } else { - // Update vu-meter bar in the LCD screen. - power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter - - if (new_power_acc_count) { - new_power_acc_count--; - } else { // power_acc_count = 0 - new_power_acc_count = new_divider; - new_level_message.value = power_acc / (new_divider *8); // Why ?orig / (new_divider / 4); // Why ? - shared_memory.application_queue.push(new_level_message); - power_acc = 0; - } - } - - q = sample / 32768.0f; - q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB) - switch (mode) { - case Mode::AM: re = q + 80; im = q + 80; break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier) - case Mode::DSB: re = q; im = q; break; - default: break; - } - buffer.p[counter] = { re, im }; - } +AM::AM() { + mode = Mode::AM; } +void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) { + int32_t sample = 0; + int8_t re = 0, im = 0; + float q = 0.0; + + for (size_t counter = 0; counter < buffer.count; counter++) { + if (counter % 128 == 0) { + sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off) + sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation. + } + + if (play_beep) { + sample = apply_beep(sample, configured_in, new_beep_index, new_beep_timer, new_txprogress_message) << 5; // Apply beep -if selected - atom sample by sample. + } else { + // Update vu-meter bar in the LCD screen. + power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter + + if (new_power_acc_count) { + new_power_acc_count--; + } else { // power_acc_count = 0 + new_power_acc_count = new_divider; + new_level_message.value = power_acc / (new_divider * 8); // Why ?orig / (new_divider / 4); // Why ? + shared_memory.application_queue.push(new_level_message); + power_acc = 0; + } + } + + q = sample / 32768.0f; + q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB) + switch (mode) { + case Mode::AM: + re = q + 80; + im = q + 80; + break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier) + case Mode::DSB: + re = q; + im = q; + break; + default: + break; + } + buffer.p[counter] = {re, im}; + } } -} + +} // namespace modulate +} // namespace dsp diff --git a/firmware/baseband/dsp_modulate.hpp b/firmware/baseband/dsp_modulate.hpp index e8a2c4321..f90d6b345 100644 --- a/firmware/baseband/dsp_modulate.hpp +++ b/firmware/baseband/dsp_modulate.hpp @@ -31,89 +31,86 @@ namespace dsp { namespace modulate { enum class Mode { - None, - AM, - DSB, - LSB, - USB, - FM + None, + AM, + DSB, + LSB, + USB, + FM }; /// class Modulator { -public: - virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer,bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) = 0; - virtual ~Modulator(); + public: + virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) = 0; + virtual ~Modulator(); - Mode get_mode(); - void set_mode(Mode new_mode); + Mode get_mode(); + void set_mode(Mode new_mode); void set_over(uint32_t new_over); - void set_gain_shiftbits_vumeter_beep(float new_audio_gain ,uint8_t new_audio_shift_bits_s16, bool new_play_beep ); - int32_t apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message ); - float audio_gain { }; - uint8_t audio_shift_bits_s16_FM { }; // shift bits factor to the captured ADC S16 audio sample. - uint8_t audio_shift_bits_s16_AM_DSB_SSB { }; - bool play_beep { false }; - uint32_t power_acc_count { 0 }; // this var it is initialized from Proc_mictx.cpp - uint32_t divider { }; // this var it is initialized from Proc_mictx.cpp - uint64_t power_acc { 0 }; // it is aux Accumulated sum (Absolute sample signal) , initialitzed to zero. - AudioLevelReportMessage level_message { }; - -private: - static constexpr size_t baseband_fs = 1536000U; - TXProgressMessage txprogress_message { }; - ToneGen beep_gen { }; - uint32_t beep_index { }, beep_timer { }; - - -protected: - uint32_t over = 1; - Mode mode = Mode::None; + void set_gain_shiftbits_vumeter_beep(float new_audio_gain, uint8_t new_audio_shift_bits_s16, bool new_play_beep); + int32_t apply_beep(int32_t sample_in, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message); + float audio_gain{}; + uint8_t audio_shift_bits_s16_FM{}; // shift bits factor to the captured ADC S16 audio sample. + uint8_t audio_shift_bits_s16_AM_DSB_SSB{}; + bool play_beep{false}; + uint32_t power_acc_count{0}; // this var it is initialized from Proc_mictx.cpp + uint32_t divider{}; // this var it is initialized from Proc_mictx.cpp + uint64_t power_acc{0}; // it is aux Accumulated sum (Absolute sample signal) , initialitzed to zero. + AudioLevelReportMessage level_message{}; + + private: + static constexpr size_t baseband_fs = 1536000U; + TXProgressMessage txprogress_message{}; + ToneGen beep_gen{}; + uint32_t beep_index{}, beep_timer{}; + + protected: + uint32_t over = 1; + Mode mode = Mode::None; }; /// class SSB : public Modulator { -public: - SSB(); + public: + SSB(); - virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ); + virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider); -private: - dsp::HilbertTransform hilbert; + private: + dsp::HilbertTransform hilbert; }; /// class FM : public Modulator { -public: - FM(); + public: + FM(); - virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) ; - void set_fm_delta(uint32_t new_delta); - void set_tone_gen_configure(const uint32_t delta, const float tone_mix_weight); + virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider); + void set_fm_delta(uint32_t new_delta); + void set_tone_gen_configure(const uint32_t delta, const float tone_mix_weight); -/// - -private: - uint32_t fm_delta { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - int32_t sample { 0 }, delta { }; - ToneGen tone_gen { }; - + /// + private: + uint32_t fm_delta{0}; + uint32_t phase{0}, sphase{0}; + int32_t sample{0}, delta{}; + ToneGen tone_gen{}; }; class AM : public Modulator { -public: - AM(); + public: + AM(); - virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ); + virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider); }; } /* namespace modulate */ } /* namespace dsp */ -#endif/*__DSP_MODULATE_H__*/ +#endif /*__DSP_MODULATE_H__*/ diff --git a/firmware/baseband/dsp_squelch.cpp b/firmware/baseband/dsp_squelch.cpp index b4a2d3a48..7525e09e9 100644 --- a/firmware/baseband/dsp_squelch.cpp +++ b/firmware/baseband/dsp_squelch.cpp @@ -25,29 +25,28 @@ #include bool FMSquelch::execute(const buffer_f32_t& audio) { - if( threshold_squared == 0.0f ) { - return true; - } + if (threshold_squared == 0.0f) { + return true; + } - // TODO: No hard-coded array size. - std::array squelch_energy_buffer; - const buffer_f32_t squelch_energy { - squelch_energy_buffer.data(), - squelch_energy_buffer.size() - }; - non_audio_hpf.execute(audio, squelch_energy); + // TODO: No hard-coded array size. + std::array squelch_energy_buffer; + const buffer_f32_t squelch_energy{ + squelch_energy_buffer.data(), + squelch_energy_buffer.size()}; + non_audio_hpf.execute(audio, squelch_energy); - float non_audio_max_squared = 0; - for(const auto sample : squelch_energy_buffer) { - const float sample_squared = sample * sample; - if( sample_squared > non_audio_max_squared ) { - non_audio_max_squared = sample_squared; - } - } + float non_audio_max_squared = 0; + for (const auto sample : squelch_energy_buffer) { + const float sample_squared = sample * sample; + if (sample_squared > non_audio_max_squared) { + non_audio_max_squared = sample_squared; + } + } - return (non_audio_max_squared < threshold_squared); + return (non_audio_max_squared < threshold_squared); } void FMSquelch::set_threshold(const float new_value) { - threshold_squared = new_value * new_value; + threshold_squared = new_value * new_value; } diff --git a/firmware/baseband/dsp_squelch.hpp b/firmware/baseband/dsp_squelch.hpp index 4701b9acd..6b69a5bf3 100644 --- a/firmware/baseband/dsp_squelch.hpp +++ b/firmware/baseband/dsp_squelch.hpp @@ -30,16 +30,16 @@ #include class FMSquelch { -public: - bool execute(const buffer_f32_t& audio); + public: + bool execute(const buffer_f32_t& audio); - void set_threshold(const float new_value); + void set_threshold(const float new_value); -private: - static constexpr size_t N = 32; - float threshold_squared { 0.0f }; + private: + static constexpr size_t N = 32; + float threshold_squared{0.0f}; - IIRBiquadFilter non_audio_hpf { non_audio_hpf_config }; + IIRBiquadFilter non_audio_hpf{non_audio_hpf_config}; }; -#endif/*__DSP_SQUELCH_H__*/ +#endif /*__DSP_SQUELCH_H__*/ diff --git a/firmware/baseband/event_m4.cpp b/firmware/baseband/event_m4.cpp index ee0079eb1..98767dc00 100644 --- a/firmware/baseband/event_m4.cpp +++ b/firmware/baseband/event_m4.cpp @@ -37,88 +37,85 @@ using namespace lpc43xx; extern "C" { CH_IRQ_HANDLER(MAPP_IRQHandler) { - CH_IRQ_PROLOGUE(); + CH_IRQ_PROLOGUE(); - chSysLockFromIsr(); - EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND); - chSysUnlockFromIsr(); + chSysLockFromIsr(); + EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND); + chSysUnlockFromIsr(); - creg::m0apptxevent::clear(); + creg::m0apptxevent::clear(); - CH_IRQ_EPILOGUE(); + CH_IRQ_EPILOGUE(); } - } Thread* EventDispatcher::thread_event_loop = nullptr; EventDispatcher::EventDispatcher( - std::unique_ptr baseband_processor -) : baseband_processor { std::move(baseband_processor) } -{ + std::unique_ptr baseband_processor) + : baseband_processor{std::move(baseband_processor)} { } void EventDispatcher::run() { - thread_event_loop = chThdSelf(); + thread_event_loop = chThdSelf(); - lpc43xx::creg::m0apptxevent::enable(); + lpc43xx::creg::m0apptxevent::enable(); - while(is_running) { - const auto events = wait(); - dispatch(events); - } + while (is_running) { + const auto events = wait(); + dispatch(events); + } - lpc43xx::creg::m0apptxevent::disable(); + lpc43xx::creg::m0apptxevent::disable(); } void EventDispatcher::request_stop() { - is_running = false; + is_running = false; } eventmask_t EventDispatcher::wait() { - return chEvtWaitAny(ALL_EVENTS); + return chEvtWaitAny(ALL_EVENTS); } void EventDispatcher::dispatch(const eventmask_t events) { - if( events & EVT_MASK_BASEBAND ) { - handle_baseband_queue(); - } + if (events & EVT_MASK_BASEBAND) { + handle_baseband_queue(); + } - if( events & EVT_MASK_SPECTRUM ) { - handle_spectrum(); - } + if (events & EVT_MASK_SPECTRUM) { + handle_spectrum(); + } } void EventDispatcher::handle_baseband_queue() { - const auto message = shared_memory.baseband_message; - if( message ) { - on_message(message); - } + const auto message = shared_memory.baseband_message; + if (message) { + on_message(message); + } } void EventDispatcher::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::Shutdown: - on_message_shutdown(*reinterpret_cast(message)); - break; - - default: - on_message_default(message); - shared_memory.baseband_message = nullptr; - break; - } + switch (message->id) { + case Message::ID::Shutdown: + on_message_shutdown(*reinterpret_cast(message)); + break; + + default: + on_message_default(message); + shared_memory.baseband_message = nullptr; + break; + } } void EventDispatcher::on_message_shutdown(const ShutdownMessage&) { - request_stop(); + request_stop(); } void EventDispatcher::on_message_default(const Message* const message) { - baseband_processor->on_message(message); + baseband_processor->on_message(message); } void EventDispatcher::handle_spectrum() { - const UpdateSpectrumMessage message; - baseband_processor->on_message(&message); + const UpdateSpectrumMessage message; + baseband_processor->on_message(&message); } - diff --git a/firmware/baseband/event_m4.hpp b/firmware/baseband/event_m4.hpp index 42b5d3b90..9dfa57950 100644 --- a/firmware/baseband/event_m4.hpp +++ b/firmware/baseband/event_m4.hpp @@ -35,38 +35,38 @@ constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0); constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1); class EventDispatcher { -public: - EventDispatcher(std::unique_ptr baseband_processor); + public: + EventDispatcher(std::unique_ptr baseband_processor); - void run(); - void request_stop(); + void run(); + void request_stop(); - static inline void events_flag(const eventmask_t events) { - chEvtSignal(thread_event_loop, events); - } + static inline void events_flag(const eventmask_t events) { + chEvtSignal(thread_event_loop, events); + } - static inline void events_flag_isr(const eventmask_t events) { - chEvtSignalI(thread_event_loop, events); - } + static inline void events_flag_isr(const eventmask_t events) { + chEvtSignalI(thread_event_loop, events); + } -private: - static Thread* thread_event_loop; + private: + static Thread* thread_event_loop; - std::unique_ptr baseband_processor; + std::unique_ptr baseband_processor; - bool is_running = true; + bool is_running = true; - eventmask_t wait(); + eventmask_t wait(); - void dispatch(const eventmask_t events); + void dispatch(const eventmask_t events); - void handle_baseband_queue(); + void handle_baseband_queue(); - void on_message(const Message* const message); - void on_message_shutdown(const ShutdownMessage&); - void on_message_default(const Message* const message); + void on_message(const Message* const message); + void on_message_shutdown(const ShutdownMessage&); + void on_message_default(const Message* const message); - void handle_spectrum(); + void handle_spectrum(); }; -#endif/*__EVENT_M4_H__*/ +#endif /*__EVENT_M4_H__*/ diff --git a/firmware/baseband/fxpt_atan2.cpp b/firmware/baseband/fxpt_atan2.cpp index a3531f79a..0ca1678c4 100644 --- a/firmware/baseband/fxpt_atan2.cpp +++ b/firmware/baseband/fxpt_atan2.cpp @@ -38,7 +38,7 @@ */ /* static inline int16_t q15_from_double(const double d) { - return lrint(d * 32768); + return lrint(d * 32768); } */ /** @@ -49,16 +49,16 @@ static inline int16_t q15_from_double(const double d) { * @param i 16-bit signed integer * @return negative absolute value of i; defined for all values of i */ - /* +/* static inline int16_t s16_nabs(const int16_t j) { #if (((int16_t)-1) >> 1) == ((int16_t)-1) - // signed right shift sign-extends (arithmetic) - const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement - // if j is positive (negSign is -1), xor will invert j and sub will add 1 - // otherwise j is unchanged - return (j ^ negSign) - negSign; + // signed right shift sign-extends (arithmetic) + const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement + // if j is positive (negSign is -1), xor will invert j and sub will add 1 + // otherwise j is unchanged + return (j ^ negSign) - negSign; #else - return (j < 0 ? j : -j); + return (j < 0 ? j : -j); #endif } */ @@ -71,13 +71,13 @@ static inline int16_t s16_nabs(const int16_t j) { * @return product of j and k, in same format */ static inline int16_t q15_mul(const int16_t j, const int16_t k) { - const int32_t intermediate = j * k; -#if 0 // don't round + const int32_t intermediate = j * k; +#if 0 // don't round return intermediate >> 15; -#elif 0 // biased rounding - return (intermediate + 0x4000) >> 15; -#else // unbiased rounding - return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15; +#elif 0 // biased rounding + return (intermediate + 0x4000) >> 15; +#else // unbiased rounding + return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15; #endif } @@ -92,7 +92,7 @@ static inline int16_t q15_mul(const int16_t j, const int16_t k) { * @return numer / denom in same format as numer and denom */ static inline int16_t q15_div(const int16_t numer, const int16_t denom) { - return (static_cast(numer) << 15) / denom; + return (static_cast(numer) << 15) / denom; } /** @@ -112,41 +112,41 @@ static inline int16_t q15_div(const int16_t numer, const int16_t denom) { */ static inline int16_t nabs(const int16_t j) { - //return -abs(x); - return (j < 0 ? j : -j); + // return -abs(x); + return (j < 0 ? j : -j); } int16_t fxpt_atan2(const int16_t y, const int16_t x) { - static const int16_t k1 = 2847; - static const int16_t k2 = 11039; - if (x == y) { // x/y or y/x would return -1 since 1 isn't representable - if (y > 0) { // 1/8 - return 8192; - } else if (y < 0) { // 5/8 - return 40960; - } else { // x = y = 0 - return 0; - } - } - const int16_t nabs_y = nabs(y); - const int16_t nabs_x = nabs(x); - if (nabs_x < nabs_y) { // octants 1, 4, 5, 8 - const int16_t y_over_x = q15_div(y, x); - const int16_t correction = q15_mul(k1, nabs(y_over_x)); - const int16_t unrotated = q15_mul(k2 + correction, y_over_x); - if (x > 0) { // octants 1, 8 - return unrotated; - } else { // octants 4, 5 - return 32768 + unrotated; - } - } else { // octants 2, 3, 6, 7 - const int16_t x_over_y = q15_div(x, y); - const int16_t correction = q15_mul(k1, nabs(x_over_y)); - const int16_t unrotated = q15_mul(k2 + correction, x_over_y); - if (y > 0) { // octants 2, 3 - return 16384 - unrotated; - } else { // octants 6, 7 - return 49152 - unrotated; - } - } + static const int16_t k1 = 2847; + static const int16_t k2 = 11039; + if (x == y) { // x/y or y/x would return -1 since 1 isn't representable + if (y > 0) { // 1/8 + return 8192; + } else if (y < 0) { // 5/8 + return 40960; + } else { // x = y = 0 + return 0; + } + } + const int16_t nabs_y = nabs(y); + const int16_t nabs_x = nabs(x); + if (nabs_x < nabs_y) { // octants 1, 4, 5, 8 + const int16_t y_over_x = q15_div(y, x); + const int16_t correction = q15_mul(k1, nabs(y_over_x)); + const int16_t unrotated = q15_mul(k2 + correction, y_over_x); + if (x > 0) { // octants 1, 8 + return unrotated; + } else { // octants 4, 5 + return 32768 + unrotated; + } + } else { // octants 2, 3, 6, 7 + const int16_t x_over_y = q15_div(x, y); + const int16_t correction = q15_mul(k1, nabs(x_over_y)); + const int16_t unrotated = q15_mul(k2 + correction, x_over_y); + if (y > 0) { // octants 2, 3 + return 16384 - unrotated; + } else { // octants 6, 7 + return 49152 - unrotated; + } + } } diff --git a/firmware/baseband/fxpt_atan2.hpp b/firmware/baseband/fxpt_atan2.hpp index b57d14e24..e14796efc 100644 --- a/firmware/baseband/fxpt_atan2.hpp +++ b/firmware/baseband/fxpt_atan2.hpp @@ -26,4 +26,4 @@ int16_t fxpt_atan2(const int16_t y, const int16_t x); -#endif/*__FXPT_ATAN2_H__*/ +#endif /*__FXPT_ATAN2_H__*/ diff --git a/firmware/baseband/gpdma_lli.hpp b/firmware/baseband/gpdma_lli.hpp index 3bde23716..e79a464dc 100644 --- a/firmware/baseband/gpdma_lli.hpp +++ b/firmware/baseband/gpdma_lli.hpp @@ -32,192 +32,190 @@ namespace gpdma { namespace lli { enum class ChainType : uint8_t { - Loop = 0, - OneShot = 1, + Loop = 0, + OneShot = 1, }; enum class Interrupt : uint8_t { - All = 0, - Last = 1, + All = 0, + Last = 1, }; struct ChainConfig { - ChainType type; - size_t length; - Interrupt interrupt; + ChainType type; + size_t length; + Interrupt interrupt; }; enum class BurstSize : uint8_t { - Transfer1 = 0, - Transfer4 = 1, - Transfer8 = 2, - Transfer16 = 3, - Transfer32 = 4, - Transfer64 = 5, - Transfer128 = 6, - Transfer256 = 7, + Transfer1 = 0, + Transfer4 = 1, + Transfer8 = 2, + Transfer16 = 3, + Transfer32 = 4, + Transfer64 = 5, + Transfer128 = 6, + Transfer256 = 7, }; enum class TransferWidth : uint8_t { - Byte = 0, - HalfWord = 1, - Word = 2, + Byte = 0, + HalfWord = 1, + Word = 2, }; enum class Increment : uint8_t { - No = 0, - Yes = 1, + No = 0, + Yes = 1, }; using PeripheralIndex = uint8_t; struct Endpoint { - PeripheralIndex peripheral; - BurstSize burst_size; - TransferWidth transfer_size; - Increment increment; + PeripheralIndex peripheral; + BurstSize burst_size; + TransferWidth transfer_size; + Increment increment; }; struct ChannelConfig { - ChainConfig chain; - FlowControl flow_control; - Endpoint source; - Endpoint destination; - - constexpr gpdma::channel::Control control( - const size_t transfer_size, - const bool last - ) { - return { - .transfersize = transfer_size, - .sbsize = toUType(source.burst_size), - .dbsize = toUType(destination.burst_size), - .swidth = toUType(source.transfer_size), - .dwidth = toUType(destination.transfer_size), - .s = source_endpoint_type(flow_control), - .d = destination_endpoint_type(flow_control), - .si = toUType(source.increment), - .di = toUType(destination.increment), - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U, - }; - } - - constexpr gpdma::channel::Config config() { - return { - .e = 0, - .srcperipheral = source.peripheral, - .destperipheral = destination.peripheral, - .flowcntrl = flow_control, - .ie = 1, - .itc = 1, - .l = 0, - .a = 0, - .h = 0, - }; - }; + ChainConfig chain; + FlowControl flow_control; + Endpoint source; + Endpoint destination; + + constexpr gpdma::channel::Control control( + const size_t transfer_size, + const bool last) { + return { + .transfersize = transfer_size, + .sbsize = toUType(source.burst_size), + .dbsize = toUType(destination.burst_size), + .swidth = toUType(source.transfer_size), + .dwidth = toUType(destination.transfer_size), + .s = source_endpoint_type(flow_control), + .d = destination_endpoint_type(flow_control), + .si = toUType(source.increment), + .di = toUType(destination.increment), + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U, + }; + } + + constexpr gpdma::channel::Config config() { + return { + .e = 0, + .srcperipheral = source.peripheral, + .destperipheral = destination.peripheral, + .flowcntrl = flow_control, + .ie = 1, + .itc = 1, + .l = 0, + .a = 0, + .h = 0, + }; + }; }; -constexpr ChannelConfig channel_config_baseband_tx { - { ChainType::Loop, 4, Interrupt::All }, - gpdma::FlowControl::MemoryToPeripheral_DMAControl, - { 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes }, - { 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No }, +constexpr ChannelConfig channel_config_baseband_tx{ + {ChainType::Loop, 4, Interrupt::All}, + gpdma::FlowControl::MemoryToPeripheral_DMAControl, + {0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes}, + {0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No}, }; -constexpr ChannelConfig channel_config_baseband_rx { - { ChainType::Loop, 4, Interrupt::All }, - gpdma::FlowControl::PeripheralToMemory_DMAControl, - { 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No }, - { 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes }, +constexpr ChannelConfig channel_config_baseband_rx{ + {ChainType::Loop, 4, Interrupt::All}, + gpdma::FlowControl::PeripheralToMemory_DMAControl, + {0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No}, + {0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes}, }; -constexpr ChannelConfig channel_config_audio_tx { - { ChainType::Loop, 4, Interrupt::All }, - gpdma::FlowControl::MemoryToPeripheral_DMAControl, - { 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes }, - { 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No }, +constexpr ChannelConfig channel_config_audio_tx{ + {ChainType::Loop, 4, Interrupt::All}, + gpdma::FlowControl::MemoryToPeripheral_DMAControl, + {0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes}, + {0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No}, }; -constexpr ChannelConfig channel_config_audio_rx { - { ChainType::Loop, 4, Interrupt::All }, - gpdma::FlowControl::PeripheralToMemory_DMAControl, - { 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No }, - { 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes }, +constexpr ChannelConfig channel_config_audio_rx{ + {ChainType::Loop, 4, Interrupt::All}, + gpdma::FlowControl::PeripheralToMemory_DMAControl, + {0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No}, + {0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes}, }; -constexpr ChannelConfig channel_config_rssi { - { ChainType::Loop, 4, Interrupt::All }, - gpdma::FlowControl::PeripheralToMemory_DMAControl, - { 0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No }, - { 0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes }, +constexpr ChannelConfig channel_config_rssi{ + {ChainType::Loop, 4, Interrupt::All}, + gpdma::FlowControl::PeripheralToMemory_DMAControl, + {0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No}, + {0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes}, }; class Chain { -public: - using chain_t = std::vector; - using chain_p = std::unique_ptr; - - Chain(const ChannelConfig& cc) : - chain(std::make_unique(cc.chain.length)) - { - set_lli_sequential(cc.chain_type); - set_source_address()... - } - -private: - chain_p chain; - - void set_source_peripheral(void* const address) { - set_source_address(address, 0); - } - - void set_destination_peripheral(void* const address) { - set_destination_address(address, 0); - } - - void set_source_address(void* const address, const size_t increment) { - size_t offset = 0; - for(auto& item : *chain) { - item.srcaddr = (uint32_t)address + offset; - offset += increment; - } - } - - void set_destination_address(void* const address, const size_t increment) { - size_t offset = 0; - for(auto& item : *chain) { - item.destaddr = (uint32_t)address + offset; - offset += increment; - } - } - - void set_control(const gpdma::channel::Control control) { - for(auto& item : *chain) { - item.control = control; - } - } - - void set_lli_sequential(ChainType chain_type) { - for(auto& item : *chain) { - item.lli = lli_pointer(&item + 1); - } - if( chain_type == ChainType::Loop ) { - chain[chain->size() - 1].lli = lli_pointer(&chain[0]); - } else { - chain[chain->size() - 1].lli = lli_pointer(nullptr); - } - } - - gpdma::channel::LLIPointer lli_pointer(const void* lli) { - return { - .lm = 0, - .r = 0, - .lli = reinterpret_cast(lli), - }; - } + public: + using chain_t = std::vector; + using chain_p = std::unique_ptr; + + Chain(const ChannelConfig& cc) + : chain(std::make_unique(cc.chain.length)) { + set_lli_sequential(cc.chain_type); + set_source_address()... + } + + private: + chain_p chain; + + void set_source_peripheral(void* const address) { + set_source_address(address, 0); + } + + void set_destination_peripheral(void* const address) { + set_destination_address(address, 0); + } + + void set_source_address(void* const address, const size_t increment) { + size_t offset = 0; + for (auto& item : *chain) { + item.srcaddr = (uint32_t)address + offset; + offset += increment; + } + } + + void set_destination_address(void* const address, const size_t increment) { + size_t offset = 0; + for (auto& item : *chain) { + item.destaddr = (uint32_t)address + offset; + offset += increment; + } + } + + void set_control(const gpdma::channel::Control control) { + for (auto& item : *chain) { + item.control = control; + } + } + + void set_lli_sequential(ChainType chain_type) { + for (auto& item : *chain) { + item.lli = lli_pointer(&item + 1); + } + if (chain_type == ChainType::Loop) { + chain[chain->size() - 1].lli = lli_pointer(&chain[0]); + } else { + chain[chain->size() - 1].lli = lli_pointer(nullptr); + } + } + + gpdma::channel::LLIPointer lli_pointer(const void* lli) { + return { + .lm = 0, + .r = 0, + .lli = reinterpret_cast(lli), + }; + } }; } /* namespace lli */ diff --git a/firmware/baseband/halconf.h b/firmware/baseband/halconf.h index 3f24c6999..089f95715 100755 --- a/firmware/baseband/halconf.h +++ b/firmware/baseband/halconf.h @@ -35,84 +35,84 @@ * @brief Enables the TM subsystem. */ #if !defined(HAL_USE_TM) || defined(__DOXYGEN__) -#define HAL_USE_TM FALSE +#define HAL_USE_TM FALSE #endif /** * @brief Enables the PAL subsystem. */ #if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) -#define HAL_USE_PAL FALSE +#define HAL_USE_PAL FALSE #endif /** * @brief Enables the ADC subsystem. */ #if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) -#define HAL_USE_ADC FALSE +#define HAL_USE_ADC FALSE #endif /** * @brief Enables the CAN subsystem. */ #if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) -#define HAL_USE_CAN FALSE +#define HAL_USE_CAN FALSE #endif /** * @brief Enables the EXT subsystem. */ #if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) -#define HAL_USE_EXT FALSE +#define HAL_USE_EXT FALSE #endif /** * @brief Enables the GPT subsystem. */ #if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) -#define HAL_USE_GPT FALSE +#define HAL_USE_GPT FALSE #endif /** * @brief Enables the I2C subsystem. */ #if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) -#define HAL_USE_I2C FALSE +#define HAL_USE_I2C FALSE #endif /** * @brief Enables the ICU subsystem. */ #if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) -#define HAL_USE_ICU FALSE +#define HAL_USE_ICU FALSE #endif /** * @brief Enables the MAC subsystem. */ #if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) -#define HAL_USE_MAC FALSE +#define HAL_USE_MAC FALSE #endif /** * @brief Enables the MMC_SPI subsystem. */ #if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) -#define HAL_USE_MMC_SPI FALSE +#define HAL_USE_MMC_SPI FALSE #endif /** * @brief Enables the PWM subsystem. */ #if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) -#define HAL_USE_PWM FALSE +#define HAL_USE_PWM FALSE #endif /** * @brief Enables the RTC subsystem. */ #if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) -#define HAL_USE_RTC FALSE +#define HAL_USE_RTC FALSE #endif /** @@ -120,9 +120,9 @@ */ #if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) #if defined(BASEBAND_flash_utility) || defined(BASEBAND_sd_over_usb) -#define HAL_USE_SDC TRUE +#define HAL_USE_SDC TRUE #else -#define HAL_USE_SDC FALSE +#define HAL_USE_SDC FALSE #endif /* BASEBAND_flash_utility */ #endif @@ -130,35 +130,35 @@ * @brief Enables the SERIAL subsystem. */ #if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) -#define HAL_USE_SERIAL FALSE +#define HAL_USE_SERIAL FALSE #endif /** * @brief Enables the SERIAL over USB subsystem. */ #if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) -#define HAL_USE_SERIAL_USB FALSE +#define HAL_USE_SERIAL_USB FALSE #endif /** * @brief Enables the SPI subsystem. */ #if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) -#define HAL_USE_SPI FALSE +#define HAL_USE_SPI FALSE #endif /** * @brief Enables the UART subsystem. */ #if !defined(HAL_USE_UART) || defined(__DOXYGEN__) -#define HAL_USE_UART FALSE +#define HAL_USE_UART FALSE #endif /** * @brief Enables the USB subsystem. */ #if !defined(HAL_USE_USB) || defined(__DOXYGEN__) -#define HAL_USE_USB FALSE +#define HAL_USE_USB FALSE #endif /*===========================================================================*/ @@ -170,7 +170,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) -#define ADC_USE_WAIT TRUE +#define ADC_USE_WAIT TRUE #endif /** @@ -178,7 +178,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define ADC_USE_MUTUAL_EXCLUSION TRUE +#define ADC_USE_MUTUAL_EXCLUSION TRUE #endif /*===========================================================================*/ @@ -189,7 +189,7 @@ * @brief Sleep mode related APIs inclusion switch. */ #if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) -#define CAN_USE_SLEEP_MODE TRUE +#define CAN_USE_SLEEP_MODE TRUE #endif /*===========================================================================*/ @@ -200,7 +200,7 @@ * @brief Enables the mutual exclusion APIs on the I2C bus. */ #if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define I2C_USE_MUTUAL_EXCLUSION TRUE +#define I2C_USE_MUTUAL_EXCLUSION TRUE #endif /*===========================================================================*/ @@ -211,14 +211,14 @@ * @brief Enables an event sources for incoming packets. */ #if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) -#define MAC_USE_ZERO_COPY FALSE +#define MAC_USE_ZERO_COPY FALSE #endif /** * @brief Enables an event sources for incoming packets. */ #if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) -#define MAC_USE_EVENTS TRUE +#define MAC_USE_EVENTS TRUE #endif /*===========================================================================*/ @@ -234,7 +234,7 @@ * use a DMA channel and heavily loads the CPU. */ #if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) -#define MMC_NICE_WAITING TRUE +#define MMC_NICE_WAITING TRUE #endif /*===========================================================================*/ @@ -246,7 +246,7 @@ * @note Attempts are performed at 10mS intervals. */ #if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) -#define SDC_INIT_RETRY 100 +#define SDC_INIT_RETRY 100 #endif /** @@ -255,7 +255,7 @@ * at @p FALSE. */ #if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) -#define SDC_MMC_SUPPORT FALSE +#define SDC_MMC_SUPPORT FALSE #endif /** @@ -265,7 +265,7 @@ * lower priority, this may slow down the driver a bit however. */ #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) -#define SDC_NICE_WAITING FALSE +#define SDC_NICE_WAITING FALSE #endif /*===========================================================================*/ @@ -278,7 +278,7 @@ * default configuration. */ #if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) -#define SERIAL_DEFAULT_BITRATE 38400 +#define SERIAL_DEFAULT_BITRATE 38400 #endif /** @@ -289,7 +289,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 16 #endif /*===========================================================================*/ @@ -301,7 +301,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) -#define SPI_USE_WAIT TRUE +#define SPI_USE_WAIT TRUE #endif /** @@ -309,7 +309,7 @@ * @note Disabling this option saves both code and data space. */ #if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define SPI_USE_MUTUAL_EXCLUSION TRUE +#define SPI_USE_MUTUAL_EXCLUSION TRUE #endif #endif /* _HALCONF_H_ */ diff --git a/firmware/baseband/linear_resampler.hpp b/firmware/baseband/linear_resampler.hpp index d889a2c25..1dd93b9f5 100644 --- a/firmware/baseband/linear_resampler.hpp +++ b/firmware/baseband/linear_resampler.hpp @@ -26,44 +26,42 @@ namespace dsp { namespace interpolation { class LinearResampler { -public: - void configure( - const float input_rate, - const float output_rate - ) { - phase_increment = calculate_increment(input_rate, output_rate); - } + public: + void configure( + const float input_rate, + const float output_rate) { + phase_increment = calculate_increment(input_rate, output_rate); + } - template - void operator()( - const float sample, - InterpolatedSampleHandler interpolated_sample_handler - ) { - const float sample_delta = sample - last_sample; - while( phase < 1.0f ) { - const float interpolated_value = last_sample + phase * sample_delta; - interpolated_sample_handler(interpolated_value); - phase += phase_increment; - } - last_sample = sample; - phase -= 1.0f; - } + template + void operator()( + const float sample, + InterpolatedSampleHandler interpolated_sample_handler) { + const float sample_delta = sample - last_sample; + while (phase < 1.0f) { + const float interpolated_value = last_sample + phase * sample_delta; + interpolated_sample_handler(interpolated_value); + phase += phase_increment; + } + last_sample = sample; + phase -= 1.0f; + } - void advance(const float fraction) { - phase += (fraction * phase_increment); - } + void advance(const float fraction) { + phase += (fraction * phase_increment); + } -private: - float last_sample { 0.0f }; - float phase { 0.0f }; - float phase_increment { 0.0f }; + private: + float last_sample{0.0f}; + float phase{0.0f}; + float phase_increment{0.0f}; - static constexpr float calculate_increment(const float input_rate, const float output_rate) { - return input_rate / output_rate; - } + static constexpr float calculate_increment(const float input_rate, const float output_rate) { + return input_rate / output_rate; + } }; } /* namespace interpolation */ } /* namespace dsp */ -#endif/*__LINEAR_RESAMPLER_H__*/ +#endif /*__LINEAR_RESAMPLER_H__*/ diff --git a/firmware/baseband/main.cpp b/firmware/baseband/main.cpp index 5f8a7da0f..f03644489 100755 --- a/firmware/baseband/main.cpp +++ b/firmware/baseband/main.cpp @@ -53,69 +53,68 @@ extern "C" { void __late_init(void) { - /* - * System initializations. - * - HAL initialization, this also initializes the configured device drivers - * and performs the board-specific initializations. - * - Kernel initialization, the main() function becomes a thread and the - * RTOS is active. - */ - halInit(); - - /* After this call, scheduler, systick, heap, etc. are available. */ - /* By doing chSysInit() here, it runs before C++ constructors, which may - * require the heap. - */ - chSysInit(); + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + + /* After this call, scheduler, systick, heap, etc. are available. */ + /* By doing chSysInit() here, it runs before C++ constructors, which may + * require the heap. + */ + chSysInit(); } - } static void init() { - audio::dma::init(); - audio::dma::configure(); - audio::dma::enable(); + audio::dma::init(); + audio::dma::configure(); + audio::dma::enable(); - LPC_CREG->DMAMUX = portapack::gpdma_mux; - gpdma::controller.enable(); - nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); + LPC_CREG->DMAMUX = portapack::gpdma_mux; + gpdma::controller.enable(); + nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); - touch::dma::init(); - touch::dma::allocate(); - touch::dma::enable(); + touch::dma::init(); + touch::dma::allocate(); + touch::dma::enable(); } static void halt() { - port_disable(); - while(true) { - port_wait_for_interrupt(); - } + port_disable(); + while (true) { + port_wait_for_interrupt(); + } } static void shutdown() { - // TODO: Is this complete? - - nvicDisableVector(DMA_IRQn); - - chSysDisable(); + // TODO: Is this complete? + + nvicDisableVector(DMA_IRQn); + + chSysDisable(); - systick_stop(); + systick_stop(); - ShutdownMessage shutdown_message; - shared_memory.application_queue.push(shutdown_message); + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); - halt(); + halt(); } int main(void) { - init(); + init(); - /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ + /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ - EventDispatcher event_dispatcher; - event_dispatcher.run(); + EventDispatcher event_dispatcher; + event_dispatcher.run(); - shutdown(); + shutdown(); - return 0; + return 0; } diff --git a/firmware/baseband/matched_filter.cpp b/firmware/baseband/matched_filter.cpp index 9471a2029..2776072bd 100644 --- a/firmware/baseband/matched_filter.cpp +++ b/firmware/baseband/matched_filter.cpp @@ -30,77 +30,75 @@ namespace dsp { namespace matched_filter { void MatchedFilter::configure( - const tap_t* const taps, - const size_t taps_count, - const size_t decimation_factor -) { - samples_ = std::make_unique(taps_count); - taps_reversed_ = std::make_unique(taps_count); - taps_count_ = taps_count; - decimation_factor_ = decimation_factor; - output = 0; - std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); + const tap_t* const taps, + const size_t taps_count, + const size_t decimation_factor) { + samples_ = std::make_unique(taps_count); + taps_reversed_ = std::make_unique(taps_count); + taps_count_ = taps_count; + decimation_factor_ = decimation_factor; + output = 0; + std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); } bool MatchedFilter::execute_once( - const sample_t input -) { - samples_[taps_count_ - decimation_factor_ + decimation_phase] = input; + const sample_t input) { + samples_[taps_count_ - decimation_factor_ + decimation_phase] = input; - advance_decimation_phase(); - if( is_new_decimation_cycle() ) { - float sr_tr = 0.0f; - float si_tr = 0.0f; - float si_ti = 0.0f; - float sr_ti = 0.0f; - for(size_t n=0; n 0) { - *t++ = *s++; - *t++ = *s++; - *t++ = *s++; - *t++ = *s++; - shift_count--; - } + const sample_t* s = &samples_[decimation_factor_]; + sample_t* t = &samples_[0]; - shift_count = (taps_count_ - decimation_factor_) % unroll_factor; - while(shift_count > 0) { - *t++ = *s++; - shift_count--; - } + const size_t unroll_factor = 4; + size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor; + while (shift_count > 0) { + *t++ = *s++; + *t++ = *s++; + *t++ = *s++; + *t++ = *s++; + shift_count--; + } + + shift_count = (taps_count_ - decimation_factor_) % unroll_factor; + while (shift_count > 0) { + *t++ = *s++; + shift_count--; + } } } /* namespace matched_filter */ diff --git a/firmware/baseband/matched_filter.hpp b/firmware/baseband/matched_filter.hpp index a9c6f0994..4f0d93b81 100644 --- a/firmware/baseband/matched_filter.hpp +++ b/firmware/baseband/matched_filter.hpp @@ -35,62 +35,59 @@ namespace matched_filter { // a multiple of the complex sinusoid period. class MatchedFilter { -public: - using sample_t = std::complex; - using tap_t = std::complex; - - using taps_t = tap_t[]; - - template - MatchedFilter( - const T& taps, - size_t decimation_factor = 1 - ) { - configure(taps, decimation_factor); - } - - template - void configure( - const T& taps, - size_t decimation_factor - ) { - configure(taps.data(), taps.size(), decimation_factor); - } - - bool execute_once(const sample_t input); - - float get_output() const { - return output; - } - -private: - using samples_t = sample_t[]; - - std::unique_ptr samples_ { }; - std::unique_ptr taps_reversed_ { }; - size_t taps_count_ { 0 }; - size_t decimation_factor_ { 1 }; - size_t decimation_phase { 0 }; - float output { 0 }; - - void shift_by_decimation_factor(); - - void advance_decimation_phase() { - decimation_phase = (decimation_phase + 1) % decimation_factor_; - } - - bool is_new_decimation_cycle() const { - return (decimation_phase == 0); - } - - void configure( - const tap_t* const taps, - const size_t taps_count, - const size_t decimation_factor - ); + public: + using sample_t = std::complex; + using tap_t = std::complex; + + using taps_t = tap_t[]; + + template + MatchedFilter( + const T& taps, + size_t decimation_factor = 1) { + configure(taps, decimation_factor); + } + + template + void configure( + const T& taps, + size_t decimation_factor) { + configure(taps.data(), taps.size(), decimation_factor); + } + + bool execute_once(const sample_t input); + + float get_output() const { + return output; + } + + private: + using samples_t = sample_t[]; + + std::unique_ptr samples_{}; + std::unique_ptr taps_reversed_{}; + size_t taps_count_{0}; + size_t decimation_factor_{1}; + size_t decimation_phase{0}; + float output{0}; + + void shift_by_decimation_factor(); + + void advance_decimation_phase() { + decimation_phase = (decimation_phase + 1) % decimation_factor_; + } + + bool is_new_decimation_cycle() const { + return (decimation_phase == 0); + } + + void configure( + const tap_t* const taps, + const size_t taps_count, + const size_t decimation_factor); }; } /* namespace matched_filter */ } /* namespace dsp */ -#endif/*__MATCHED_FILTER_H__*/ +#endif /*__MATCHED_FILTER_H__*/ diff --git a/firmware/baseband/mcuconf.h b/firmware/baseband/mcuconf.h index bc4d61da3..10d51a702 100755 --- a/firmware/baseband/mcuconf.h +++ b/firmware/baseband/mcuconf.h @@ -37,12 +37,12 @@ */ //#define LPC_ADC0_IRQ_PRIORITY 2 -#define LPC_DMA_IRQ_PRIORITY 3 +#define LPC_DMA_IRQ_PRIORITY 3 //#define LPC_ADC1_IRQ_PRIORITY 4 -#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4 +#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4 /* M4 is initialized by M0, which has already started PLL1 */ #if !defined(LPC43XX_M4_CLK) || defined(__DOXYGEN__) -#define LPC43XX_M4_CLK 200000000 +#define LPC43XX_M4_CLK 200000000 #endif diff --git a/firmware/baseband/ook.hpp b/firmware/baseband/ook.hpp index 04324fe65..6430401d2 100644 --- a/firmware/baseband/ook.hpp +++ b/firmware/baseband/ook.hpp @@ -31,65 +31,61 @@ #include class OOKSlicerMagSquaredInt { -public: - using symbol_t = bool; + public: + using symbol_t = bool; - constexpr OOKSlicerMagSquaredInt( - const float samples_per_symbol - ) : mag2_threshold_leak_factor { - static_cast( - factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32) - ) - } - { - } + constexpr OOKSlicerMagSquaredInt( + const float samples_per_symbol) + : mag2_threshold_leak_factor{ + static_cast( + factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32))} { + } - symbol_t operator()(const std::complex in) { - const uint32_t real2 = in.real() * in.real(); - const uint32_t imag2 = in.imag() * in.imag(); - const uint32_t mag2 = real2 + imag2; + symbol_t operator()(const std::complex in) { + const uint32_t real2 = in.real() * in.real(); + const uint32_t imag2 = in.imag() * in.imag(); + const uint32_t mag2 = real2 + imag2; - const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2 - mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32; - mag2_threshold = std::max(mag2_threshold, mag2_attenuated); - const bool symbol = (mag2 > mag2_threshold); - return symbol; - } + const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2 + mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32; + mag2_threshold = std::max(mag2_threshold, mag2_attenuated); + const bool symbol = (mag2 > mag2_threshold); + return symbol; + } -private: - const uint32_t mag2_threshold_leak_factor; - uint32_t mag2_threshold = 0; + private: + const uint32_t mag2_threshold_leak_factor; + uint32_t mag2_threshold = 0; - constexpr float factor_sq(float db) { - return std::pow(10.0f, db / (10.0f / 2)); - } + constexpr float factor_sq(float db) { + return std::pow(10.0f, db / (10.0f / 2)); + } }; class OOKClockRecovery { -public: - constexpr OOKClockRecovery( - const float samples_per_symbol - ) : symbol_phase_inc_nominal { static_cast(std::round((1ULL << 32) / samples_per_symbol)) }, - symbol_phase_inc_k { static_cast(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol)) }, - phase_detector { static_cast(std::round(samples_per_symbol)) }, - phase_accumulator { symbol_phase_inc_nominal } - { - } + public: + constexpr OOKClockRecovery( + const float samples_per_symbol) + : symbol_phase_inc_nominal{static_cast(std::round((1ULL << 32) / samples_per_symbol))}, + symbol_phase_inc_k{static_cast(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol))}, + phase_detector{static_cast(std::round(samples_per_symbol))}, + phase_accumulator{symbol_phase_inc_nominal} { + } - template - void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) { - if( phase_accumulator() ) { - const auto detector_result = phase_detector(slicer_history); - phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k); - symbol_handler(detector_result.symbol); - } - } + template + void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) { + if (phase_accumulator()) { + const auto detector_result = phase_detector(slicer_history); + phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k); + symbol_handler(detector_result.symbol); + } + } -private: - const uint32_t symbol_phase_inc_nominal; - const uint32_t symbol_phase_inc_k; - PhaseDetectorEarlyLateGate phase_detector; - PhaseAccumulator phase_accumulator; + private: + const uint32_t symbol_phase_inc_nominal; + const uint32_t symbol_phase_inc_k; + PhaseDetectorEarlyLateGate phase_detector; + PhaseAccumulator phase_accumulator; }; -#endif/*__OOK_HPP__*/ +#endif /*__OOK_HPP__*/ diff --git a/firmware/baseband/packet_builder.hpp b/firmware/baseband/packet_builder.hpp index 6a24e8030..3648064fd 100644 --- a/firmware/baseband/packet_builder.hpp +++ b/firmware/baseband/packet_builder.hpp @@ -31,109 +31,106 @@ #include "baseband_packet.hpp" struct NeverMatch { - bool operator()(const BitHistory&, const size_t) const { - return false; - } + bool operator()(const BitHistory&, const size_t) const { + return false; + } }; struct FixedLength { - bool operator()(const BitHistory&, const size_t symbols_received) const { - return symbols_received >= length; - } + bool operator()(const BitHistory&, const size_t symbols_received) const { + return symbols_received >= length; + } - const size_t length; + const size_t length; }; -template +template class PacketBuilder { -public: - using PayloadHandlerFunc = std::function; - - PacketBuilder( - const PreambleMatcher preamble_matcher, - const UnstuffMatcher unstuff_matcher, - const EndMatcher end_matcher, - PayloadHandlerFunc payload_handler - ) : payload_handler { std::move(payload_handler) }, - preamble(preamble_matcher), - unstuff(unstuff_matcher), - end(end_matcher) - { - } - - void configure( - const PreambleMatcher preamble_matcher, - const UnstuffMatcher unstuff_matcher - ) { - preamble = preamble_matcher; - unstuff = unstuff_matcher; - - reset_state(); - } - - void execute( - const uint_fast8_t symbol - ) { - bit_history.add(symbol); - - switch(state) { - case State::Preamble: - if( preamble(bit_history, packet.size()) ) { - state = State::Payload; - } - break; - - case State::Payload: - if( !unstuff(bit_history, packet.size()) ) { - packet.add(symbol); - } - - if( end(bit_history, packet.size()) ) { - // NOTE: This check is to avoid std::function nullptr check, which - // brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code. - // TODO: Make payload_handler known at compile time. - if( payload_handler ) { - packet.set_timestamp(Timestamp::now()); - payload_handler(packet); - } - reset_state(); - } else { - if( packet_truncated() ) { - reset_state(); - } - } - break; - - default: - reset_state(); - break; - } - } - -private: - enum State { - Preamble, - Payload, - }; - - bool packet_truncated() const { - return packet.size() >= packet.capacity(); - } - - const PayloadHandlerFunc payload_handler; - - BitHistory bit_history { }; - PreambleMatcher preamble { }; - UnstuffMatcher unstuff { }; - EndMatcher end { }; - - State state { State::Preamble }; - baseband::Packet packet { }; - - void reset_state() { - packet.clear(); - state = State::Preamble; - } + public: + using PayloadHandlerFunc = std::function; + + PacketBuilder( + const PreambleMatcher preamble_matcher, + const UnstuffMatcher unstuff_matcher, + const EndMatcher end_matcher, + PayloadHandlerFunc payload_handler) + : payload_handler{std::move(payload_handler)}, + preamble(preamble_matcher), + unstuff(unstuff_matcher), + end(end_matcher) { + } + + void configure( + const PreambleMatcher preamble_matcher, + const UnstuffMatcher unstuff_matcher) { + preamble = preamble_matcher; + unstuff = unstuff_matcher; + + reset_state(); + } + + void execute( + const uint_fast8_t symbol) { + bit_history.add(symbol); + + switch (state) { + case State::Preamble: + if (preamble(bit_history, packet.size())) { + state = State::Payload; + } + break; + + case State::Payload: + if (!unstuff(bit_history, packet.size())) { + packet.add(symbol); + } + + if (end(bit_history, packet.size())) { + // NOTE: This check is to avoid std::function nullptr check, which + // brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code. + // TODO: Make payload_handler known at compile time. + if (payload_handler) { + packet.set_timestamp(Timestamp::now()); + payload_handler(packet); + } + reset_state(); + } else { + if (packet_truncated()) { + reset_state(); + } + } + break; + + default: + reset_state(); + break; + } + } + + private: + enum State { + Preamble, + Payload, + }; + + bool packet_truncated() const { + return packet.size() >= packet.capacity(); + } + + const PayloadHandlerFunc payload_handler; + + BitHistory bit_history{}; + PreambleMatcher preamble{}; + UnstuffMatcher unstuff{}; + EndMatcher end{}; + + State state{State::Preamble}; + baseband::Packet packet{}; + + void reset_state() { + packet.clear(); + state = State::Preamble; + } }; -#endif/*__PACKET_BUILDER_H__*/ +#endif /*__PACKET_BUILDER_H__*/ diff --git a/firmware/baseband/phase_accumulator.hpp b/firmware/baseband/phase_accumulator.hpp index bf3b331a3..fa85a13e7 100644 --- a/firmware/baseband/phase_accumulator.hpp +++ b/firmware/baseband/phase_accumulator.hpp @@ -25,26 +25,25 @@ #include class PhaseAccumulator { -public: - constexpr PhaseAccumulator( - const uint32_t phase_inc - ) : phase_inc { phase_inc } - { - } + public: + constexpr PhaseAccumulator( + const uint32_t phase_inc) + : phase_inc{phase_inc} { + } - bool operator()() { - const auto last_phase = phase; - phase += phase_inc; - return (phase < last_phase); - } + bool operator()() { + const auto last_phase = phase; + phase += phase_inc; + return (phase < last_phase); + } - void set_inc(const uint32_t new_phase_inc) { - phase_inc = new_phase_inc; - } + void set_inc(const uint32_t new_phase_inc) { + phase_inc = new_phase_inc; + } -private: - uint32_t phase { 0 }; - uint32_t phase_inc; + private: + uint32_t phase{0}; + uint32_t phase_inc; }; -#endif/*__PHASE_ACCUMULATOR_HPP__*/ +#endif /*__PHASE_ACCUMULATOR_HPP__*/ diff --git a/firmware/baseband/phase_detector.hpp b/firmware/baseband/phase_detector.hpp index 4e6f0d34f..000a3a348 100644 --- a/firmware/baseband/phase_detector.hpp +++ b/firmware/baseband/phase_detector.hpp @@ -27,44 +27,43 @@ #include class PhaseDetectorEarlyLateGate { -public: - using history_t = uint32_t; + public: + using history_t = uint32_t; - using symbol_t = bool; - using error_t = int; + using symbol_t = bool; + using error_t = int; - struct result_t { - symbol_t symbol; - error_t error; - }; + struct result_t { + symbol_t symbol; + error_t error; + }; - constexpr PhaseDetectorEarlyLateGate( - const size_t samples_per_symbol - ) : sample_threshold { samples_per_symbol / 2 }, - late_mask { (1UL << sample_threshold) - 1UL }, - early_mask { late_mask << sample_threshold } - { - } + constexpr PhaseDetectorEarlyLateGate( + const size_t samples_per_symbol) + : sample_threshold{samples_per_symbol / 2}, + late_mask{(1UL << sample_threshold) - 1UL}, + early_mask{late_mask << sample_threshold} { + } - result_t operator()(const history_t symbol_history) const { - static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch"); + result_t operator()(const history_t symbol_history) const { + static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch"); - // history = ...0111, early - // history = ...1110, late + // history = ...0111, early + // history = ...1110, late - const size_t late_side = __builtin_popcountl(symbol_history & late_mask); - const size_t early_side = __builtin_popcountl(symbol_history & early_mask); - const size_t total_count = late_side + early_side; - const auto lateness = static_cast(late_side) - static_cast(early_side); - const symbol_t symbol = (total_count >= sample_threshold); - const error_t error = symbol ? -lateness : lateness; - return { symbol, error }; - } + const size_t late_side = __builtin_popcountl(symbol_history & late_mask); + const size_t early_side = __builtin_popcountl(symbol_history & early_mask); + const size_t total_count = late_side + early_side; + const auto lateness = static_cast(late_side) - static_cast(early_side); + const symbol_t symbol = (total_count >= sample_threshold); + const error_t error = symbol ? -lateness : lateness; + return {symbol, error}; + } -private: - const size_t sample_threshold; - const history_t late_mask; - const history_t early_mask; + private: + const size_t sample_threshold; + const history_t late_mask; + const history_t early_mask; }; -#endif/*__PHASE_DETECTOR_HPP__*/ +#endif /*__PHASE_DETECTOR_HPP__*/ diff --git a/firmware/baseband/proc_acars.cpp b/firmware/baseband/proc_acars.cpp index 176e428fb..3c270442a 100644 --- a/firmware/baseband/proc_acars.cpp +++ b/firmware/baseband/proc_acars.cpp @@ -29,53 +29,51 @@ #include "event_m4.hpp" ACARSProcessor::ACARSProcessor() { - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); - packet.clear(); + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); + packet.clear(); } void ACARSProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ + /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto decimator_out = decim_1_out; + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decimator_out = decim_1_out; - /* 38.4kHz, 32 samples */ - feed_channel_stats(decimator_out); + /* 38.4kHz, 32 samples */ + feed_channel_stats(decimator_out); - for(size_t i=0; i= 0.0f) ? 1 : 0; - //const auto decoded_symbol = acars_decode(sliced_symbol); + const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + // const auto decoded_symbol = acars_decode(sliced_symbol); - // DEBUG - packet.add(sliced_symbol); - if (packet.size() == 256) { - payload_handler(packet); - packet.clear(); - } + // DEBUG + packet.add(sliced_symbol); + if (packet.size() == 256) { + payload_handler(packet); + packet.clear(); + } - //packet_builder.execute(decoded_symbol); + // packet_builder.execute(decoded_symbol); } void ACARSProcessor::payload_handler( - const baseband::Packet& packet -) { - const ACARSPacketMessage message { packet }; - shared_memory.application_queue.push(message); + const baseband::Packet& packet) { + const ACARSPacketMessage message{packet}; + shared_memory.application_queue.push(message); } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_acars.hpp b/firmware/baseband/proc_acars.hpp index 69d909706..6e9d0c27e 100644 --- a/firmware/baseband/proc_acars.hpp +++ b/firmware/baseband/proc_acars.hpp @@ -76,63 +76,70 @@ // 16 taps, 1 symbol, 2 cycles // Number of taps: size of one symbol in samples (in/symbol) -// Cycles: - +// Cycles: // Translate+rectangular filter // sample=38.4k, deviation=4800, symbol=2400 // Length: 16 taps, 1 symbol, 2 cycles of sinusoid // This is actually the same as rect_taps_307k2_38k4_1t_19k2_p -constexpr std::array, 16> rect_taps_38k4_4k8_1t_2k4_p { { - { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, - { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, - { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, - { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, - { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, - { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, - { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, - { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, -} }; +constexpr std::array, 16> rect_taps_38k4_4k8_1t_2k4_p{{ + {6.2500000000e-02f, 0.0000000000e+00f}, + {4.4194173824e-02f, 4.4194173824e-02f}, + {0.0000000000e+00f, 6.2500000000e-02f}, + {-4.4194173824e-02f, 4.4194173824e-02f}, + {-6.2500000000e-02f, 0.0000000000e+00f}, + {-4.4194173824e-02f, -4.4194173824e-02f}, + {0.0000000000e+00f, -6.2500000000e-02f}, + {4.4194173824e-02f, -4.4194173824e-02f}, + {6.2500000000e-02f, 0.0000000000e+00f}, + {4.4194173824e-02f, 4.4194173824e-02f}, + {0.0000000000e+00f, 6.2500000000e-02f}, + {-4.4194173824e-02f, 4.4194173824e-02f}, + {-6.2500000000e-02f, 0.0000000000e+00f}, + {-4.4194173824e-02f, -4.4194173824e-02f}, + {0.0000000000e+00f, -6.2500000000e-02f}, + {4.4194173824e-02f, -4.4194173824e-02f}, +}}; class ACARSProcessor : public BasebandProcessor { -public: - ACARSProcessor(); - - void execute(const buffer_c8_t& buffer) override; - -private: - static constexpr size_t baseband_fs = 2457600; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; // Translate already done here ! - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::matched_filter::MatchedFilter mf { rect_taps_38k4_4k8_1t_2k4_p, 8 }; - - clock_recovery::ClockRecovery clock_recovery { - 4800, 2400, { 0.0555f }, - [this](const float symbol) { this->consume_symbol(symbol); } - }; - symbol_coding::ACARSDecoder acars_decode { }; - /*PacketBuilder packet_builder { - { 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH - { }, - { 128 }, - [this](const baseband::Packet& packet) { - this->payload_handler(packet); - } - };*/ - baseband::Packet packet { }; - - void consume_symbol(const float symbol); - void payload_handler(const baseband::Packet& packet); + public: + ACARSProcessor(); + + void execute(const buffer_c8_t& buffer) override; + + private: + static constexpr size_t baseband_fs = 2457600; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; // Translate already done here ! + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::matched_filter::MatchedFilter mf{rect_taps_38k4_4k8_1t_2k4_p, 8}; + + clock_recovery::ClockRecovery clock_recovery{ + 4800, + 2400, + {0.0555f}, + [this](const float symbol) { this->consume_symbol(symbol); }}; + symbol_coding::ACARSDecoder acars_decode{}; + /*PacketBuilder packet_builder { + { 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH + { }, + { 128 }, + [this](const baseband::Packet& packet) { + this->payload_handler(packet); + } + };*/ + baseband::Packet packet{}; + + void consume_symbol(const float symbol); + void payload_handler(const baseband::Packet& packet); }; -#endif/*__PROC_ACARS_H__*/ +#endif /*__PROC_ACARS_H__*/ diff --git a/firmware/baseband/proc_adsbrx.cpp b/firmware/baseband/proc_adsbrx.cpp index f52d51013..d097086c6 100644 --- a/firmware/baseband/proc_adsbrx.cpp +++ b/firmware/baseband/proc_adsbrx.cpp @@ -29,138 +29,133 @@ #include using namespace adsb; - + void ADSBRXProcessor::execute(const buffer_c8_t& buffer) { - int8_t re, im; - uint32_t mag; - uint32_t c; - uint8_t bit, byte{}; - - // This is called at 2M/2048 = 977Hz - // One pulse = 500ns = 2 samples - // One bit = 2 pulses = 1us = 4 samples - - if (!configured) return; - - for (size_t i = 0; i < buffer.count; i++) { - - // Compute sample's magnitude - re = (int32_t)buffer.p[i].real(); - im = (int32_t)buffer.p[i].imag(); - mag = ((uint32_t)(re*re) + (uint32_t)(im*im)); - - if (decoding) { - // Decode - - // 1 bit lasts 2 samples - if (sample_count & 1) { - if (bit_count >= msgLen) - { - const ADSBFrameMessage message(frame, amp); - shared_memory.application_queue.push(message); - decoding = false; - bit = (prev_mag > mag) ? 1 : 0; - } - else - { - bit = (prev_mag > mag) ? 1 : 0; - } - - byte = bit | (byte << 1); - bit_count++; - - // Perform checks at the end of the first byte - if (!(bit_count & 7)) { - - // Store the byte - frame.push_byte(byte); - - // Check at the end of the first byte of the message - uint8_t df = (byte >> 3); - if ( (bit_count == 8) && !(df & 0x10) ) { - msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56. - } - - // Abondon all frames that arent DF17 or DF18 extended squitters - if ( (bit_count == 8) && !((df == 17)||(df == 18)) ) { - decoding = false; - bit = (prev_mag > mag) ? 1 : 0; - frame.clear(); - } - } // last bit of a byte - } // Second sample of each bit - sample_count++; - } - - // Continue looking for preamble even if in a packet - // switch if new preamble is higher magnitude - - // Shift the preamble - for (c = 0; c < (ADSB_PREAMBLE_LENGTH ); c++) { shifter[c] = shifter[c + 1]; } - shifter[ADSB_PREAMBLE_LENGTH] = mag; - - // First check of relations between the first 10 samples - // representing a valid preamble. We don't even investigate further - // if this simple test is not passed - if (shifter[0] < shifter[1] && - shifter[1] > shifter[2] && - shifter[2] < shifter[3] && - shifter[3] > shifter[4] && - shifter[4] < shifter[1] && - shifter[5] < shifter[1] && - shifter[6] < shifter[1] && - shifter[7] < shifter[1] && - shifter[8] > shifter[9] && - shifter[9] < shifter[10] && - shifter[10]> shifter[11] ) - { - // The samples between the two spikes must be < than the average - // of the high spikes level. We don't test bits too near to - // the high levels as signals can be out of phase so part of the - // energy can be in the near samples - int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]); - uint32_t high = thisAmp / 9; - if ( - shifter[5] < high && - shifter[6] < high && - // Similarly samples in the range 11-13 must be low, as it is the - // space between the preamble and real data. Again we don't test - // bits too near to high levels, see above - shifter[12] < high && - shifter[13] < high && - shifter[14] < high ) - { - if ((decoding == false) || // New preamble - ((decoding == true) && (thisAmp > amp))) // Higher power than existing packet - { - decoding = true; - msgLen = 112; - amp = thisAmp; - sample_count = 0; - bit_count = 0; - frame.clear(); - } - } // 4 & 5 low and 11-14 low - } // Check for preamble pattern - - // Store mag for next time - prev_mag = mag; - } + int8_t re, im; + uint32_t mag; + uint32_t c; + uint8_t bit, byte{}; + + // This is called at 2M/2048 = 977Hz + // One pulse = 500ns = 2 samples + // One bit = 2 pulses = 1us = 4 samples + + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + // Compute sample's magnitude + re = (int32_t)buffer.p[i].real(); + im = (int32_t)buffer.p[i].imag(); + mag = ((uint32_t)(re * re) + (uint32_t)(im * im)); + + if (decoding) { + // Decode + + // 1 bit lasts 2 samples + if (sample_count & 1) { + if (bit_count >= msgLen) { + const ADSBFrameMessage message(frame, amp); + shared_memory.application_queue.push(message); + decoding = false; + bit = (prev_mag > mag) ? 1 : 0; + } else { + bit = (prev_mag > mag) ? 1 : 0; + } + + byte = bit | (byte << 1); + bit_count++; + + // Perform checks at the end of the first byte + if (!(bit_count & 7)) { + // Store the byte + frame.push_byte(byte); + + // Check at the end of the first byte of the message + uint8_t df = (byte >> 3); + if ((bit_count == 8) && !(df & 0x10)) { + msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56. + } + + // Abondon all frames that arent DF17 or DF18 extended squitters + if ((bit_count == 8) && !((df == 17) || (df == 18))) { + decoding = false; + bit = (prev_mag > mag) ? 1 : 0; + frame.clear(); + } + } // last bit of a byte + } // Second sample of each bit + sample_count++; + } + + // Continue looking for preamble even if in a packet + // switch if new preamble is higher magnitude + + // Shift the preamble + for (c = 0; c < (ADSB_PREAMBLE_LENGTH); c++) { + shifter[c] = shifter[c + 1]; + } + shifter[ADSB_PREAMBLE_LENGTH] = mag; + + // First check of relations between the first 10 samples + // representing a valid preamble. We don't even investigate further + // if this simple test is not passed + if (shifter[0] < shifter[1] && + shifter[1] > shifter[2] && + shifter[2] < shifter[3] && + shifter[3] > shifter[4] && + shifter[4] < shifter[1] && + shifter[5] < shifter[1] && + shifter[6] < shifter[1] && + shifter[7] < shifter[1] && + shifter[8] > shifter[9] && + shifter[9] < shifter[10] && + shifter[10] > shifter[11]) { + // The samples between the two spikes must be < than the average + // of the high spikes level. We don't test bits too near to + // the high levels as signals can be out of phase so part of the + // energy can be in the near samples + int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]); + uint32_t high = thisAmp / 9; + if ( + shifter[5] < high && + shifter[6] < high && + // Similarly samples in the range 11-13 must be low, as it is the + // space between the preamble and real data. Again we don't test + // bits too near to high levels, see above + shifter[12] < high && + shifter[13] < high && + shifter[14] < high) { + if ((decoding == false) || // New preamble + ((decoding == true) && (thisAmp > amp))) // Higher power than existing packet + { + decoding = true; + msgLen = 112; + amp = thisAmp; + sample_count = 0; + bit_count = 0; + frame.clear(); + } + } // 4 & 5 low and 11-14 low + } // Check for preamble pattern + + // Store mag for next time + prev_mag = mag; + } } void ADSBRXProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::ADSBConfigure) { - bit_count = 0; - sample_count = 0; - decoding = false; - configured = true; - } + if (message->id == Message::ID::ADSBConfigure) { + bit_count = 0; + sample_count = 0; + decoding = false; + configured = true; + } } #ifndef _WIN32 int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } #endif diff --git a/firmware/baseband/proc_adsbrx.hpp b/firmware/baseband/proc_adsbrx.hpp index 5f4cd42ac..a9fcd7bf4 100644 --- a/firmware/baseband/proc_adsbrx.hpp +++ b/firmware/baseband/proc_adsbrx.hpp @@ -34,30 +34,30 @@ using namespace adsb; #define ADSB_PREAMBLE_LENGTH 16 class ADSBRXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 2000000; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - ADSBFrame frame { }; - bool configured { false }; - uint32_t prev_mag { 0 }; - size_t bit_count { 0 }, sample_count { 0 }; - size_t msgLen{ 112 }; - uint32_t shifter[ADSB_PREAMBLE_LENGTH+1]; - bool decoding { }; - bool preamble { }, active { }; - uint16_t bit_pos { 0 }; - uint8_t cur_bit { 0 }; - uint32_t sample { 0 }; - int32_t re { }, im { }; - int32_t amp {0}; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 2000000; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + ADSBFrame frame{}; + bool configured{false}; + uint32_t prev_mag{0}; + size_t bit_count{0}, sample_count{0}; + size_t msgLen{112}; + uint32_t shifter[ADSB_PREAMBLE_LENGTH + 1]; + bool decoding{}; + bool preamble{}, active{}; + uint16_t bit_pos{0}; + uint8_t cur_bit{0}; + uint32_t sample{0}; + int32_t re{}, im{}; + int32_t amp{0}; }; #endif diff --git a/firmware/baseband/proc_adsbtx.cpp b/firmware/baseband/proc_adsbtx.cpp index 52fc0d779..98ee743a7 100644 --- a/firmware/baseband/proc_adsbtx.cpp +++ b/firmware/baseband/proc_adsbtx.cpp @@ -28,46 +28,45 @@ #include void ADSBTXProcessor::execute(const buffer_c8_t& buffer) { - - // This is called at 4M/2048 = 1953Hz - // One pulse = 500ns = 2 samples - // One bit = 2 pulses = 1us = 4 samples - // Test this with ./dump1090 --freq 434000000 --gain 20 - // Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast - - if (!configured) return; + // This is called at 4M/2048 = 1953Hz + // One pulse = 500ns = 2 samples + // One bit = 2 pulses = 1us = 4 samples + // Test this with ./dump1090 --freq 434000000 --gain 20 + // Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast - for (size_t i = 0; i < buffer.count; i++) { - if (bit_pos >= (240 << 1)) { - configured = false; - cur_bit = 0; - } else { - cur_bit = shared_memory.bb_data.data[bit_pos >> 1]; - bit_pos++; - } - - if (cur_bit) { - // Crude AM - buffer.p[i] = am_lut[phase & 3]; - phase++; - } else { - buffer.p[i] = { 0, 0 }; - } - } + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + if (bit_pos >= (240 << 1)) { + configured = false; + cur_bit = 0; + } else { + cur_bit = shared_memory.bb_data.data[bit_pos >> 1]; + bit_pos++; + } + + if (cur_bit) { + // Crude AM + buffer.p[i] = am_lut[phase & 3]; + phase++; + } else { + buffer.p[i] = {0, 0}; + } + } } void ADSBTXProcessor::on_message(const Message* const p) { - const auto message = *reinterpret_cast(p); - - if (message.id == Message::ID::ADSBConfigure) { - bit_pos = 0; - phase = 0; - configured = true; - } + const auto message = *reinterpret_cast(p); + + if (message.id == Message::ID::ADSBConfigure) { + bit_pos = 0; + phase = 0; + configured = true; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_adsbtx.hpp b/firmware/baseband/proc_adsbtx.hpp index a92f05a64..0884bebab 100644 --- a/firmware/baseband/proc_adsbtx.hpp +++ b/firmware/baseband/proc_adsbtx.hpp @@ -27,28 +27,27 @@ #include "baseband_thread.hpp" class ADSBTXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const p) override; - -private: - bool configured = false; - - BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - const complex8_t am_lut[4] = { - { 127, 0 }, - { 0, 127 }, - { -127, 0 }, - { 0, -127 } - }; - - uint32_t bit_pos { 0 }; - uint32_t cur_bit { 0 }; - uint32_t phase { 0 }; - - TXProgressMessage txprogress_message { }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const p) override; + + private: + bool configured = false; + + BasebandThread baseband_thread{4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + const complex8_t am_lut[4] = { + {127, 0}, + {0, 127}, + {-127, 0}, + {0, -127}}; + + uint32_t bit_pos{0}; + uint32_t cur_bit{0}; + uint32_t phase{0}; + + TXProgressMessage txprogress_message{}; }; #endif diff --git a/firmware/baseband/proc_afsk.cpp b/firmware/baseband/proc_afsk.cpp index 081087a5b..c6fee3981 100644 --- a/firmware/baseband/proc_afsk.cpp +++ b/firmware/baseband/proc_afsk.cpp @@ -28,97 +28,95 @@ #include void AFSKProcessor::execute(const buffer_c8_t& buffer) { - - // This is called at 2.28M/2048 = 1113Hz - - if (!configured) return; - - for (size_t i = 0; i= afsk_samples_per_bit) { - if (configured) { - cur_word = *word_ptr; - - if (!cur_word) { - // End of data - if (repeat_counter < afsk_repeat) { - // Repeat - bit_pos = 0; - word_ptr = (uint16_t*)shared_memory.bb_data.data; - cur_word = *word_ptr; - txprogress_message.done = false; - txprogress_message.progress = repeat_counter + 1; - shared_memory.application_queue.push(txprogress_message); - repeat_counter++; - } else { - // Stop - cur_word = 0; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - configured = false; - } - } - } - - cur_bit = (cur_word >> (symbol_count - bit_pos)) & 1; - - if (bit_pos >= symbol_count) { - bit_pos = 0; - word_ptr++; - } else { - bit_pos++; - } - - sample_count = 0; - } else { - sample_count++; - } - - if (cur_bit) - tone_phase += afsk_phase_inc_mark; - else - tone_phase += afsk_phase_inc_space; - - tone_sample = sine_table_i8[(tone_phase & 0xFF000000U) >> 24]; - - delta = tone_sample * fm_delta; - - phase += delta; - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); - - buffer.p[i] = {re, im}; - } + // This is called at 2.28M/2048 = 1113Hz + + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + if (sample_count >= afsk_samples_per_bit) { + if (configured) { + cur_word = *word_ptr; + + if (!cur_word) { + // End of data + if (repeat_counter < afsk_repeat) { + // Repeat + bit_pos = 0; + word_ptr = (uint16_t*)shared_memory.bb_data.data; + cur_word = *word_ptr; + txprogress_message.done = false; + txprogress_message.progress = repeat_counter + 1; + shared_memory.application_queue.push(txprogress_message); + repeat_counter++; + } else { + // Stop + cur_word = 0; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + configured = false; + } + } + } + + cur_bit = (cur_word >> (symbol_count - bit_pos)) & 1; + + if (bit_pos >= symbol_count) { + bit_pos = 0; + word_ptr++; + } else { + bit_pos++; + } + + sample_count = 0; + } else { + sample_count++; + } + + if (cur_bit) + tone_phase += afsk_phase_inc_mark; + else + tone_phase += afsk_phase_inc_space; + + tone_sample = sine_table_i8[(tone_phase & 0xFF000000U) >> 24]; + + delta = tone_sample * fm_delta; + + phase += delta; + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); + + buffer.p[i] = {re, im}; + } } void AFSKProcessor::on_message(const Message* const msg) { - const auto message = *reinterpret_cast(msg); - - if (message.id == Message::ID::AFSKTxConfigure) { - if (message.samples_per_bit) { - afsk_samples_per_bit = message.samples_per_bit; - afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF; - afsk_phase_inc_space = message.phase_inc_space * AFSK_DELTA_COEF; - afsk_repeat = message.repeat - 1; - fm_delta = message.fm_delta * (0xFFFFFFULL / AFSK_SAMPLERATE); - symbol_count = message.symbol_count - 1; - - sample_count = afsk_samples_per_bit; - repeat_counter = 0; - bit_pos = 0; - word_ptr = (uint16_t*)shared_memory.bb_data.data; - cur_word = 0; - cur_bit = 0; - configured = true; - } else - configured = false; // Kill - } + const auto message = *reinterpret_cast(msg); + + if (message.id == Message::ID::AFSKTxConfigure) { + if (message.samples_per_bit) { + afsk_samples_per_bit = message.samples_per_bit; + afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF; + afsk_phase_inc_space = message.phase_inc_space * AFSK_DELTA_COEF; + afsk_repeat = message.repeat - 1; + fm_delta = message.fm_delta * (0xFFFFFFULL / AFSK_SAMPLERATE); + symbol_count = message.symbol_count - 1; + + sample_count = afsk_samples_per_bit; + repeat_counter = 0; + bit_pos = 0; + word_ptr = (uint16_t*)shared_memory.bb_data.data; + cur_word = 0; + cur_bit = 0; + configured = true; + } else + configured = false; // Kill + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_afsk.hpp b/firmware/baseband/proc_afsk.hpp index 2085022c4..5c065ed6b 100644 --- a/firmware/baseband/proc_afsk.hpp +++ b/firmware/baseband/proc_afsk.hpp @@ -30,35 +30,35 @@ #define AFSK_DELTA_COEF ((1ULL << 32) / AFSK_SAMPLERATE) class AFSKProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; + public: + void execute(const buffer_c8_t& buffer) override; -private: - bool configured = false; - - BasebandThread baseband_thread { AFSK_SAMPLERATE, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - uint32_t afsk_samples_per_bit { 0 }; - uint32_t afsk_phase_inc_mark { 0 }; - uint32_t afsk_phase_inc_space { 0 }; - uint8_t afsk_repeat { 0 }; - uint32_t fm_delta { 0 }; - uint8_t symbol_count { 0 }; - - uint8_t repeat_counter { 0 }; - uint8_t bit_pos { 0 }; - uint16_t * word_ptr { }; - uint16_t cur_word { 0 }; - uint8_t cur_bit { 0 }; - uint32_t sample_count { 0 }; - uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 }; - int32_t tone_sample { 0 }, delta { 0 }; - - int8_t re { 0 }, im { 0 }; - - TXProgressMessage txprogress_message { }; + void on_message(const Message* const msg) override; + + private: + bool configured = false; + + BasebandThread baseband_thread{AFSK_SAMPLERATE, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + uint32_t afsk_samples_per_bit{0}; + uint32_t afsk_phase_inc_mark{0}; + uint32_t afsk_phase_inc_space{0}; + uint8_t afsk_repeat{0}; + uint32_t fm_delta{0}; + uint8_t symbol_count{0}; + + uint8_t repeat_counter{0}; + uint8_t bit_pos{0}; + uint16_t* word_ptr{}; + uint16_t cur_word{0}; + uint8_t cur_bit{0}; + uint32_t sample_count{0}; + uint32_t tone_phase{0}, phase{0}, sphase{0}; + int32_t tone_sample{0}, delta{0}; + + int8_t re{0}, im{0}; + + TXProgressMessage txprogress_message{}; }; #endif diff --git a/firmware/baseband/proc_afskrx.cpp b/firmware/baseband/proc_afskrx.cpp index 004773757..609377160 100644 --- a/firmware/baseband/proc_afskrx.cpp +++ b/firmware/baseband/proc_afskrx.cpp @@ -26,166 +26,162 @@ #include "event_m4.hpp" void AFSKRxProcessor::execute(const buffer_c8_t& buffer) { - // This is called at 3072000 / 2048 = 1500Hz - - if (!configured) return; - - // FM demodulation - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples) - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples) - const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples) - - feed_channel_stats(channel_out); - - auto audio = demod.execute(channel_out, audio_buffer); - - audio_output.write(audio); - - // Audio signal processing - for (size_t c = 0; c < audio.count; c++) { - - const int32_t sample_int = audio.p[c] * 32768.0f; - int32_t current_sample = __SSAT(sample_int, 16); - - current_sample /= 128; - - // Delay line put - delay_line[delay_line_index & 0x3F] = current_sample; - - // Delay line get, and LPF - sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4; - sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2); - - delay_line_index++; - - prev_filtered = sample_filtered; - prev_mixed = sample_mixed; - - // Slice - sample_bits <<= 1; - sample_bits |= (sample_filtered < -20) ? 1 : 0; - - // Check for "clean" transition: either 0011 or 1100 - if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) { - // Adjust phase - if (phase < 0x8000) - phase += 0x800; // Is this a proper value ? - else - phase -= 0x800; - } - - phase += phase_inc; - - if (phase >= 0x10000) { - phase &= 0xFFFF; - - if (trigger_word) { - - // Continuous-stream value-triggered mode (AX.25) - UNTESTED - word_bits <<= 1; - word_bits |= (sample_bits & 1); - - bit_counter++; - - if (triggered) { - if (bit_counter == word_length) { - bit_counter = 0; - - data_message.is_data = true; - data_message.value = word_bits & word_mask; - shared_memory.application_queue.push(data_message); - } - } else { - if ((word_bits & word_mask) == trigger_value) { - triggered = !triggered; - bit_counter = 0; - - data_message.is_data = true; - data_message.value = trigger_value; - shared_memory.application_queue.push(data_message); - } - } - - } else { - - // RS232-like modem mode - if (state == WAIT_START) { - if (!(sample_bits & 1)) { - // Got start bit - state = RECEIVE; - bit_counter = 0; - } - } else if (state == WAIT_STOP) { - if (sample_bits & 1) { - // Got stop bit - state = WAIT_START; - } - } else { - word_bits <<= 1; - word_bits |= (sample_bits & 1); - - bit_counter++; - } - - if (bit_counter == word_length) { - bit_counter = 0; - state = WAIT_STOP; - - data_message.is_data = true; - data_message.value = word_bits; - shared_memory.application_queue.push(data_message); - } - - } - } - } + // This is called at 3072000 / 2048 = 1500Hz + + if (!configured) return; + + // FM demodulation + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples) + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples) + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples) + + feed_channel_stats(channel_out); + + auto audio = demod.execute(channel_out, audio_buffer); + + audio_output.write(audio); + + // Audio signal processing + for (size_t c = 0; c < audio.count; c++) { + const int32_t sample_int = audio.p[c] * 32768.0f; + int32_t current_sample = __SSAT(sample_int, 16); + + current_sample /= 128; + + // Delay line put + delay_line[delay_line_index & 0x3F] = current_sample; + + // Delay line get, and LPF + sample_mixed = (delay_line[(delay_line_index - (samples_per_bit / 2)) & 0x3F] * current_sample) / 4; + sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2); + + delay_line_index++; + + prev_filtered = sample_filtered; + prev_mixed = sample_mixed; + + // Slice + sample_bits <<= 1; + sample_bits |= (sample_filtered < -20) ? 1 : 0; + + // Check for "clean" transition: either 0011 or 1100 + if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) { + // Adjust phase + if (phase < 0x8000) + phase += 0x800; // Is this a proper value ? + else + phase -= 0x800; + } + + phase += phase_inc; + + if (phase >= 0x10000) { + phase &= 0xFFFF; + + if (trigger_word) { + // Continuous-stream value-triggered mode (AX.25) - UNTESTED + word_bits <<= 1; + word_bits |= (sample_bits & 1); + + bit_counter++; + + if (triggered) { + if (bit_counter == word_length) { + bit_counter = 0; + + data_message.is_data = true; + data_message.value = word_bits & word_mask; + shared_memory.application_queue.push(data_message); + } + } else { + if ((word_bits & word_mask) == trigger_value) { + triggered = !triggered; + bit_counter = 0; + + data_message.is_data = true; + data_message.value = trigger_value; + shared_memory.application_queue.push(data_message); + } + } + + } else { + // RS232-like modem mode + if (state == WAIT_START) { + if (!(sample_bits & 1)) { + // Got start bit + state = RECEIVE; + bit_counter = 0; + } + } else if (state == WAIT_STOP) { + if (sample_bits & 1) { + // Got stop bit + state = WAIT_START; + } + } else { + word_bits <<= 1; + word_bits |= (sample_bits & 1); + + bit_counter++; + } + + if (bit_counter == word_length) { + bit_counter = 0; + state = WAIT_STOP; + + data_message.is_data = true; + data_message.value = word_bits; + shared_memory.application_queue.push(data_message); + } + } + } + } } void AFSKRxProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::AFSKRxConfigure) - configure(*reinterpret_cast(message)); + if (message->id == Message::ID::AFSKRxConfigure) + configure(*reinterpret_cast(message)); } void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) { - /*constexpr size_t decim_0_input_fs = baseband_fs; - constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; - - constexpr size_t decim_1_input_fs = decim_0_output_fs; - constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - - constexpr size_t channel_filter_input_fs = decim_1_output_fs; - const size_t channel_filter_output_fs = channel_filter_input_fs / 2; - - const size_t demod_input_fs = channel_filter_output_fs;*/ - - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); - channel_filter.configure(taps_11k0_channel.taps, 2); - demod.configure(audio_fs, 5000); - - audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0); - - samples_per_bit = audio_fs / message.baudrate; - - phase_inc = (0x10000 * message.baudrate) / audio_fs; - phase = 0; - - trigger_word = message.trigger_word; - word_length = message.word_length; - trigger_value = message.trigger_value; - word_mask = (1 << word_length) - 1; - - // Delay line - delay_line_index = 0; - - triggered = false; - state = WAIT_START; - - configured = true; + /*constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_1_output_fs; + const size_t channel_filter_output_fs = channel_filter_input_fs / 2; + + const size_t demod_input_fs = channel_filter_output_fs;*/ + + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); + channel_filter.configure(taps_11k0_channel.taps, 2); + demod.configure(audio_fs, 5000); + + audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0); + + samples_per_bit = audio_fs / message.baudrate; + + phase_inc = (0x10000 * message.baudrate) / audio_fs; + phase = 0; + + trigger_word = message.trigger_word; + word_length = message.word_length; + trigger_value = message.trigger_value; + word_mask = (1 << word_length) - 1; + + // Delay line + delay_line_index = 0; + + triggered = false; + state = WAIT_START; + + configured = true; } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_afskrx.hpp b/firmware/baseband/proc_afskrx.hpp index 6504da7a9..9bb8e71d8 100644 --- a/firmware/baseband/proc_afskrx.hpp +++ b/firmware/baseband/proc_afskrx.hpp @@ -36,68 +36,66 @@ #include "message.hpp" class AFSKRxProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 3072000; - static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; - - size_t samples_per_bit { }; - - enum State { - WAIT_START = 0, - WAIT_STOP, - RECEIVE - }; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - std::array audio { }; - const buffer_f32_t audio_buffer { - audio.data(), - audio.size() - }; - - // Array size ok down to 375 bauds (24000 / 375) - std::array delay_line { 0 }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { }; - - dsp::demodulate::FM demod { }; - - AudioOutput audio_output { }; - - State state { }; - size_t delay_line_index { }; - uint32_t bit_counter { 0 }; - uint32_t word_bits { 0 }; - uint32_t sample_bits { 0 }; - uint32_t phase { }, phase_inc { }; - int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { }; - uint32_t word_length { }; - uint32_t word_mask { }; - uint32_t trigger_value { }; - - bool configured { false }; - bool wait_start { }; - bool bit_value { }; - bool trigger_word { }; - bool triggered { }; - - void configure(const AFSKRxConfigureMessage& message); - - AFSKDataMessage data_message { false, 0 }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 3072000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + size_t samples_per_bit{}; + + enum State { + WAIT_START = 0, + WAIT_STOP, + RECEIVE + }; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + std::array audio{}; + const buffer_f32_t audio_buffer{ + audio.data(), + audio.size()}; + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line{0}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + + dsp::demodulate::FM demod{}; + + AudioOutput audio_output{}; + + State state{}; + size_t delay_line_index{}; + uint32_t bit_counter{0}; + uint32_t word_bits{0}; + uint32_t sample_bits{0}; + uint32_t phase{}, phase_inc{}; + int32_t sample_mixed{}, prev_mixed{}, sample_filtered{}, prev_filtered{}; + uint32_t word_length{}; + uint32_t word_mask{}; + uint32_t trigger_value{}; + + bool configured{false}; + bool wait_start{}; + bool bit_value{}; + bool trigger_word{}; + bool triggered{}; + + void configure(const AFSKRxConfigureMessage& message); + + AFSKDataMessage data_message{false, 0}; }; -#endif/*__PROC_TPMS_H__*/ +#endif /*__PROC_TPMS_H__*/ diff --git a/firmware/baseband/proc_ais.cpp b/firmware/baseband/proc_ais.cpp index 8cef6893c..278d7ef25 100644 --- a/firmware/baseband/proc_ais.cpp +++ b/firmware/baseband/proc_ais.cpp @@ -28,45 +28,43 @@ #include "event_m4.hpp" AISProcessor::AISProcessor() { - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); } void AISProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ + /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto decimator_out = decim_1_out; + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decimator_out = decim_1_out; - /* 38.4kHz, 32 samples */ - feed_channel_stats(decimator_out); + /* 38.4kHz, 32 samples */ + feed_channel_stats(decimator_out); - for(size_t i=0; i= 0.0f) ? 1 : 0; - const auto decoded_symbol = nrzi_decode(sliced_symbol); + const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + const auto decoded_symbol = nrzi_decode(sliced_symbol); - packet_builder.execute(decoded_symbol); + packet_builder.execute(decoded_symbol); } void AISProcessor::payload_handler( - const baseband::Packet& packet -) { - const AISPacketMessage message { packet }; - shared_memory.application_queue.push(message); + const baseband::Packet& packet) { + const AISPacketMessage message{packet}; + shared_memory.application_queue.push(message); } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_ais.hpp b/firmware/baseband/proc_ais.hpp index a25b8dde3..a4b0e2fd2 100644 --- a/firmware/baseband/proc_ais.hpp +++ b/firmware/baseband/proc_ais.hpp @@ -43,43 +43,42 @@ #include "ais_baseband.hpp" class AISProcessor : public BasebandProcessor { -public: - AISProcessor(); - - void execute(const buffer_c8_t& buffer) override; - -private: - static constexpr size_t baseband_fs = 2457600; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; - - clock_recovery::ClockRecovery clock_recovery { - 19200, 9600, { 0.0555f }, - [this](const float symbol) { this->consume_symbol(symbol); } - }; - symbol_coding::NRZIDecoder nrzi_decode { }; - PacketBuilder packet_builder { - { 0b0101010101111110, 16, 1 }, - { 0b111110, 6 }, - { 0b01111110, 8 }, - [this](const baseband::Packet& packet) { - this->payload_handler(packet); - } - }; - - void consume_symbol(const float symbol); - void payload_handler(const baseband::Packet& packet); + public: + AISProcessor(); + + void execute(const buffer_c8_t& buffer) override; + + private: + static constexpr size_t baseband_fs = 2457600; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::matched_filter::MatchedFilter mf{baseband::ais::square_taps_38k4_1t_p, 2}; + + clock_recovery::ClockRecovery clock_recovery{ + 19200, + 9600, + {0.0555f}, + [this](const float symbol) { this->consume_symbol(symbol); }}; + symbol_coding::NRZIDecoder nrzi_decode{}; + PacketBuilder packet_builder{ + {0b0101010101111110, 16, 1}, + {0b111110, 6}, + {0b01111110, 8}, + [this](const baseband::Packet& packet) { + this->payload_handler(packet); + }}; + + void consume_symbol(const float symbol); + void payload_handler(const baseband::Packet& packet); }; -#endif/*__PROC_AIS_H__*/ +#endif /*__PROC_AIS_H__*/ diff --git a/firmware/baseband/proc_am_audio.cpp b/firmware/baseband/proc_am_audio.cpp index 9934488a7..aa9689ec1 100644 --- a/firmware/baseband/proc_am_audio.cpp +++ b/firmware/baseband/proc_am_audio.cpp @@ -28,91 +28,91 @@ #include void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) { - if( !configured ) { - return; - } + if (!configured) { + return; + } - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); + channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); - const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer); - const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer); + const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer); + const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer); - // TODO: Feed channel_stats post-decimation data? - feed_channel_stats(channel_out); + // TODO: Feed channel_stats post-decimation data? + feed_channel_stats(channel_out); - auto audio = demodulate(channel_out); - audio_compressor.execute_in_place(audio); - audio_output.write(audio); + auto audio = demodulate(channel_out); + audio_compressor.execute_in_place(audio); + audio_output.write(audio); } buffer_f32_t NarrowbandAMAudio::demodulate(const buffer_c16_t& channel) { - if( modulation_ssb ) { - return demod_ssb.execute(channel, audio_buffer); - } else { - return demod_am.execute(channel, audio_buffer); - } + if (modulation_ssb) { + return demod_ssb.execute(channel, audio_buffer); + } else { + return demod_am.execute(channel, audio_buffer); + } } void NarrowbandAMAudio::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::AMConfigure: - configure(*reinterpret_cast(message)); - break; - - case Message::ID::CaptureConfig: - capture_config(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::AMConfigure: + configure(*reinterpret_cast(message)); + break; + + case Message::ID::CaptureConfig: + capture_config(*reinterpret_cast(message)); + break; + + default: + break; + } } void NarrowbandAMAudio::configure(const AMConfigureMessage& message) { - constexpr size_t decim_0_input_fs = baseband_fs; - constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; - - constexpr size_t decim_1_input_fs = decim_0_output_fs; - constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - - constexpr size_t decim_2_input_fs = decim_1_output_fs; - constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor; - - constexpr size_t channel_filter_input_fs = decim_2_output_fs; - //const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor; - - decim_0.configure(message.decim_0_filter.taps, 33554432); - decim_1.configure(message.decim_1_filter.taps, 131072); - decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor); - channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor); - channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs; - channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs; - channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs; - channel_spectrum.set_decimation_factor(1.0f); - modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB); - audio_output.configure(message.audio_hpf_config); - - configured = true; + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + + constexpr size_t decim_2_input_fs = decim_1_output_fs; + constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_2_output_fs; + // const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor; + + decim_0.configure(message.decim_0_filter.taps, 33554432); + decim_1.configure(message.decim_1_filter.taps, 131072); + decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor); + channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor); + channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs; + channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs; + channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs; + channel_spectrum.set_decimation_factor(1.0f); + modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB); + audio_output.configure(message.audio_hpf_config); + + configured = true; } void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) { - if( message.config ) { - audio_output.set_stream(std::make_unique(message.config)); - } else { - audio_output.set_stream(nullptr); - } + if (message.config) { + audio_output.set_stream(std::make_unique(message.config)); + } else { + audio_output.set_stream(nullptr); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_am_audio.hpp b/firmware/baseband/proc_am_audio.hpp index 483729049..c124d1dbc 100644 --- a/firmware/baseband/proc_am_audio.hpp +++ b/firmware/baseband/proc_am_audio.hpp @@ -36,51 +36,49 @@ #include class NarrowbandAMAudio : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 3072000; - static constexpr size_t decim_2_decimation_factor = 4; - static constexpr size_t channel_filter_decimation_factor = 1; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - std::array audio { }; - const buffer_f32_t audio_buffer { - audio.data(), - audio.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex decim_2 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { }; - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; - - bool modulation_ssb = false; - dsp::demodulate::AM demod_am { }; - dsp::demodulate::SSB demod_ssb { }; - FeedForwardCompressor audio_compressor { }; - AudioOutput audio_output { }; - - SpectrumCollector channel_spectrum { }; - - bool configured { false }; - void configure(const AMConfigureMessage& message); - void capture_config(const CaptureConfigMessage& message); - - buffer_f32_t demodulate(const buffer_c16_t& channel); + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 3072000; + static constexpr size_t decim_2_decimation_factor = 4; + static constexpr size_t channel_filter_decimation_factor = 1; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + std::array audio{}; + const buffer_f32_t audio_buffer{ + audio.data(), + audio.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::decimate::FIRAndDecimateComplex decim_2{}; + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; + + bool modulation_ssb = false; + dsp::demodulate::AM demod_am{}; + dsp::demodulate::SSB demod_ssb{}; + FeedForwardCompressor audio_compressor{}; + AudioOutput audio_output{}; + + SpectrumCollector channel_spectrum{}; + + bool configured{false}; + void configure(const AMConfigureMessage& message); + void capture_config(const CaptureConfigMessage& message); + + buffer_f32_t demodulate(const buffer_c16_t& channel); }; -#endif/*__PROC_AM_AUDIO_H__*/ +#endif /*__PROC_AM_AUDIO_H__*/ diff --git a/firmware/baseband/proc_am_tv.cpp b/firmware/baseband/proc_am_tv.cpp index c5353a573..4b1e6bcb1 100644 --- a/firmware/baseband/proc_am_tv.cpp +++ b/firmware/baseband/proc_am_tv.cpp @@ -30,59 +30,57 @@ #include void WidebandFMAudio::execute(const buffer_c8_t& buffer) { - if( !configured ) { - return; - } - - std::fill(spectrum.begin(), spectrum.end(), 0); + if (!configured) { + return; + } - for(size_t i=0; iid) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::WFMConfigure: + configure(*reinterpret_cast(message)); + break; - case Message::ID::WFMConfigure: - configure(*reinterpret_cast(message)); - break; - - default: - break; - } + default: + break; + } } void WidebandFMAudio::configure(const WFMConfigureMessage& message) { - (void)message; // avoid warning - configured = true; + (void)message; // avoid warning + configured = true; } - int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_am_tv.hpp b/firmware/baseband/proc_am_tv.hpp index 356877b05..7b76c7a2d 100644 --- a/firmware/baseband/proc_am_tv.hpp +++ b/firmware/baseband/proc_am_tv.hpp @@ -36,30 +36,28 @@ #include "tv_collector.hpp" class WidebandFMAudio : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; + public: + void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const message) override; + void on_message(const Message* const message) override; -private: - static constexpr size_t baseband_fs = 2000000; + private: + static constexpr size_t baseband_fs = 2000000; - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; - AudioSpectrum audio_spectrum { }; - TvCollector channel_spectrum { }; - std::array spectrum { }; - - bool configured { false }; - void configure(const WFMConfigureMessage& message); + AudioSpectrum audio_spectrum{}; + TvCollector channel_spectrum{}; + std::array spectrum{}; + bool configured{false}; + void configure(const WFMConfigureMessage& message); }; -#endif/*__PROC_AM_TV_H__*/ +#endif /*__PROC_AM_TV_H__*/ diff --git a/firmware/baseband/proc_aprsrx.cpp b/firmware/baseband/proc_aprsrx.cpp index 6a579ac09..08bde317c 100644 --- a/firmware/baseband/proc_aprsrx.cpp +++ b/firmware/baseband/proc_aprsrx.cpp @@ -28,230 +28,223 @@ #include "stdio.h" void APRSRxProcessor::execute(const buffer_c8_t& buffer) { - // This is called at 3072000 / 2048 = 1500Hz - - if (!configured) return; - - // FM demodulation - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples) - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples) - const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples) - - feed_channel_stats(channel_out); - - auto audio = demod.execute(channel_out, audio_buffer); - - audio_output.write(audio); - - // Audio signal processing - for (size_t c = 0; c < audio.count; c++) { - - const int32_t sample_int = audio.p[c] * 32768.0f; - int32_t current_sample = __SSAT(sample_int, 16); - - current_sample /= 128; - - // Delay line put - delay_line[delay_line_index & 0x3F] = current_sample; - - // Delay line get, and LPF - sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4; - sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2); - - delay_line_index++; - - prev_filtered = sample_filtered; - prev_mixed = sample_mixed; - - // Slice - sample_bits <<= 1; - - uint8_t bit = (sample_filtered < -20) ? 1 : 0; - sample_bits |= bit; + // This is called at 3072000 / 2048 = 1500Hz -/* - int16_t scaled = bit == 1 ? 32767 : -32767; + if (!configured) return; + + // FM demodulation + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples) + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples) + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples) + + feed_channel_stats(channel_out); + + auto audio = demod.execute(channel_out, audio_buffer); + + audio_output.write(audio); + + // Audio signal processing + for (size_t c = 0; c < audio.count; c++) { + const int32_t sample_int = audio.p[c] * 32768.0f; + int32_t current_sample = __SSAT(sample_int, 16); + + current_sample /= 128; + + // Delay line put + delay_line[delay_line_index & 0x3F] = current_sample; + + // Delay line get, and LPF + sample_mixed = (delay_line[(delay_line_index - (samples_per_bit / 2)) & 0x3F] * current_sample) / 4; + sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2); + + delay_line_index++; + + prev_filtered = sample_filtered; + prev_mixed = sample_mixed; - if( stream ) { - const size_t bytes_to_write = sizeof(scaled) * 1; - const auto result = stream->write(&scaled, bytes_to_write); - } + // Slice + sample_bits <<= 1; + + uint8_t bit = (sample_filtered < -20) ? 1 : 0; + sample_bits |= bit; + + /* + int16_t scaled = bit == 1 ? 32767 : -32767; + + if( stream ) { + const size_t bytes_to_write = sizeof(scaled) * 1; + const auto result = stream->write(&scaled, bytes_to_write); + } */ - - // Check for "clean" transition: either 0011 or 1100 - if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) { - // Adjust phase - if (phase < 0x8000) - phase += 0x800; // Is this a proper value ? - else - phase -= 0x800; - } - - phase += phase_inc; - - if (phase >= 0x10000) { - phase &= 0xFFFF; - - if (true) { - uint8_t bit; - if(__builtin_popcount(sample_bits & 0xFF) >= 0x05){ - bit = 0x1; - } - else { - bit = 0x0; - } - - if(parse_bit(bit)){ - parse_packet(); - } - } - } - } + + // Check for "clean" transition: either 0011 or 1100 + if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) { + // Adjust phase + if (phase < 0x8000) + phase += 0x800; // Is this a proper value ? + else + phase -= 0x800; + } + + phase += phase_inc; + + if (phase >= 0x10000) { + phase &= 0xFFFF; + + if (true) { + uint8_t bit; + if (__builtin_popcount(sample_bits & 0xFF) >= 0x05) { + bit = 0x1; + } else { + bit = 0x0; + } + + if (parse_bit(bit)) { + parse_packet(); + } + } + } + } } -void APRSRxProcessor::parse_packet(){ - //validate crc - if(packet_buffer_size >= aprs::APRS_MIN_LENGTH){ - uint16_t crc = 0xFFFF; +void APRSRxProcessor::parse_packet() { + // validate crc + if (packet_buffer_size >= aprs::APRS_MIN_LENGTH) { + uint16_t crc = 0xFFFF; - for(size_t i = 0; i < packet_buffer_size; i++){ - uint8_t byte = packet_buffer[i]; - crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF; - } + for (size_t i = 0; i < packet_buffer_size; i++) { + uint8_t byte = packet_buffer[i]; + crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF; + } - if(crc == 0xF0B8){ - parse_ax25(); - } - } + if (crc == 0xF0B8) { + parse_ax25(); + } + } } -void APRSRxProcessor::parse_ax25(){ - aprs_packet.clear(); - aprs_packet.set_valid_checksum(true); +void APRSRxProcessor::parse_ax25() { + aprs_packet.clear(); + aprs_packet.set_valid_checksum(true); - for(size_t i = 0; i < packet_buffer_size; i++){ - aprs_packet.set(i, packet_buffer[i]); - } + for (size_t i = 0; i < packet_buffer_size; i++) { + aprs_packet.set(i, packet_buffer[i]); + } - APRSPacketMessage packet_message { aprs_packet }; - shared_memory.application_queue.push(packet_message); + APRSPacketMessage packet_message{aprs_packet}; + shared_memory.application_queue.push(packet_message); } -bool APRSRxProcessor::parse_bit(const uint8_t current_bit){ - uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1; - last_bit = current_bit; - - //int16_t log = decoded_bit == 0 ? -32768 : 32767; - //if(stream){ - // const size_t bytes_to_write = sizeof(log) * 1; - // const auto result = stream->write(&log, bytes_to_write); - //} - - if(decoded_bit & 0x1){ - if(ones_count < 8){ - ones_count++; - } - } - else { - if(ones_count > 6){ //not valid - state = WAIT_FLAG; - current_byte = 0; - ones_count = 0; - byte_index = 0; - packet_buffer_size = 0; - return false; - } - else if(ones_count == 6){ //flag - bool done = false; - if(state == IN_FRAME){ - done = true; - } - else { - packet_buffer_size = 0; - } - state = WAIT_FRAME; - current_byte = 0; - ones_count = 0; - byte_index = 0; - - return done; - } - else if(ones_count == 5){ //bit stuff - ones_count = 0; - return false; - } - else { - ones_count = 0; - } - } - - //store - current_byte = current_byte >> 1; - current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0); - byte_index++; - - if(byte_index >= 8){ - byte_index = 0; - if(state == WAIT_FRAME){ - state = IN_FRAME; - } - - if(state == IN_FRAME){ - if(packet_buffer_size + 1 >= 256){ - state = WAIT_FLAG; - current_byte = 0; - ones_count = 0; - byte_index = 0; - packet_buffer_size = 0; - return false; - } - packet_buffer[packet_buffer_size++] = current_byte; - } - } - - return false; +bool APRSRxProcessor::parse_bit(const uint8_t current_bit) { + uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1; + last_bit = current_bit; + + // int16_t log = decoded_bit == 0 ? -32768 : 32767; + // if(stream){ + // const size_t bytes_to_write = sizeof(log) * 1; + // const auto result = stream->write(&log, bytes_to_write); + // } + + if (decoded_bit & 0x1) { + if (ones_count < 8) { + ones_count++; + } + } else { + if (ones_count > 6) { // not valid + state = WAIT_FLAG; + current_byte = 0; + ones_count = 0; + byte_index = 0; + packet_buffer_size = 0; + return false; + } else if (ones_count == 6) { // flag + bool done = false; + if (state == IN_FRAME) { + done = true; + } else { + packet_buffer_size = 0; + } + state = WAIT_FRAME; + current_byte = 0; + ones_count = 0; + byte_index = 0; + + return done; + } else if (ones_count == 5) { // bit stuff + ones_count = 0; + return false; + } else { + ones_count = 0; + } + } + + // store + current_byte = current_byte >> 1; + current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0); + byte_index++; + + if (byte_index >= 8) { + byte_index = 0; + if (state == WAIT_FRAME) { + state = IN_FRAME; + } + + if (state == IN_FRAME) { + if (packet_buffer_size + 1 >= 256) { + state = WAIT_FLAG; + current_byte = 0; + ones_count = 0; + byte_index = 0; + packet_buffer_size = 0; + return false; + } + packet_buffer[packet_buffer_size++] = current_byte; + } + } + + return false; } void APRSRxProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::APRSRxConfigure) - configure(*reinterpret_cast(message)); - if(message->id == Message::ID::CaptureConfig) - capture_config(*reinterpret_cast(message)); + if (message->id == Message::ID::APRSRxConfigure) + configure(*reinterpret_cast(message)); + if (message->id == Message::ID::CaptureConfig) + capture_config(*reinterpret_cast(message)); } void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) { - if( message.config ) { - //stream = std::make_unique(message.config); - audio_output.set_stream(std::make_unique(message.config)); - } else { - //stream.reset(); - audio_output.set_stream(nullptr); - } + if (message.config) { + // stream = std::make_unique(message.config); + audio_output.set_stream(std::make_unique(message.config)); + } else { + // stream.reset(); + audio_output.set_stream(nullptr); + } } -void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) { - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); - channel_filter.configure(taps_11k0_channel.taps, 2); - demod.configure(audio_fs, 5000); - - audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0); - - samples_per_bit = audio_fs / message.baudrate; - - phase_inc = (0x10000 * message.baudrate) / audio_fs; - phase = 0; - - // Delay line - delay_line_index = 0; - - state = WAIT_FLAG; - - configured = true; +void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) { + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); + channel_filter.configure(taps_11k0_channel.taps, 2); + demod.configure(audio_fs, 5000); + + audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0); + + samples_per_bit = audio_fs / message.baudrate; + + phase_inc = (0x10000 * message.baudrate) / audio_fs; + phase = 0; + + // Delay line + delay_line_index = 0; + + state = WAIT_FLAG; + + configured = true; } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_aprsrx.hpp b/firmware/baseband/proc_aprsrx.hpp index 9bcc4828f..f1c384966 100644 --- a/firmware/baseband/proc_aprsrx.hpp +++ b/firmware/baseband/proc_aprsrx.hpp @@ -36,113 +36,110 @@ #include "fifo.hpp" #include "message.hpp" -#include "aprs_packet.hpp" +#include "aprs_packet.hpp" static uint16_t crc_ccitt_tab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78}; class APRSRxProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 3072000; - static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; - - size_t samples_per_bit { }; - - enum State { - WAIT_FLAG, - WAIT_FRAME, - IN_FRAME - }; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - std::array audio { }; - const buffer_f32_t audio_buffer { - audio.data(), - audio.size() - }; - - // Array size ok down to 375 bauds (24000 / 375) - std::array delay_line { 0 }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { }; - - std::unique_ptr stream { }; - - dsp::demodulate::FM demod { }; - - AudioOutput audio_output { }; - - State state { }; - size_t delay_line_index { }; - uint32_t bit_counter { 0 }; - uint32_t word_bits { 0 }; - uint32_t sample_bits { 0 }; - uint32_t phase { }, phase_inc { }; - int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { }; - uint8_t last_bit = 0; - uint8_t ones_count = 0 ; - uint8_t current_byte = 0; - uint8_t byte_index = 0; - uint8_t packet_buffer[256]; - size_t packet_buffer_size = 0; - - bool configured { false }; - bool wait_start { 0 }; - bool bit_value { 0 }; - - aprs::APRSPacket aprs_packet { }; - - void configure(const APRSRxConfigureMessage& message); - void capture_config(const CaptureConfigMessage& message); - void parse_packet(); - bool parse_bit(const uint8_t bit); - void parse_ax25(); + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 3072000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + size_t samples_per_bit{}; + + enum State { + WAIT_FLAG, + WAIT_FRAME, + IN_FRAME + }; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + std::array audio{}; + const buffer_f32_t audio_buffer{ + audio.data(), + audio.size()}; + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line{0}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + + std::unique_ptr stream{}; + + dsp::demodulate::FM demod{}; + + AudioOutput audio_output{}; + + State state{}; + size_t delay_line_index{}; + uint32_t bit_counter{0}; + uint32_t word_bits{0}; + uint32_t sample_bits{0}; + uint32_t phase{}, phase_inc{}; + int32_t sample_mixed{}, prev_mixed{}, sample_filtered{}, prev_filtered{}; + uint8_t last_bit = 0; + uint8_t ones_count = 0; + uint8_t current_byte = 0; + uint8_t byte_index = 0; + uint8_t packet_buffer[256]; + size_t packet_buffer_size = 0; + + bool configured{false}; + bool wait_start{0}; + bool bit_value{0}; + + aprs::APRSPacket aprs_packet{}; + + void configure(const APRSRxConfigureMessage& message); + void capture_config(const CaptureConfigMessage& message); + void parse_packet(); + bool parse_bit(const uint8_t bit); + void parse_ax25(); }; -#endif/*__PROC_TPMS_H__*/ +#endif /*__PROC_TPMS_H__*/ diff --git a/firmware/baseband/proc_audiotx.cpp b/firmware/baseband/proc_audiotx.cpp index b87cfc786..39d2ea1f6 100644 --- a/firmware/baseband/proc_audiotx.cpp +++ b/firmware/baseband/proc_audiotx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -27,95 +27,93 @@ #include -void AudioTXProcessor::execute(const buffer_c8_t& buffer){ - - if (!configured) return; - - // Zero-order hold (poop) - for (size_t i = 0; i < buffer.count; i++) { - resample_acc += resample_inc; - if (resample_acc >= 0x10000) { - resample_acc -= 0x10000; - if (stream) { - stream->read(&audio_sample, 1); - bytes_read++; - } - } - - sample = tone_gen.process(audio_sample - 0x80); - - // FM - delta = sample * fm_delta; - - phase += delta; - sphase = phase + (64 << 24); - - re = sine_table_i8[(sphase & 0xFF000000U) >> 24]; - im = sine_table_i8[(phase & 0xFF000000U) >> 24]; - - buffer.p[i] = { (int8_t)re, (int8_t)im }; - } - - progress_samples += buffer.count; - if (progress_samples >= progress_interval_samples) { - progress_samples -= progress_interval_samples; - - txprogress_message.progress = bytes_read; // Inform UI about progress - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } +void AudioTXProcessor::execute(const buffer_c8_t& buffer) { + if (!configured) return; + + // Zero-order hold (poop) + for (size_t i = 0; i < buffer.count; i++) { + resample_acc += resample_inc; + if (resample_acc >= 0x10000) { + resample_acc -= 0x10000; + if (stream) { + stream->read(&audio_sample, 1); + bytes_read++; + } + } + + sample = tone_gen.process(audio_sample - 0x80); + + // FM + delta = sample * fm_delta; + + phase += delta; + sphase = phase + (64 << 24); + + re = sine_table_i8[(sphase & 0xFF000000U) >> 24]; + im = sine_table_i8[(phase & 0xFF000000U) >> 24]; + + buffer.p[i] = {(int8_t)re, (int8_t)im}; + } + + progress_samples += buffer.count; + if (progress_samples >= progress_interval_samples) { + progress_samples -= progress_interval_samples; + + txprogress_message.progress = bytes_read; // Inform UI about progress + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + } } void AudioTXProcessor::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::AudioTXConfig: - audio_config(*reinterpret_cast(message)); - break; - - case Message::ID::ReplayConfig: - configured = false; - bytes_read = 0; - replay_config(*reinterpret_cast(message)); - break; - - case Message::ID::SamplerateConfig: - samplerate_config(*reinterpret_cast(message)); - break; - - case Message::ID::FIFOData: - configured = true; - break; - - default: - break; - } + switch (message->id) { + case Message::ID::AudioTXConfig: + audio_config(*reinterpret_cast(message)); + break; + + case Message::ID::ReplayConfig: + configured = false; + bytes_read = 0; + replay_config(*reinterpret_cast(message)); + break; + + case Message::ID::SamplerateConfig: + samplerate_config(*reinterpret_cast(message)); + break; + + case Message::ID::FIFOData: + configured = true; + break; + + default: + break; + } } void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) { - fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs); - tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight); - progress_interval_samples = message.divider; - resample_acc = 0; + fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs); + tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight); + progress_interval_samples = message.divider; + resample_acc = 0; } void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) { - if( message.config ) { - - stream = std::make_unique(message.config); - - // Tell application that the buffers and FIFO pointers are ready, prefill - shared_memory.application_queue.push(sig_message); - } else { - stream.reset(); - } + if (message.config) { + stream = std::make_unique(message.config); + + // Tell application that the buffers and FIFO pointers are ready, prefill + shared_memory.application_queue.push(sig_message); + } else { + stream.reset(); + } } void AudioTXProcessor::samplerate_config(const SamplerateConfigMessage& message) { - resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate + resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_audiotx.hpp b/firmware/baseband/proc_audiotx.hpp index 2eecc5888..1d8557fa3 100644 --- a/firmware/baseband/proc_audiotx.hpp +++ b/firmware/baseband/proc_audiotx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -29,38 +29,38 @@ #include "stream_output.hpp" class AudioTXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const msg) override; + + private: + static constexpr size_t baseband_fs = 1536000; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + std::unique_ptr stream{}; + + ToneGen tone_gen{}; + + uint32_t resample_inc{}, resample_acc{}; + uint32_t fm_delta{0}; + uint32_t phase{0}, sphase{0}; + uint8_t audio_sample{}; + int32_t sample{0}, delta{}; + int8_t re{0}, im{0}; + + size_t progress_interval_samples = 0, progress_samples = 0; + + bool configured{false}; + uint32_t bytes_read{0}; + + void samplerate_config(const SamplerateConfigMessage& message); + void audio_config(const AudioTXConfigMessage& message); + void replay_config(const ReplayConfigMessage& message); -private: - static constexpr size_t baseband_fs = 1536000; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - std::unique_ptr stream { }; - - ToneGen tone_gen { }; - - uint32_t resample_inc { }, resample_acc { }; - uint32_t fm_delta { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - uint8_t audio_sample { }; - int32_t sample { 0 }, delta { }; - int8_t re { 0 }, im { 0 }; - - size_t progress_interval_samples = 0 , progress_samples = 0; - - bool configured { false }; - uint32_t bytes_read { 0 }; - - void samplerate_config(const SamplerateConfigMessage& message); - void audio_config(const AudioTXConfigMessage& message); - void replay_config(const ReplayConfigMessage& message); - - TXProgressMessage txprogress_message { }; - RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest }; + TXProgressMessage txprogress_message{}; + RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest}; }; #endif diff --git a/firmware/baseband/proc_btlerx.cpp b/firmware/baseband/proc_btlerx.cpp index 66a369ed9..d19263c74 100644 --- a/firmware/baseband/proc_btlerx.cpp +++ b/firmware/baseband/proc_btlerx.cpp @@ -27,313 +27,264 @@ #include "event_m4.hpp" void BTLERxProcessor::execute(const buffer_c8_t& buffer) { - if (!configured) return; - - // FM demodulation - - /*const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto channel = decim_1.execute(decim_0_out, dst_buffer); - - feed_channel_stats(channel); - - auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/ - - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - feed_channel_stats(decim_0_out); - - auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer); - - /*std::fill(spectrum.begin(), spectrum.end(), 0); - for(size_t i=0; i g_threshold) - { - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE]) - transitions = transitions + 1; - } - } - else - { - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE]) - transitions = transitions + 1; - } - } - - bool packet_detected=false; - //if ( transitions==4 && abs(g_threshold)<15500) - if ( transitions==4) - { - - - uint8_t packet_data[500]; - int packet_length; - uint32_t packet_crc; - //uint32_t calced_crc; // NOTE: restore when CRC is passing - uint64_t packet_addr_l; - //uint32_t result; // NOTE: restore when CRC is passing - uint8_t crc[3]; - uint8_t packet_header_arr[2]; - - packet_addr_l=0; - for (int i=0;i<4;i++) - { - bool current_bit; - uint8_t byte=0; - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - packet_addr_l|=((uint64_t)byte_temp)<<(8*i); - } - - channel_number = 38; - - - for (int t=0;t<2;t++) - { - bool current_bit; - uint8_t byte=0; - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - - packet_header_arr[t] = byte; - } - - - - uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - uint8_t lfsr_1 = byte_temp2 | 2; - int header_length = 2; - int header_counter = 0; - while(header_length--) - { - for(uint8_t i = 0x80; i; i >>= 1) - { - if(lfsr_1 & 0x80) - { - lfsr_1 ^= 0x11; - (packet_header_arr[header_counter]) ^= i; - } - lfsr_1 <<= 1; - } - header_counter = header_counter + 1; - } - - - - if (packet_addr_l==0x8E89BED6) - { - - uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - packet_length=byte_temp3&0x3F; - - } - else - { - packet_length=0; - } - - for (int t=0;t g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - - packet_data[t] = byte; - } - - - - uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - uint8_t lfsr_2 = byte_temp4 | 2; - int pdu_crc_length = packet_length+2+3; - int pdu_crc_counter = 0; - while(pdu_crc_length--) - { - for(uint8_t i = 0x80; i; i >>= 1) - { - if(lfsr_2 & 0x80) - { - lfsr_2 ^= 0x11; - (packet_data[pdu_crc_counter]) ^= i; - } - lfsr_2 <<= 1; - } - pdu_crc_counter = pdu_crc_counter + 1; - } - - - - if (packet_addr_l==0x8E89BED6) - { - crc[0]=crc[1]=crc[2]=0x55; - } - else - { - crc[0]=crc[1]=crc[2]=0; - } - - uint8_t v, t, d, crc_length; - uint32_t crc_result=0; - crc_length = packet_length + 2; - int counter = 0; - while(crc_length--) - { - uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - d = byte_temp5; - for(v = 0; v < 8; v++, d >>= 1) - { - t = crc[0] >> 7; - crc[0] <<= 1; - if(crc[1] & 0x80) crc[0] |= 1; - crc[1] <<= 1; - if(crc[2] & 0x80) crc[1] |= 1; - crc[2] <<= 1; - if(t != (d & 1)) - { - crc[2] ^= 0x5B; - crc[1] ^= 0x06; - } - } - counter = counter + 1; - } - for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v]; - //calced_crc = crc_result; // NOTE: restore when CRC is passing - - packet_crc=0; - for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c]; - - if (packet_addr_l==0x8E89BED6) - //if (packet_crc==calced_crc) // NOTE: restore when CRC is passing - { - uint8_t mac_data[6]; - int counter = 0; - for (int i = 7; i >= 2; i--) - { - uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); - //result = byte_temp6; // NOTE: restore when CRC is passing - mac_data[counter] = byte_temp6; - counter = counter + 1; - } - - data_message.is_data = false; - data_message.value = 'A'; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[0]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[1]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[2]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[3]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[4]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = mac_data[5]; - shared_memory.application_queue.push(data_message); - - data_message.is_data = false; - data_message.value = 'B'; - shared_memory.application_queue.push(data_message); - - packet_detected = true; - } - else - packet_detected = false; - } - - if (packet_detected) - { - skipSamples=20; - } - } - } + if (!configured) return; + + // FM demodulation + + /*const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto channel = decim_1.execute(decim_0_out, dst_buffer); + + feed_channel_stats(channel); + + auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/ + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + feed_channel_stats(decim_0_out); + + auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer); + + /*std::fill(spectrum.begin(), spectrum.end(), 0); + for(size_t i=0; i g_threshold) { + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + c) % RB_SIZE] > rb_buf[(rb_head + c + 1) % RB_SIZE]) + transitions = transitions + 1; + } + } else { + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + c) % RB_SIZE] < rb_buf[(rb_head + c + 1) % RB_SIZE]) + transitions = transitions + 1; + } + } + + bool packet_detected = false; + // if ( transitions==4 && abs(g_threshold)<15500) + if (transitions == 4) { + uint8_t packet_data[500]; + int packet_length; + uint32_t packet_crc; + // uint32_t calced_crc; // NOTE: restore when CRC is passing + uint64_t packet_addr_l; + // uint32_t result; // NOTE: restore when CRC is passing + uint8_t crc[3]; + uint8_t packet_header_arr[2]; + + packet_addr_l = 0; + for (int i = 0; i < 4; i++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + (i + 1) * 8 + c) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + uint8_t byte_temp = (uint8_t)(((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + packet_addr_l |= ((uint64_t)byte_temp) << (8 * i); + } + + channel_number = 38; + + for (int t = 0; t < 2; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + 5 * 8 + t * 8 + c) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + + packet_header_arr[t] = byte; + } + + uint8_t byte_temp2 = (uint8_t)(((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + uint8_t lfsr_1 = byte_temp2 | 2; + int header_length = 2; + int header_counter = 0; + while (header_length--) { + for (uint8_t i = 0x80; i; i >>= 1) { + if (lfsr_1 & 0x80) { + lfsr_1 ^= 0x11; + (packet_header_arr[header_counter]) ^= i; + } + lfsr_1 <<= 1; + } + header_counter = header_counter + 1; + } + + if (packet_addr_l == 0x8E89BED6) { + uint8_t byte_temp3 = (uint8_t)(((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + packet_length = byte_temp3 & 0x3F; + + } else { + packet_length = 0; + } + + for (int t = 0; t < packet_length + 2 + 3; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + 5 * 8 + t * 8 + c) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + + packet_data[t] = byte; + } + + uint8_t byte_temp4 = (uint8_t)(((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + uint8_t lfsr_2 = byte_temp4 | 2; + int pdu_crc_length = packet_length + 2 + 3; + int pdu_crc_counter = 0; + while (pdu_crc_length--) { + for (uint8_t i = 0x80; i; i >>= 1) { + if (lfsr_2 & 0x80) { + lfsr_2 ^= 0x11; + (packet_data[pdu_crc_counter]) ^= i; + } + lfsr_2 <<= 1; + } + pdu_crc_counter = pdu_crc_counter + 1; + } + + if (packet_addr_l == 0x8E89BED6) { + crc[0] = crc[1] = crc[2] = 0x55; + } else { + crc[0] = crc[1] = crc[2] = 0; + } + + uint8_t v, t, d, crc_length; + uint32_t crc_result = 0; + crc_length = packet_length + 2; + int counter = 0; + while (crc_length--) { + uint8_t byte_temp5 = (uint8_t)(((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + d = byte_temp5; + for (v = 0; v < 8; v++, d >>= 1) { + t = crc[0] >> 7; + crc[0] <<= 1; + if (crc[1] & 0x80) crc[0] |= 1; + crc[1] <<= 1; + if (crc[2] & 0x80) crc[1] |= 1; + crc[2] <<= 1; + if (t != (d & 1)) { + crc[2] ^= 0x5B; + crc[1] ^= 0x06; + } + } + counter = counter + 1; + } + for (v = 0; v < 3; v++) crc_result = (crc_result << 8) | crc[v]; + // calced_crc = crc_result; // NOTE: restore when CRC is passing + + packet_crc = 0; + for (int c = 0; c < 3; c++) packet_crc = (packet_crc << 8) | packet_data[packet_length + 2 + c]; + + if (packet_addr_l == 0x8E89BED6) + // if (packet_crc==calced_crc) // NOTE: restore when CRC is passing + { + uint8_t mac_data[6]; + int counter = 0; + for (int i = 7; i >= 2; i--) { + uint8_t byte_temp6 = (uint8_t)(((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + // result = byte_temp6; // NOTE: restore when CRC is passing + mac_data[counter] = byte_temp6; + counter = counter + 1; + } + + data_message.is_data = false; + data_message.value = 'A'; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[0]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[1]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[2]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[3]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[4]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[5]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = false; + data_message.value = 'B'; + shared_memory.application_queue.push(data_message); + + packet_detected = true; + } else + packet_detected = false; + } + + if (packet_detected) { + skipSamples = 20; + } + } + } } void BTLERxProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::BTLERxConfigure) - configure(*reinterpret_cast(message)); + if (message->id == Message::ID::BTLERxConfigure) + configure(*reinterpret_cast(message)); } -void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) { - (void)message; //avoid warning - decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); - decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); - demod.configure(audio_fs, 5000); +void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) { + (void)message; // avoid warning + decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); + decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); + demod.configure(audio_fs, 5000); - configured = true; + configured = true; } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_btlerx.hpp b/firmware/baseband/proc_btlerx.hpp index 2bb48872b..b7cdaf1fe 100644 --- a/firmware/baseband/proc_btlerx.hpp +++ b/firmware/baseband/proc_btlerx.hpp @@ -37,59 +37,54 @@ #include "message.hpp" class BTLERxProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 4000000; - static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - std::array spectrum { }; - const buffer_c16_t spectrum_buffer { - spectrum.data(), - spectrum.size() - }; - - const buffer_s16_t work_audio_buffer { - (int16_t*)dst.data(), - sizeof(dst) / sizeof(int16_t) - }; - - - // Array size ok down to 375 bauds (24000 / 375) - std::array delay_line { 0 }; - std::array rb_buf { 0 }; - - /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; - dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; - - dsp::demodulate::FM demod { }; - int rb_head {-1}; - int32_t g_threshold {0}; - uint8_t channel_number {38}; - int skipSamples {1000}; - int RB_SIZE {1000}; - - bool configured { false }; - - - void configure(const BTLERxConfigureMessage& message); - - AFSKDataMessage data_message { false, 0 }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 4000000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + std::array spectrum{}; + const buffer_c16_t spectrum_buffer{ + spectrum.data(), + spectrum.size()}; + + const buffer_s16_t work_audio_buffer{ + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t)}; + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line{0}; + std::array rb_buf{0}; + + /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + + dsp::demodulate::FM demod{}; + int rb_head{-1}; + int32_t g_threshold{0}; + uint8_t channel_number{38}; + int skipSamples{1000}; + int RB_SIZE{1000}; + + bool configured{false}; + + void configure(const BTLERxConfigureMessage& message); + + AFSKDataMessage data_message{false, 0}; }; -#endif/*__PROC_BTLERX_H__*/ +#endif /*__PROC_BTLERX_H__*/ diff --git a/firmware/baseband/proc_capture.cpp b/firmware/baseband/proc_capture.cpp index fe2fddbaf..8703bcee8 100644 --- a/firmware/baseband/proc_capture.cpp +++ b/firmware/baseband/proc_capture.cpp @@ -29,84 +29,83 @@ #include "utility.hpp" CaptureProcessor::CaptureProcessor() { - decim_0.configure(taps_200k_decim_0.taps, 33554432); - decim_1.configure(taps_200k_decim_1.taps, 131072); - - channel_spectrum.set_decimation_factor(1); + decim_0.configure(taps_200k_decim_0.taps, 33554432); + decim_1.configure(taps_200k_decim_1.taps, 131072); + + channel_spectrum.set_decimation_factor(1); } void CaptureProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto& decimator_out = decim_1_out; - const auto& channel = decimator_out; - - if( stream ) { - const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count; - const size_t written = stream->write(decimator_out.p, bytes_to_write); - if( written != bytes_to_write ) - { - //TODO eventually report error somewhere - } - } - - feed_channel_stats(channel); - - spectrum_samples += channel.count; - if( spectrum_samples >= spectrum_interval_samples ) { - spectrum_samples -= spectrum_interval_samples; - channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); - } + /* 2.4576MHz, 2048 samples */ + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto& decimator_out = decim_1_out; + const auto& channel = decimator_out; + + if (stream) { + const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count; + const size_t written = stream->write(decimator_out.p, bytes_to_write); + if (written != bytes_to_write) { + // TODO eventually report error somewhere + } + } + + feed_channel_stats(channel); + + spectrum_samples += channel.count; + if (spectrum_samples >= spectrum_interval_samples) { + spectrum_samples -= spectrum_interval_samples; + channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); + } } void CaptureProcessor::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::SamplerateConfig: - samplerate_config(*reinterpret_cast(message)); - break; - - case Message::ID::CaptureConfig: - capture_config(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::SamplerateConfig: + samplerate_config(*reinterpret_cast(message)); + break; + + case Message::ID::CaptureConfig: + capture_config(*reinterpret_cast(message)); + break; + + default: + break; + } } void CaptureProcessor::samplerate_config(const SamplerateConfigMessage& message) { - baseband_fs = message.sample_rate; - baseband_thread.set_sampling_rate(baseband_fs); - - size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; + baseband_fs = message.sample_rate; + baseband_thread.set_sampling_rate(baseband_fs); + + size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; - size_t decim_1_input_fs = decim_0_output_fs; - size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + size_t decim_1_input_fs = decim_0_output_fs; + size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs; - channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs; - channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs; + channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs; + channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs; + channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs; - spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz; - spectrum_samples = 0; + spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz; + spectrum_samples = 0; } void CaptureProcessor::capture_config(const CaptureConfigMessage& message) { - if( message.config ) { - stream = std::make_unique(message.config); - } else { - stream.reset(); - } + if (message.config) { + stream = std::make_unique(message.config); + } else { + stream.reset(); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_capture.hpp b/firmware/baseband/proc_capture.hpp index 2710b81b9..99c48735b 100644 --- a/firmware/baseband/proc_capture.hpp +++ b/firmware/baseband/proc_capture.hpp @@ -37,41 +37,40 @@ #include class CaptureProcessor : public BasebandProcessor { -public: - CaptureProcessor(); + public: + CaptureProcessor(); - void execute(const buffer_c8_t& buffer) override; + void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const message) override; + void on_message(const Message* const message) override; -private: - // TODO: Repeated value needs to be transmitted from application side. - size_t baseband_fs = 0; - static constexpr auto spectrum_rate_hz = 50.0f; + private: + // TODO: Repeated value needs to be transmitted from application side. + size_t baseband_fs = 0; + static constexpr auto spectrum_rate_hz = 50.0f; - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; - dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; - std::unique_ptr stream { }; + std::unique_ptr stream{}; - SpectrumCollector channel_spectrum { }; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; + SpectrumCollector channel_spectrum{}; + size_t spectrum_interval_samples = 0; + size_t spectrum_samples = 0; - void samplerate_config(const SamplerateConfigMessage& message); - void capture_config(const CaptureConfigMessage& message); + void samplerate_config(const SamplerateConfigMessage& message); + void capture_config(const CaptureConfigMessage& message); }; -#endif/*__PROC_CAPTURE_HPP__*/ +#endif /*__PROC_CAPTURE_HPP__*/ diff --git a/firmware/baseband/proc_ert.cpp b/firmware/baseband/proc_ert.cpp index 2bc8b858e..019c97ef9 100644 --- a/firmware/baseband/proc_ert.cpp +++ b/firmware/baseband/proc_ert.cpp @@ -26,94 +26,90 @@ #include "event_m4.hpp" float ERTProcessor::abs(const complex8_t& v) { - // const int16_t r = v.real() - offset_i; - // const int16_t i = v.imag() - offset_q; - // const uint32_t r2 = r * r; - // const uint32_t i2 = i * i; - // const uint32_t r2_i2 = r2 + i2; - // return std::sqrt(static_cast(r2_i2)); - const float r = static_cast(v.real()) - offset_i; - const float i = static_cast(v.imag()) - offset_q; - const float r2 = r * r; - const float i2 = i * i; - const float r2_i2 = r2 + i2; - return std::sqrt(r2_i2); + // const int16_t r = v.real() - offset_i; + // const int16_t i = v.imag() - offset_q; + // const uint32_t r2 = r * r; + // const uint32_t i2 = i * i; + // const uint32_t r2_i2 = r2 + i2; + // return std::sqrt(static_cast(r2_i2)); + const float r = static_cast(v.real()) - offset_i; + const float i = static_cast(v.imag()) - offset_q; + const float r2 = r * r; + const float i2 = i * i; + const float r2_i2 = r2 + i2; + return std::sqrt(r2_i2); } void ERTProcessor::execute(const buffer_c8_t& buffer) { - /* 4.194304MHz, 2048 samples */ - - const complex8_t* src = &buffer.p[0]; - const complex8_t* const src_end = &buffer.p[buffer.count]; - - average_i += src->real(); - average_q += src->imag(); - average_count++; - if( average_count == average_window ) { - offset_i = static_cast(average_i) / average_window; - offset_q = static_cast(average_q) / average_window; - average_i = 0; - average_q = 0; - average_count = 0; - } - - const float gain = 128 * samples_per_symbol; - const float k = 1.0f / gain; - - while(src < src_end) { - float sum = 0.0f; - for(size_t i=0; i<(samples_per_symbol / 2); i++) { - sum += abs(*(src++)); - } - sum_half_period[1] = sum_half_period[0]; - sum_half_period[0] = sum; - - sum_period[2] = sum_period[1]; - sum_period[1] = sum_period[0]; - sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k; - - manchester[2] = manchester[1]; - manchester[1] = manchester[0]; - manchester[0] = sum_period[2] - sum_period[0]; - - const auto data = manchester[0] - manchester[2]; - - clock_recovery(data); - } + /* 4.194304MHz, 2048 samples */ + + const complex8_t* src = &buffer.p[0]; + const complex8_t* const src_end = &buffer.p[buffer.count]; + + average_i += src->real(); + average_q += src->imag(); + average_count++; + if (average_count == average_window) { + offset_i = static_cast(average_i) / average_window; + offset_q = static_cast(average_q) / average_window; + average_i = 0; + average_q = 0; + average_count = 0; + } + + const float gain = 128 * samples_per_symbol; + const float k = 1.0f / gain; + + while (src < src_end) { + float sum = 0.0f; + for (size_t i = 0; i < (samples_per_symbol / 2); i++) { + sum += abs(*(src++)); + } + sum_half_period[1] = sum_half_period[0]; + sum_half_period[0] = sum; + + sum_period[2] = sum_period[1]; + sum_period[1] = sum_period[0]; + sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k; + + manchester[2] = manchester[1]; + manchester[1] = manchester[0]; + manchester[0] = sum_period[2] - sum_period[0]; + + const auto data = manchester[0] - manchester[2]; + + clock_recovery(data); + } } void ERTProcessor::consume_symbol( - const float raw_symbol -) { - const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - scm_builder.execute(sliced_symbol); - scmplus_builder.execute(sliced_symbol); - idm_builder.execute(sliced_symbol); + const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + scm_builder.execute(sliced_symbol); + scmplus_builder.execute(sliced_symbol); + idm_builder.execute(sliced_symbol); } void ERTProcessor::scm_handler( - const baseband::Packet& packet -) { - const ERTPacketMessage message { ert::Packet::Type::SCM, packet }; - shared_memory.application_queue.push(message); + const baseband::Packet& packet) { + const ERTPacketMessage message{ert::Packet::Type::SCM, packet}; + shared_memory.application_queue.push(message); } void ERTProcessor::scmplus_handler( - const baseband::Packet& packet -) { - const ERTPacketMessage message { ert::Packet::Type::SCMPLUS, packet }; - shared_memory.application_queue.push(message); + const baseband::Packet& packet) { + const ERTPacketMessage message{ert::Packet::Type::SCMPLUS, packet}; + shared_memory.application_queue.push(message); } void ERTProcessor::idm_handler( - const baseband::Packet& packet -) { - const ERTPacketMessage message { ert::Packet::Type::IDM, packet }; - shared_memory.application_queue.push(message); + const baseband::Packet& packet) { + const ERTPacketMessage message{ert::Packet::Type::IDM, packet}; + shared_memory.application_queue.push(message); } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_ert.hpp b/firmware/baseband/proc_ert.hpp index f830305f5..9e2a86553 100644 --- a/firmware/baseband/proc_ert.hpp +++ b/firmware/baseband/proc_ert.hpp @@ -40,85 +40,83 @@ #include // ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x1f2a60)[2:].zfill(21))]) -constexpr uint64_t scm_preamble_and_sync_manchester { 0b101010101001011001100110010110100101010101 }; -constexpr size_t scm_preamble_and_sync_length { 42 - 10 }; -constexpr size_t scm_payload_length_max { 150 }; +constexpr uint64_t scm_preamble_and_sync_manchester{0b101010101001011001100110010110100101010101}; +constexpr size_t scm_preamble_and_sync_length{42 - 10}; +constexpr size_t scm_payload_length_max{150}; // ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x16a3)[2:].zfill(16))]) -constexpr uint64_t scmplus_preamble_and_sync_manchester { 0b01010110011010011001100101011010 }; -constexpr size_t scmplus_preamble_and_sync_length { 32 - 0 }; -constexpr size_t scmplus_payload_length_max { 224 }; +constexpr uint64_t scmplus_preamble_and_sync_manchester{0b01010110011010011001100101011010}; +constexpr size_t scmplus_preamble_and_sync_length{32 - 0}; +constexpr size_t scmplus_payload_length_max{224}; // ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x555516a3)[2:].zfill(32))]) -constexpr uint64_t idm_preamble_and_sync_manchester { 0b0110011001100110011001100110011001010110011010011001100101011010 }; -constexpr size_t idm_preamble_and_sync_length { 64 - 16 }; -constexpr size_t idm_payload_length_max { 1408 }; +constexpr uint64_t idm_preamble_and_sync_manchester{0b0110011001100110011001100110011001010110011010011001100101011010}; +constexpr size_t idm_preamble_and_sync_length{64 - 16}; +constexpr size_t idm_payload_length_max{1408}; class ERTProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - -private: - const uint32_t baseband_sampling_rate = 4194304; - const size_t decimation = 1; - const float symbol_rate = 32768; - - const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation; - const size_t samples_per_symbol = channel_sampling_rate / symbol_rate; - const float clock_recovery_rate = symbol_rate * 2; - - BasebandThread baseband_thread { baseband_sampling_rate, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - clock_recovery::ClockRecovery clock_recovery { - clock_recovery_rate, symbol_rate, { 1.0f / 18.0f }, - [this](const float symbol) { this->consume_symbol(symbol); } - }; - - PacketBuilder scm_builder { - { scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1 }, - { }, - { scm_payload_length_max }, - [this](const baseband::Packet& packet) { - this->scm_handler(packet); - } - }; - - PacketBuilder scmplus_builder { - { scmplus_preamble_and_sync_manchester, scmplus_preamble_and_sync_length, 1 }, - { }, - { scmplus_payload_length_max }, - [this](const baseband::Packet& packet) { - this->scmplus_handler(packet); - } - }; - - PacketBuilder idm_builder { - { idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1 }, - { }, - { idm_payload_length_max }, - [this](const baseband::Packet& packet) { - this->idm_handler(packet); - } - }; - - void consume_symbol(const float symbol); - void scm_handler(const baseband::Packet& packet); - void scmplus_handler(const baseband::Packet& packet); - void idm_handler(const baseband::Packet& packet); - - float sum_half_period[2]; - float sum_period[3]; - float manchester[3]; - - const size_t average_window { 2048 }; - int32_t average_i { 0 }; - int32_t average_q { 0 }; - size_t average_count { 0 }; - float offset_i { 0.0f }; - float offset_q { 0.0f }; - - float abs(const complex8_t& v); + public: + void execute(const buffer_c8_t& buffer) override; + + private: + const uint32_t baseband_sampling_rate = 4194304; + const size_t decimation = 1; + const float symbol_rate = 32768; + + const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation; + const size_t samples_per_symbol = channel_sampling_rate / symbol_rate; + const float clock_recovery_rate = symbol_rate * 2; + + BasebandThread baseband_thread{baseband_sampling_rate, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + clock_recovery::ClockRecovery clock_recovery{ + clock_recovery_rate, + symbol_rate, + {1.0f / 18.0f}, + [this](const float symbol) { this->consume_symbol(symbol); }}; + + PacketBuilder scm_builder{ + {scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1}, + {}, + {scm_payload_length_max}, + [this](const baseband::Packet& packet) { + this->scm_handler(packet); + }}; + + PacketBuilder scmplus_builder{ + {scmplus_preamble_and_sync_manchester, scmplus_preamble_and_sync_length, 1}, + {}, + {scmplus_payload_length_max}, + [this](const baseband::Packet& packet) { + this->scmplus_handler(packet); + }}; + + PacketBuilder idm_builder{ + {idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1}, + {}, + {idm_payload_length_max}, + [this](const baseband::Packet& packet) { + this->idm_handler(packet); + }}; + + void consume_symbol(const float symbol); + void scm_handler(const baseband::Packet& packet); + void scmplus_handler(const baseband::Packet& packet); + void idm_handler(const baseband::Packet& packet); + + float sum_half_period[2]; + float sum_period[3]; + float manchester[3]; + + const size_t average_window{2048}; + int32_t average_i{0}; + int32_t average_q{0}; + size_t average_count{0}; + float offset_i{0.0f}; + float offset_q{0.0f}; + + float abs(const complex8_t& v); }; -#endif/*__PROC_ERT_H__*/ +#endif /*__PROC_ERT_H__*/ diff --git a/firmware/baseband/proc_flash_utility.cpp b/firmware/baseband/proc_flash_utility.cpp index c3730858f..9de74270a 100644 --- a/firmware/baseband/proc_flash_utility.cpp +++ b/firmware/baseband/proc_flash_utility.cpp @@ -34,83 +34,83 @@ void initialize_flash(); void erase_flash(); void initialize_sdcard(); -void write_firmware(FIL *); -void write_page(size_t, uint8_t *, size_t); +void write_firmware(FIL*); +void write_page(size_t, uint8_t*, size_t); int main() { - const TCHAR *filename = reinterpret_cast(&shared_memory.bb_data.data[0]); + const TCHAR* filename = reinterpret_cast(&shared_memory.bb_data.data[0]); - initialize_flash(); - palSetPad(LED_PORT, LEDRX_PAD); - erase_flash(); + initialize_flash(); + palSetPad(LED_PORT, LEDRX_PAD); + erase_flash(); - initialize_sdcard(); + initialize_sdcard(); - FIL firmware_file; - if (f_open(&firmware_file, filename, FA_READ) != FR_OK) chDbgPanic("no file"); + FIL firmware_file; + if (f_open(&firmware_file, filename, FA_READ) != FR_OK) chDbgPanic("no file"); - palSetPad(LED_PORT, LEDTX_PAD); + palSetPad(LED_PORT, LEDTX_PAD); - write_firmware(&firmware_file); + write_firmware(&firmware_file); - palClearPad(LED_PORT, LEDTX_PAD); - palClearPad(LED_PORT, LEDRX_PAD); + palClearPad(LED_PORT, LEDTX_PAD); + palClearPad(LED_PORT, LEDRX_PAD); - f_close(&firmware_file); + f_close(&firmware_file); - while(1) - __WFE(); + while (1) + __WFE(); - return 0; + return 0; } void initialize_flash() { - w25q80bv::disable_spifi(); - w25q80bv::initialite_spi(); - w25q80bv::setup(); + w25q80bv::disable_spifi(); + w25q80bv::initialite_spi(); + w25q80bv::setup(); - w25q80bv::wait_for_device(); - w25q80bv::wait_not_busy(); + w25q80bv::wait_for_device(); + w25q80bv::wait_not_busy(); } void erase_flash() { - w25q80bv::remove_write_protection(); - w25q80bv::wait_not_busy(); + w25q80bv::remove_write_protection(); + w25q80bv::wait_not_busy(); - w25q80bv::erase_chip(); - w25q80bv::wait_not_busy(); + w25q80bv::erase_chip(); + w25q80bv::wait_not_busy(); } void initialize_sdcard() { - static FATFS fs; + static FATFS fs; - sdcStart(&SDCD1, nullptr); - if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); - if (f_mount(&fs, reinterpret_cast(_T("")), 1) != FR_OK) chDbgPanic("no sd card #2"); + sdcStart(&SDCD1, nullptr); + if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); + if (f_mount(&fs, reinterpret_cast(_T("")), 1) != FR_OK) chDbgPanic("no sd card #2"); } -void write_firmware(FIL *firmware_file) { - uint8_t *data_buffer = &shared_memory.bb_data.data[0]; +void write_firmware(FIL* firmware_file) { + uint8_t* data_buffer = &shared_memory.bb_data.data[0]; - for (size_t page_index = 0; page_index < NUM_PAGES; page_index++) { - if (page_index % 32 == 0) - palTogglePad(LED_PORT, LEDTX_PAD); + for (size_t page_index = 0; page_index < NUM_PAGES; page_index++) { + if (page_index % 32 == 0) + palTogglePad(LED_PORT, LEDTX_PAD); - size_t bytes_read; - if (f_read(firmware_file, data_buffer, PAGE_LEN, &bytes_read) != FR_OK) chDbgPanic("no data"); + size_t bytes_read; + if (f_read(firmware_file, data_buffer, PAGE_LEN, &bytes_read) != FR_OK) chDbgPanic("no data"); - if (bytes_read > 0) - write_page(page_index, data_buffer, bytes_read); + if (bytes_read > 0) + write_page(page_index, data_buffer, bytes_read); - if (bytes_read < PAGE_LEN) - return; - } + if (bytes_read < PAGE_LEN) + return; + } } -void write_page(size_t page_index, uint8_t *data_buffer, size_t data_length) { - w25q80bv::wait_not_busy(); - w25q80bv::remove_write_protection(); - w25q80bv::wait_not_busy(); - w25q80bv::write(page_index, data_buffer, data_length); - w25q80bv::wait_not_busy(); +void write_page(size_t page_index, uint8_t* data_buffer, size_t data_length) { + w25q80bv::wait_not_busy(); + w25q80bv::remove_write_protection(); + w25q80bv::wait_not_busy(); + w25q80bv::write(page_index, data_buffer, data_length); + w25q80bv::wait_not_busy(); } diff --git a/firmware/baseband/proc_fsk.cpp b/firmware/baseband/proc_fsk.cpp index 1b4f9f5f0..90d357249 100644 --- a/firmware/baseband/proc_fsk.cpp +++ b/firmware/baseband/proc_fsk.cpp @@ -28,80 +28,79 @@ #include void FSKProcessor::execute(const buffer_c8_t& buffer) { - int8_t re, im; - - // This is called at 2.28M/2048 = 1113Hz - - for (size_t i = 0; i < buffer.count; i++) { - - if (configured) { - if (sample_count >= samples_per_bit) { - if (bit_pos > length) { - // End of data - cur_bit = 0; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - configured = false; - } else { - cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80; - bit_pos++; - if (progress_count >= progress_notice) { - progress_count = 0; - txprogress_message.progress++; - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } else { - progress_count++; - } - } - sample_count = 0; - } else { - sample_count++; - } - - if (cur_bit) - phase += shift_one; - else - phase += shift_zero; - - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000) >> 24]); - } else { - re = 0; - im = 0; - } - - buffer.p[i] = {re, im}; - } + int8_t re, im; + + // This is called at 2.28M/2048 = 1113Hz + + for (size_t i = 0; i < buffer.count; i++) { + if (configured) { + if (sample_count >= samples_per_bit) { + if (bit_pos > length) { + // End of data + cur_bit = 0; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + configured = false; + } else { + cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80; + bit_pos++; + if (progress_count >= progress_notice) { + progress_count = 0; + txprogress_message.progress++; + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + } else { + progress_count++; + } + } + sample_count = 0; + } else { + sample_count++; + } + + if (cur_bit) + phase += shift_one; + else + phase += shift_zero; + + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000) >> 24]); + } else { + re = 0; + im = 0; + } + + buffer.p[i] = {re, im}; + } } void FSKProcessor::on_message(const Message* const p) { - const auto message = *reinterpret_cast(p); - - if (message.id == Message::ID::FSKConfigure) { - samples_per_bit = message.samples_per_bit; - length = message.stream_length + 32; // Why ?! - - shift_one = message.shift * (0xFFFFFFFFULL / 2280000); - shift_zero = -shift_one; - - progress_notice = message.progress_notice; - - sample_count = samples_per_bit; - progress_count = 0; - bit_pos = 0; - cur_bit = 0; - - txprogress_message.progress = 0; - txprogress_message.done = false; - configured = true; - } + const auto message = *reinterpret_cast(p); + + if (message.id == Message::ID::FSKConfigure) { + samples_per_bit = message.samples_per_bit; + length = message.stream_length + 32; // Why ?! + + shift_one = message.shift * (0xFFFFFFFFULL / 2280000); + shift_zero = -shift_one; + + progress_notice = message.progress_notice; + + sample_count = samples_per_bit; + progress_count = 0; + bit_pos = 0; + cur_bit = 0; + + txprogress_message.progress = 0; + txprogress_message.done = false; + configured = true; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_fsk.hpp b/firmware/baseband/proc_fsk.hpp index 77ba82eb9..b8f16eb9d 100644 --- a/firmware/baseband/proc_fsk.hpp +++ b/firmware/baseband/proc_fsk.hpp @@ -27,27 +27,27 @@ #include "baseband_thread.hpp" class FSKProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const p) override; - -private: - bool configured = false; - - BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - uint32_t samples_per_bit { 0 }; - uint32_t length { 0 }; - - uint32_t shift_zero { }, shift_one { }; - uint32_t bit_pos { 0 }; - uint32_t progress_notice { }, progress_count { 0 }; - uint8_t cur_bit { 0 }; - uint32_t sample_count { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - - TXProgressMessage txprogress_message { }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const p) override; + + private: + bool configured = false; + + BasebandThread baseband_thread{2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + uint32_t samples_per_bit{0}; + uint32_t length{0}; + + uint32_t shift_zero{}, shift_one{}; + uint32_t bit_pos{0}; + uint32_t progress_notice{}, progress_count{0}; + uint8_t cur_bit{0}; + uint32_t sample_count{0}; + uint32_t phase{0}, sphase{0}; + + TXProgressMessage txprogress_message{}; }; #endif diff --git a/firmware/baseband/proc_gps_sim.cpp b/firmware/baseband/proc_gps_sim.cpp index 83b68c0f7..860da2168 100644 --- a/firmware/baseband/proc_gps_sim.cpp +++ b/firmware/baseband/proc_gps_sim.cpp @@ -30,99 +30,98 @@ #include "utility.hpp" ReplayProcessor::ReplayProcessor() { - channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000; - channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000; - channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000; - - spectrum_samples = 0; - - channel_spectrum.set_decimation_factor(1); - - configured = false; + channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000; + channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000; + channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000; + + spectrum_samples = 0; + + channel_spectrum.set_decimation_factor(1); + + configured = false; } void ReplayProcessor::execute(const buffer_c8_t& buffer) { - /* 4MHz, 2048 samples */ - - if (!configured) return; - - // File data is in C16 format, we need C8 - // File samplerate is 500kHz, we're at 4MHz - // iq_buffer can only be 512 C16 samples (RAM limitation) - // To fill up the 2048-sample C8 buffer, we need: - // 2048 samples * 2 bytes per sample = 4096 bytes - // Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each - // So 256 * 4 bytes per sample (C16) = 1024 bytes from the file - if( stream ) { //sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048 - const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count ); // *2 (C16), /8 (oversampling) should be == 1024 - bytes_read += stream->read(iq_buffer.p, bytes_to_read); - } - - // Fill and "stretch" - for (size_t i = 0; i < buffer.count; i++) { - auto re_out = iq_buffer.p[i].real() ; - auto im_out = iq_buffer.p[i].imag() ; - buffer.p[i] = { (int8_t)re_out, (int8_t)im_out }; - } - - spectrum_samples += buffer.count; - if( spectrum_samples >= spectrum_interval_samples ) { - spectrum_samples -= spectrum_interval_samples; - - txprogress_message.progress = bytes_read / 1024; // Inform UI about progress - - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } + /* 4MHz, 2048 samples */ + + if (!configured) return; + + // File data is in C16 format, we need C8 + // File samplerate is 500kHz, we're at 4MHz + // iq_buffer can only be 512 C16 samples (RAM limitation) + // To fill up the 2048-sample C8 buffer, we need: + // 2048 samples * 2 bytes per sample = 4096 bytes + // Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each + // So 256 * 4 bytes per sample (C16) = 1024 bytes from the file + if (stream) { // sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048 + const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count); // *2 (C16), /8 (oversampling) should be == 1024 + bytes_read += stream->read(iq_buffer.p, bytes_to_read); + } + + // Fill and "stretch" + for (size_t i = 0; i < buffer.count; i++) { + auto re_out = iq_buffer.p[i].real(); + auto im_out = iq_buffer.p[i].imag(); + buffer.p[i] = {(int8_t)re_out, (int8_t)im_out}; + } + + spectrum_samples += buffer.count; + if (spectrum_samples >= spectrum_interval_samples) { + spectrum_samples -= spectrum_interval_samples; + + txprogress_message.progress = bytes_read / 1024; // Inform UI about progress + + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + } } void ReplayProcessor::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::SamplerateConfig: - samplerate_config(*reinterpret_cast(message)); - break; - - case Message::ID::ReplayConfig: - configured = false; - bytes_read = 0; - replay_config(*reinterpret_cast(message)); - break; - - // App has prefilled the buffers, we're ready to go now - case Message::ID::FIFOData: - configured = true; - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::SamplerateConfig: + samplerate_config(*reinterpret_cast(message)); + break; + + case Message::ID::ReplayConfig: + configured = false; + bytes_read = 0; + replay_config(*reinterpret_cast(message)); + break; + + // App has prefilled the buffers, we're ready to go now + case Message::ID::FIFOData: + configured = true; + break; + + default: + break; + } } void ReplayProcessor::samplerate_config(const SamplerateConfigMessage& message) { - baseband_fs = message.sample_rate; - baseband_thread.set_sampling_rate(baseband_fs); - spectrum_interval_samples = baseband_fs / spectrum_rate_hz; + baseband_fs = message.sample_rate; + baseband_thread.set_sampling_rate(baseband_fs); + spectrum_interval_samples = baseband_fs / spectrum_rate_hz; } void ReplayProcessor::replay_config(const ReplayConfigMessage& message) { - if( message.config ) { - - stream = std::make_unique(message.config); - - // Tell application that the buffers and FIFO pointers are ready, prefill - shared_memory.application_queue.push(sig_message); - } else { - stream.reset(); - } + if (message.config) { + stream = std::make_unique(message.config); + + // Tell application that the buffers and FIFO pointers are ready, prefill + shared_memory.application_queue.push(sig_message); + } else { + stream.reset(); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_gps_sim.hpp b/firmware/baseband/proc_gps_sim.hpp index 1ea5f03d6..c0f07192e 100644 --- a/firmware/baseband/proc_gps_sim.hpp +++ b/firmware/baseband/proc_gps_sim.hpp @@ -35,44 +35,43 @@ #include class ReplayProcessor : public BasebandProcessor { -public: - ReplayProcessor(); - - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - size_t baseband_fs = 0; - static constexpr auto spectrum_rate_hz = 50.0f; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - std::array iq { }; - const buffer_c8_t iq_buffer { - iq.data(), - iq.size(), - baseband_fs - }; - - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; - - std::unique_ptr stream { }; - - SpectrumCollector channel_spectrum { }; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; - - bool configured { false }; - uint32_t bytes_read { 0 }; - - void samplerate_config(const SamplerateConfigMessage& message); - void replay_config(const ReplayConfigMessage& message); - - TXProgressMessage txprogress_message { }; - RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest }; + public: + ReplayProcessor(); + + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + size_t baseband_fs = 0; + static constexpr auto spectrum_rate_hz = 50.0f; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + std::array iq{}; + const buffer_c8_t iq_buffer{ + iq.data(), + iq.size(), + baseband_fs}; + + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; + + std::unique_ptr stream{}; + + SpectrumCollector channel_spectrum{}; + size_t spectrum_interval_samples = 0; + size_t spectrum_samples = 0; + + bool configured{false}; + uint32_t bytes_read{0}; + + void samplerate_config(const SamplerateConfigMessage& message); + void replay_config(const ReplayConfigMessage& message); + + TXProgressMessage txprogress_message{}; + RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest}; }; -#endif/*__PROC_GPS_SIM_HPP__*/ +#endif /*__PROC_GPS_SIM_HPP__*/ diff --git a/firmware/baseband/proc_jammer.cpp b/firmware/baseband/proc_jammer.cpp index 925267953..893e49035 100644 --- a/firmware/baseband/proc_jammer.cpp +++ b/firmware/baseband/proc_jammer.cpp @@ -28,88 +28,87 @@ #include void JammerProcessor::execute(const buffer_c8_t& buffer) { - if (!configured) return; - - for (size_t i = 0; i < buffer.count; i++) { - - if (!jammer_duration) { - // Find next enabled range - do { - current_range++; - if (current_range == JAMMER_MAX_CH) current_range = 0; - } while (!jammer_channels[current_range].enabled); - - jammer_duration = jammer_channels[current_range].duration; - jammer_bw = jammer_channels[current_range].width / 2; // TODO: Exact value - - // Ask for retune - message.freq = jammer_channels[current_range].center; - message.range = current_range; - shared_memory.application_queue.push(message); - } else { - jammer_duration--; - } - - // Phase noise - if (!period_counter) { - period_counter = noise_period; - - if (noise_type == JammerType::TYPE_FSK) { - sample = (sample + lfsr) >> 1; - } else if (noise_type == JammerType::TYPE_TONE) { - tone_delta = 150000 + (lfsr >> 9); // Approx 100Hz to 6kHz - } else if (noise_type == JammerType::TYPE_SWEEP) { - sample++; // This is like saw wave FM - } - - feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1; - lfsr = (lfsr << 1) | feedback; - if (!lfsr) lfsr = 0x1337; // Shouldn't do this :( - } else { - period_counter--; - } - - if (noise_type == JammerType::TYPE_TONE) { - aphase += tone_delta; - sample = sine_table_i8[(aphase & 0xFF000000) >> 24]; - } - - delta = sample * jammer_bw; - - phase += delta; - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000) >> 24]); - - buffer.p[i] = {re, im}; - } + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + if (!jammer_duration) { + // Find next enabled range + do { + current_range++; + if (current_range == JAMMER_MAX_CH) current_range = 0; + } while (!jammer_channels[current_range].enabled); + + jammer_duration = jammer_channels[current_range].duration; + jammer_bw = jammer_channels[current_range].width / 2; // TODO: Exact value + + // Ask for retune + message.freq = jammer_channels[current_range].center; + message.range = current_range; + shared_memory.application_queue.push(message); + } else { + jammer_duration--; + } + + // Phase noise + if (!period_counter) { + period_counter = noise_period; + + if (noise_type == JammerType::TYPE_FSK) { + sample = (sample + lfsr) >> 1; + } else if (noise_type == JammerType::TYPE_TONE) { + tone_delta = 150000 + (lfsr >> 9); // Approx 100Hz to 6kHz + } else if (noise_type == JammerType::TYPE_SWEEP) { + sample++; // This is like saw wave FM + } + + feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1; + lfsr = (lfsr << 1) | feedback; + if (!lfsr) lfsr = 0x1337; // Shouldn't do this :( + } else { + period_counter--; + } + + if (noise_type == JammerType::TYPE_TONE) { + aphase += tone_delta; + sample = sine_table_i8[(aphase & 0xFF000000) >> 24]; + } + + delta = sample * jammer_bw; + + phase += delta; + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000) >> 24]); + + buffer.p[i] = {re, im}; + } }; void JammerProcessor::on_message(const Message* const msg) { - if (msg->id == Message::ID::JammerConfigure) { - const auto message = *reinterpret_cast(msg); - - if (message.run) { - jammer_channels = (JammerChannel*)shared_memory.bb_data.data; - noise_type = message.type; - noise_period = 3072000 / message.speed; - if (noise_type == JammerType::TYPE_SWEEP) - noise_period >>= 8; - period_counter = 0; - jammer_duration = 0; - current_range = 0; - lfsr = 0xDEAD0012; - - configured = true; - } else { - configured = false; - } - } + if (msg->id == Message::ID::JammerConfigure) { + const auto message = *reinterpret_cast(msg); + + if (message.run) { + jammer_channels = (JammerChannel*)shared_memory.bb_data.data; + noise_type = message.type; + noise_period = 3072000 / message.speed; + if (noise_type == JammerType::TYPE_SWEEP) + noise_period >>= 8; + period_counter = 0; + jammer_duration = 0; + current_range = 0; + lfsr = 0xDEAD0012; + + configured = true; + } else { + configured = false; + } + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_jammer.hpp b/firmware/baseband/proc_jammer.hpp index 15e57fb24..850c4ad5e 100644 --- a/firmware/baseband/proc_jammer.hpp +++ b/firmware/baseband/proc_jammer.hpp @@ -31,29 +31,29 @@ using namespace jammer; class JammerProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; - -private: - bool configured { false }; - - BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - JammerChannel * jammer_channels { }; - - JammerType noise_type { }; - uint32_t tone_delta { 0 }, lfsr { }, feedback { }; - uint32_t noise_period { 0 }, period_counter { 0 }; - uint32_t jammer_duration { 0 }; - uint32_t current_range { 0 }; - int64_t jammer_center { 0 }, jammer_bw { 0 }; - uint32_t sample_count { 0 }; - uint32_t aphase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 }; - int8_t sample { 0 }; - int8_t re { 0 }, im { 0 }; - RetuneMessage message { }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const msg) override; + + private: + bool configured{false}; + + BasebandThread baseband_thread{3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + JammerChannel* jammer_channels{}; + + JammerType noise_type{}; + uint32_t tone_delta{0}, lfsr{}, feedback{}; + uint32_t noise_period{0}, period_counter{0}; + uint32_t jammer_duration{0}; + uint32_t current_range{0}; + int64_t jammer_center{0}, jammer_bw{0}; + uint32_t sample_count{0}; + uint32_t aphase{0}, phase{0}, delta{0}, sphase{0}; + int8_t sample{0}; + int8_t re{0}, im{0}; + RetuneMessage message{}; }; #endif diff --git a/firmware/baseband/proc_mictx.cpp b/firmware/baseband/proc_mictx.cpp index 1ee5b94df..be37c9c9f 100644 --- a/firmware/baseband/proc_mictx.cpp +++ b/firmware/baseband/proc_mictx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,147 +28,146 @@ #include -void MicTXProcessor::execute(const buffer_c8_t& buffer){ - - // This is called at 1536000/2048 = 750Hz - - if (!configured) return; - - audio_input.read_audio_buffer(audio_buffer); - modulator->set_gain_shiftbits_vumeter_beep(audio_gain, audio_shift_bits_s16, play_beep ) ; - modulator->execute(audio_buffer, buffer, configured, beep_index, beep_timer, txprogress_message, level_message, power_acc_count, divider ); // Now "Key Tones & CTCSS" baseband additon inside FM mod. dsp_modulate.cpp" - - /* Original fw 1.3.1 good reference, beep and vu-meter - for (size_t i = 0; i < buffer.count; i++) { - - if (!play_beep) { - sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000 - sample *= audio_gain; - - power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter - - if (power_acc_count) { - power_acc_count--; - } else { - power_acc_count = divider; - level_message.value = power_acc / (divider / 4); // Why ? - shared_memory.application_queue.push(level_message); - power_acc = 0; - } - } else { - if (beep_timer) { - beep_timer--; - } else { - beep_timer = baseband_fs * 0.05; // 50ms - - if (beep_index == BEEP_TONES_NB) { - configured = false; - shared_memory.application_queue.push(txprogress_message); - } else { - beep_gen.configure(beep_deltas[beep_index], 1.0); - beep_index++; - } - } - sample = beep_gen.process(0); // TODO : Pending how to move inside modulate.cpp - } - */ - - /* Original fw 1.3.1 good reference FM moulation version, including "key tones CTCSS" fw 1.3.1 - sample = tone_gen.process(sample); - - // FM - if (configured) { - delta = sample * fm_delta; - - phase += delta; - sphase = phase >> 24; - - re = (sine_table_i8[(sphase + 64) & 255]); - im = (sine_table_i8[sphase]); - } else { - re = 0; - im = 0; - } - - buffer.p[i] = { re, im }; - - } */ +void MicTXProcessor::execute(const buffer_c8_t& buffer) { + // This is called at 1536000/2048 = 750Hz + + if (!configured) return; + + audio_input.read_audio_buffer(audio_buffer); + modulator->set_gain_shiftbits_vumeter_beep(audio_gain, audio_shift_bits_s16, play_beep); + modulator->execute(audio_buffer, buffer, configured, beep_index, beep_timer, txprogress_message, level_message, power_acc_count, divider); // Now "Key Tones & CTCSS" baseband additon inside FM mod. dsp_modulate.cpp" + + /* Original fw 1.3.1 good reference, beep and vu-meter + for (size_t i = 0; i < buffer.count; i++) { + + if (!play_beep) { + sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000 + sample *= audio_gain; + + power_acc += (sample < 0) ? -sample : sample; // Power average for UI vu-meter + + if (power_acc_count) { + power_acc_count--; + } else { + power_acc_count = divider; + level_message.value = power_acc / (divider / 4); // Why ? + shared_memory.application_queue.push(level_message); + power_acc = 0; + } + } else { + if (beep_timer) { + beep_timer--; + } else { + beep_timer = baseband_fs * 0.05; // 50ms + + if (beep_index == BEEP_TONES_NB) { + configured = false; + shared_memory.application_queue.push(txprogress_message); + } else { + beep_gen.configure(beep_deltas[beep_index], 1.0); + beep_index++; + } + } + sample = beep_gen.process(0); // TODO : Pending how to move inside modulate.cpp + } + */ + + /* Original fw 1.3.1 good reference FM moulation version, including "key tones CTCSS" fw 1.3.1 + sample = tone_gen.process(sample); + + // FM + if (configured) { + delta = sample * fm_delta; + + phase += delta; + sphase = phase >> 24; + + re = (sine_table_i8[(sphase + 64) & 255]); + im = (sine_table_i8[sphase]); + } else { + re = 0; + im = 0; + } + + buffer.p[i] = { re, im }; + + } */ } void MicTXProcessor::on_message(const Message* const msg) { - const AudioTXConfigMessage config_message = *reinterpret_cast(msg); - const RequestSignalMessage request_message = *reinterpret_cast(msg); - - switch(msg->id) { - case Message::ID::AudioTXConfig: - if (fm_enabled) { - dsp::modulate::FM *fm = new dsp::modulate::FM(); - - // Config fm_delta private var inside DSP modulate.cpp - fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs)); - - // Config properly the private tone_gen function parameters inside DSP modulate.cpp - fm->set_tone_gen_configure(config_message.tone_key_delta, config_message.tone_key_mix_weight); - modulator = fm; - } - - if (usb_enabled) { - modulator = new dsp::modulate::SSB(); - modulator->set_mode(dsp::modulate::Mode::USB); - } - - if (lsb_enabled) { - modulator = new dsp::modulate::SSB(); - modulator->set_mode(dsp::modulate::Mode::LSB); - } - if (am_enabled) { - modulator = new dsp::modulate::AM(); - modulator->set_mode(dsp::modulate::Mode::AM); - } - if (dsb_enabled) { - modulator = new dsp::modulate::AM(); - modulator->set_mode(dsp::modulate::Mode::DSB); - } - - modulator->set_over(baseband_fs / 24000); // Keep no change. - - am_enabled = config_message.am_enabled; - usb_enabled = config_message.usb_enabled; - lsb_enabled = config_message.lsb_enabled; - dsb_enabled = config_message.dsb_enabled; - if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) { - fm_enabled = true; - } - - audio_gain = config_message.audio_gain; - audio_shift_bits_s16 = config_message.audio_shift_bits_s16; - divider = config_message.divider; - power_acc_count = 0; - - // now this config moved, in the case Message::ID::AudioTXConfig , only FM case. - // tone_gen.configure(config_message.tone_key_delta, config_message.tone_key_mix_weight); - - txprogress_message.done = true; - - play_beep = false; - configured = true; - break; - - case Message::ID::RequestSignal: - if (request_message.signal == RequestSignalMessage::Signal::BeepRequest) { - beep_index = 0; - beep_timer = 0; - play_beep = true; - } - break; - - default: - break; - } + const AudioTXConfigMessage config_message = *reinterpret_cast(msg); + const RequestSignalMessage request_message = *reinterpret_cast(msg); + + switch (msg->id) { + case Message::ID::AudioTXConfig: + if (fm_enabled) { + dsp::modulate::FM* fm = new dsp::modulate::FM(); + + // Config fm_delta private var inside DSP modulate.cpp + fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs)); + + // Config properly the private tone_gen function parameters inside DSP modulate.cpp + fm->set_tone_gen_configure(config_message.tone_key_delta, config_message.tone_key_mix_weight); + modulator = fm; + } + + if (usb_enabled) { + modulator = new dsp::modulate::SSB(); + modulator->set_mode(dsp::modulate::Mode::USB); + } + + if (lsb_enabled) { + modulator = new dsp::modulate::SSB(); + modulator->set_mode(dsp::modulate::Mode::LSB); + } + if (am_enabled) { + modulator = new dsp::modulate::AM(); + modulator->set_mode(dsp::modulate::Mode::AM); + } + if (dsb_enabled) { + modulator = new dsp::modulate::AM(); + modulator->set_mode(dsp::modulate::Mode::DSB); + } + + modulator->set_over(baseband_fs / 24000); // Keep no change. + + am_enabled = config_message.am_enabled; + usb_enabled = config_message.usb_enabled; + lsb_enabled = config_message.lsb_enabled; + dsb_enabled = config_message.dsb_enabled; + if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) { + fm_enabled = true; + } + + audio_gain = config_message.audio_gain; + audio_shift_bits_s16 = config_message.audio_shift_bits_s16; + divider = config_message.divider; + power_acc_count = 0; + + // now this config moved, in the case Message::ID::AudioTXConfig , only FM case. + // tone_gen.configure(config_message.tone_key_delta, config_message.tone_key_mix_weight); + + txprogress_message.done = true; + + play_beep = false; + configured = true; + break; + + case Message::ID::RequestSignal: + if (request_message.signal == RequestSignalMessage::Signal::BeepRequest) { + beep_index = 0; + beep_timer = 0; + play_beep = true; + } + break; + + default: + break; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_mictx.hpp b/firmware/baseband/proc_mictx.hpp index 59a07cb0b..2854a9119 100644 --- a/firmware/baseband/proc_mictx.hpp +++ b/firmware/baseband/proc_mictx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -30,51 +30,50 @@ #include "dsp_modulate.hpp" class MicTXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const msg) override; + + private: + static constexpr size_t baseband_fs = 1536000U; + + bool configured{false}; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + int16_t audio_data[64]; + buffer_s16_t audio_buffer{ + audio_data, + sizeof(int16_t) * 64}; + + AudioInput audio_input{}; + // ToneGen tone_gen { }; moved to dsp_modulate.cpp + // ToneGen beep_gen { }; moved to dsp_modulate.cpp + dsp::modulate::Modulator* modulator = NULL; + + bool am_enabled{false}; + bool fm_enabled{true}; + bool usb_enabled{false}; + bool lsb_enabled{false}; + bool dsb_enabled{false}; -private: - static constexpr size_t baseband_fs = 1536000U; - - bool configured { false }; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - int16_t audio_data[64]; - buffer_s16_t audio_buffer { - audio_data, - sizeof(int16_t) * 64 - }; - - AudioInput audio_input { }; - // ToneGen tone_gen { }; moved to dsp_modulate.cpp - // ToneGen beep_gen { }; moved to dsp_modulate.cpp - dsp::modulate::Modulator *modulator = NULL ; + uint32_t divider{}; + float audio_gain{}; + uint8_t audio_shift_bits_s16{}; // shift bits factor to the captured ADC S16 audio sample. - bool am_enabled { false }; - bool fm_enabled { true }; - bool usb_enabled { false }; - bool lsb_enabled { false }; - bool dsb_enabled { false }; + uint64_t power_acc{0}; + uint32_t power_acc_count{0}; + bool play_beep{false}; + uint32_t fm_delta{0}; + uint32_t phase{0}, sphase{0}; + int32_t sample{0}, delta{}; + uint32_t beep_index{}, beep_timer{}; - uint32_t divider { }; - float audio_gain { }; - uint8_t audio_shift_bits_s16 { } ; // shift bits factor to the captured ADC S16 audio sample. + int8_t re{0}, im{0}; - uint64_t power_acc { 0 }; - uint32_t power_acc_count { 0 }; - bool play_beep { false }; - uint32_t fm_delta { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - int32_t sample { 0 }, delta { }; - uint32_t beep_index { }, beep_timer { }; - - int8_t re { 0 }, im { 0 }; - - AudioLevelReportMessage level_message { }; - TXProgressMessage txprogress_message { }; + AudioLevelReportMessage level_message{}; + TXProgressMessage txprogress_message{}; }; #endif diff --git a/firmware/baseband/proc_nfm_audio.cpp b/firmware/baseband/proc_nfm_audio.cpp index 6d1d06fd2..85251932c 100644 --- a/firmware/baseband/proc_nfm_audio.cpp +++ b/firmware/baseband/proc_nfm_audio.cpp @@ -30,148 +30,147 @@ #include void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) { - //bool new_state; - - if( !configured ) { - return; - } - - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - - channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); - - const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); - - feed_channel_stats(channel_out); - - if (!pitch_rssi_enabled) { - // Normal mode, output demodulated audio - auto audio = demod.execute(channel_out, audio_buffer); - audio_output.write(audio); - - if (ctcss_detect_enabled) { - /* 24kHz int16_t[16] - * -> FIR filter, <300Hz pass, >300Hz stop, gain of 1 - * -> 12kHz int16_t[8] */ - auto audio_ctcss = ctcss_filter.execute(audio, work_audio_buffer); - - // s16 to f32 for hpf - std::array audio_f; - for (size_t i = 0; i < audio_ctcss.count; i++) { - audio_f[i] = audio_ctcss.p[i] * ki; - } - - hpf.execute_in_place(buffer_f32_t { - audio_f.data(), - audio_ctcss.count, - audio_ctcss.sampling_rate - }); - - // Zero-crossing detection - for (size_t c = 0; c < audio_ctcss.count; c++) { - cur_sample = audio_f[c]; - if (cur_sample * prev_sample < 0.0) { - z_acc += z_timer; - z_timer = 0; - z_count++; - } else - z_timer++; - prev_sample = cur_sample; - } - - if (z_count >= 30) { - ctcss_message.value = (100 * 12000 / 2 * z_count) / z_acc; - shared_memory.application_queue.push(ctcss_message); - z_count = 0; - z_acc = 0; - } - } - } else { - // Direction-finding mode; output tone with pitch related to RSSI - for (size_t c = 0; c < 16; c++) { - tone_buffer.p[c] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128; - tone_phase += tone_delta; - } - - audio_output.write(tone_buffer); - - /*new_state = audio_output.is_squelched(); - - if (new_state && !old_state) - shared_memory.application_queue.push(sig_message); - - old_state = new_state;*/ - } + // bool new_state; + + if (!configured) { + return; + } + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + + channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); + + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); + + feed_channel_stats(channel_out); + + if (!pitch_rssi_enabled) { + // Normal mode, output demodulated audio + auto audio = demod.execute(channel_out, audio_buffer); + audio_output.write(audio); + + if (ctcss_detect_enabled) { + /* 24kHz int16_t[16] + * -> FIR filter, <300Hz pass, >300Hz stop, gain of 1 + * -> 12kHz int16_t[8] */ + auto audio_ctcss = ctcss_filter.execute(audio, work_audio_buffer); + + // s16 to f32 for hpf + std::array audio_f; + for (size_t i = 0; i < audio_ctcss.count; i++) { + audio_f[i] = audio_ctcss.p[i] * ki; + } + + hpf.execute_in_place(buffer_f32_t{ + audio_f.data(), + audio_ctcss.count, + audio_ctcss.sampling_rate}); + + // Zero-crossing detection + for (size_t c = 0; c < audio_ctcss.count; c++) { + cur_sample = audio_f[c]; + if (cur_sample * prev_sample < 0.0) { + z_acc += z_timer; + z_timer = 0; + z_count++; + } else + z_timer++; + prev_sample = cur_sample; + } + + if (z_count >= 30) { + ctcss_message.value = (100 * 12000 / 2 * z_count) / z_acc; + shared_memory.application_queue.push(ctcss_message); + z_count = 0; + z_acc = 0; + } + } + } else { + // Direction-finding mode; output tone with pitch related to RSSI + for (size_t c = 0; c < 16; c++) { + tone_buffer.p[c] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128; + tone_phase += tone_delta; + } + + audio_output.write(tone_buffer); + + /*new_state = audio_output.is_squelched(); + + if (new_state && !old_state) + shared_memory.application_queue.push(sig_message); + + old_state = new_state;*/ + } } void NarrowbandFMAudio::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::NBFMConfigure: - configure(*reinterpret_cast(message)); - break; - - case Message::ID::CaptureConfig: - capture_config(*reinterpret_cast(message)); - break; - - case Message::ID::PitchRSSIConfigure: - pitch_rssi_config(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::NBFMConfigure: + configure(*reinterpret_cast(message)); + break; + + case Message::ID::CaptureConfig: + capture_config(*reinterpret_cast(message)); + break; + + case Message::ID::PitchRSSIConfigure: + pitch_rssi_config(*reinterpret_cast(message)); + break; + + default: + break; + } } void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) { - constexpr size_t decim_0_input_fs = baseband_fs; - constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; - - constexpr size_t decim_1_input_fs = decim_0_output_fs; - constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - - constexpr size_t channel_filter_input_fs = decim_1_output_fs; - const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation; - - const size_t demod_input_fs = channel_filter_output_fs; - - decim_0.configure(message.decim_0_filter.taps, 33554432); - decim_1.configure(message.decim_1_filter.taps, 131072); - channel_filter.configure(message.channel_filter.taps, message.channel_decimation); - demod.configure(demod_input_fs, message.deviation); - channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs; - channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs; - channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs; - channel_spectrum.set_decimation_factor(1.0f); - audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0); - - hpf.configure(audio_24k_hpf_30hz_config); - ctcss_filter.configure(taps_64_lp_025_025.taps); - - configured = true; + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_1_output_fs; + const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation; + + const size_t demod_input_fs = channel_filter_output_fs; + + decim_0.configure(message.decim_0_filter.taps, 33554432); + decim_1.configure(message.decim_1_filter.taps, 131072); + channel_filter.configure(message.channel_filter.taps, message.channel_decimation); + demod.configure(demod_input_fs, message.deviation); + channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs; + channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs; + channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs; + channel_spectrum.set_decimation_factor(1.0f); + audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0); + + hpf.configure(audio_24k_hpf_30hz_config); + ctcss_filter.configure(taps_64_lp_025_025.taps); + + configured = true; } void NarrowbandFMAudio::pitch_rssi_config(const PitchRSSIConfigureMessage& message) { - pitch_rssi_enabled = message.enabled; - tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000); + pitch_rssi_enabled = message.enabled; + tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000); } void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) { - if( message.config ) { - audio_output.set_stream(std::make_unique(message.config)); - } else { - audio_output.set_stream(nullptr); - } + if (message.config) { + audio_output.set_stream(std::make_unique(message.config)); + } else { + audio_output.set_stream(nullptr); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_nfm_audio.hpp b/firmware/baseband/proc_nfm_audio.hpp index 0c78e84e0..e93d7aaf2 100644 --- a/firmware/baseband/proc_nfm_audio.hpp +++ b/firmware/baseband/proc_nfm_audio.hpp @@ -37,73 +37,69 @@ #include class NarrowbandFMAudio : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 3072000; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - const buffer_s16_t work_audio_buffer { - (int16_t*)dst.data(), - sizeof(dst) / sizeof(int16_t) - }; - - std::array audio { }; - const buffer_s16_t audio_buffer { - (int16_t*)audio.data(), - sizeof(audio) / sizeof(int16_t) - }; - - std::array tone { }; - const buffer_s16_t tone_buffer { - (int16_t*)tone.data(), - sizeof(tone) / sizeof(int16_t) - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { }; - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; - - // For CTCSS decoding - dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter { }; - IIRBiquadFilter hpf { }; - - dsp::demodulate::FM demod { }; - - AudioOutput audio_output { }; - - SpectrumCollector channel_spectrum { }; - - uint32_t tone_phase { 0 }; - uint32_t tone_delta { 0 }; - bool pitch_rssi_enabled { false }; - - float cur_sample { }, prev_sample { }; - uint32_t z_acc { 0}, z_timer { 0 }, z_count { 0 }; - bool ctcss_detect_enabled { true }; - static constexpr float k = 32768.0f; - static constexpr float ki = 1.0f / k; - - bool configured { false }; - void pitch_rssi_config(const PitchRSSIConfigureMessage& message); - void configure(const NBFMConfigureMessage& message); - void capture_config(const CaptureConfigMessage& message); - - //RequestSignalMessage sig_message { RequestSignalMessage::Signal::Squelched }; - CodedSquelchMessage ctcss_message { 0 }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 3072000; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + const buffer_s16_t work_audio_buffer{ + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t)}; + + std::array audio{}; + const buffer_s16_t audio_buffer{ + (int16_t*)audio.data(), + sizeof(audio) / sizeof(int16_t)}; + + std::array tone{}; + const buffer_s16_t tone_buffer{ + (int16_t*)tone.data(), + sizeof(tone) / sizeof(int16_t)}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; + + // For CTCSS decoding + dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter{}; + IIRBiquadFilter hpf{}; + + dsp::demodulate::FM demod{}; + + AudioOutput audio_output{}; + + SpectrumCollector channel_spectrum{}; + + uint32_t tone_phase{0}; + uint32_t tone_delta{0}; + bool pitch_rssi_enabled{false}; + + float cur_sample{}, prev_sample{}; + uint32_t z_acc{0}, z_timer{0}, z_count{0}; + bool ctcss_detect_enabled{true}; + static constexpr float k = 32768.0f; + static constexpr float ki = 1.0f / k; + + bool configured{false}; + void pitch_rssi_config(const PitchRSSIConfigureMessage& message); + void configure(const NBFMConfigureMessage& message); + void capture_config(const CaptureConfigMessage& message); + + // RequestSignalMessage sig_message { RequestSignalMessage::Signal::Squelched }; + CodedSquelchMessage ctcss_message{0}; }; -#endif/*__PROC_NFM_AUDIO_H__*/ +#endif /*__PROC_NFM_AUDIO_H__*/ diff --git a/firmware/baseband/proc_noop.cpp b/firmware/baseband/proc_noop.cpp index d6443f238..acd06bb52 100644 --- a/firmware/baseband/proc_noop.cpp +++ b/firmware/baseband/proc_noop.cpp @@ -26,13 +26,13 @@ #include void NOOPProcessor::execute(const buffer_c8_t& buffer) { - for (size_t i = 0; i() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_noop.hpp b/firmware/baseband/proc_noop.hpp index 97efe0c4a..c71232e35 100644 --- a/firmware/baseband/proc_noop.hpp +++ b/firmware/baseband/proc_noop.hpp @@ -27,11 +27,11 @@ #include "baseband_thread.hpp" class NOOPProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; + public: + void execute(const buffer_c8_t& buffer) override; -private: - BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; + private: + BasebandThread baseband_thread{1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; }; #endif diff --git a/firmware/baseband/proc_nrfrx.cpp b/firmware/baseband/proc_nrfrx.cpp index b27eb148f..89dfc9c70 100644 --- a/firmware/baseband/proc_nrfrx.cpp +++ b/firmware/baseband/proc_nrfrx.cpp @@ -27,257 +27,231 @@ #include "event_m4.hpp" void NRFRxProcessor::execute(const buffer_c8_t& buffer) { - if (!configured) return; - - // FM demodulation - - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - feed_channel_stats(decim_0_out); - - auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer); - // Audio signal processing - for (size_t c = 0; c < audio_oversampled.count; c++) { - int g_srate = 4; //4 for 250KPS - //int g_srate = 1; //1 for 1MPS, not working yet - int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct. - rb_head++; - rb_head=(rb_head)%RB_SIZE; - - rb_buf[rb_head] = current_sample; - - skipSamples = skipSamples - 1; - - if (skipSamples<1) - { - int32_t threshold_tmp=0; - for (int c=0;c<8*g_srate;c++) - { - threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE]; - } - - g_threshold = (int32_t)threshold_tmp/(8*g_srate); - - int transitions=0; - if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold) - { - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) - transitions = transitions + 1; - } - } - else - { - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) - transitions = transitions + 1; - } - } - - bool packet_detected=false; - //if ( transitions==4 && abs(g_threshold)<15500) - if ( transitions==4 && abs(g_threshold)<15500) - { - int packet_length = 0; - uint8_t tmp_buf[10]; - uint8_t packet_data[500]; - uint8_t packet_packed[50]; - uint16_t pcf; - uint32_t packet_crc; - uint32_t calced_crc; - uint64_t packet_addr_l; - - /* extract address */ - packet_addr_l=0; - - for (int t=0;t<5;t++) - { - bool current_bit; - uint8_t byte=0; - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - tmp_buf[t]=byte; - } - - for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8; - - //channel_number = 26; - - - /* extract pcf */ - for (int t=0;t<2;t++) - { - bool current_bit; - uint8_t byte=0; - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - tmp_buf[t]=byte; - } - - pcf = tmp_buf[0]<<8 | tmp_buf[1]; - pcf >>=7; - - /* extract packet length, avoid excessive length packets */ - if(packet_length == 0) - packet_length=(int)pcf>>3; - if (packet_length>32) - packet_detected = false; - - /* extract data */ - for (int t=0;t g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - packet_data[t]=byte; - } - - /* Prepare packed bit stream for CRC calculation */ - uint64_t packet_header=packet_addr_l; - packet_header<<=9; - packet_header|=pcf; - for (int c=0;c<7;c++){ - packet_packed[c]=(packet_header>>((6-c)*8))&0xFF; - } - - for (int c=0;c 0; i >>= 1) - { - bit = crc & 0x8000; - if (cc & i) - { - bit = !bit; - } - crc <<= 1; - if (bit) - { - crc ^= 0x1021; - } - } - crc &= 0xffff; - } - calced_crc = (uint16_t)(crc & 0xffff); - - /* extract crc */ - for (int t=0;t<2;t++) - { - bool current_bit; - uint8_t byte=0; - for (int c=0;c<8;c++) - { - if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold) - current_bit = true; - else - current_bit = false; - byte |= current_bit << (7-c); - } - tmp_buf[t]=byte; - } - packet_crc = tmp_buf[0]<<8 | tmp_buf[1]; - - /* NRF24L01+ packet found, dump information */ - //if (packet_addr_l==0xE7E7E7E7) - if (packet_crc==calced_crc) - { - data_message.is_data = false; - data_message.value = 'A'; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = packet_addr_l; - shared_memory.application_queue.push(data_message); - - for (int c=0;c<7;c++) - { - data_message.is_data = true; - data_message.value = packet_addr_l >> 8; - shared_memory.application_queue.push(data_message); - } - /*data_message.is_data = true; - data_message.value = packet_addr_l; - shared_memory.application_queue.push(data_message); - - data_message.is_data = true; - data_message.value = packet_addr_l >> 8; - shared_memory.application_queue.push(data_message);*/ - - data_message.is_data = false; - data_message.value = 'B'; - shared_memory.application_queue.push(data_message); - - for (int c=0;c g_threshold) { + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + c * g_srate) % RB_SIZE] > rb_buf[(rb_head + (c + 1) * g_srate) % RB_SIZE]) + transitions = transitions + 1; + } + } else { + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + c * g_srate) % RB_SIZE] < rb_buf[(rb_head + (c + 1) * g_srate) % RB_SIZE]) + transitions = transitions + 1; + } + } + + bool packet_detected = false; + // if ( transitions==4 && abs(g_threshold)<15500) + if (transitions == 4 && abs(g_threshold) < 15500) { + int packet_length = 0; + uint8_t tmp_buf[10]; + uint8_t packet_data[500]; + uint8_t packet_packed[50]; + uint16_t pcf; + uint32_t packet_crc; + uint32_t calced_crc; + uint64_t packet_addr_l; + + /* extract address */ + packet_addr_l = 0; + + for (int t = 0; t < 5; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + (1 * 8 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + tmp_buf[t] = byte; + } + + for (int t = 0; t < 5; t++) packet_addr_l |= ((uint64_t)tmp_buf[t]) << (4 - t) * 8; + + // channel_number = 26; + + /* extract pcf */ + for (int t = 0; t < 2; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + (6 * 8 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + tmp_buf[t] = byte; + } + + pcf = tmp_buf[0] << 8 | tmp_buf[1]; + pcf >>= 7; + + /* extract packet length, avoid excessive length packets */ + if (packet_length == 0) + packet_length = (int)pcf >> 3; + if (packet_length > 32) + packet_detected = false; + + /* extract data */ + for (int t = 0; t < packet_length; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + (6 * 8 + 9 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + packet_data[t] = byte; + } + + /* Prepare packed bit stream for CRC calculation */ + uint64_t packet_header = packet_addr_l; + packet_header <<= 9; + packet_header |= pcf; + for (int c = 0; c < 7; c++) { + packet_packed[c] = (packet_header >> ((6 - c) * 8)) & 0xFF; + } + + for (int c = 0; c < packet_length; c++) { + packet_packed[c + 7] = packet_data[c]; + } + + /* calculate packet crc */ + const uint8_t* data = packet_packed; + size_t data_len = 7 + packet_length; + bool bit; + uint8_t cc; + uint_fast16_t crc = 0x3C18; + while (data_len--) { + cc = *data++; + for (uint8_t i = 0x80; i > 0; i >>= 1) { + bit = crc & 0x8000; + if (cc & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x1021; + } + } + crc &= 0xffff; + } + calced_crc = (uint16_t)(crc & 0xffff); + + /* extract crc */ + for (int t = 0; t < 2; t++) { + bool current_bit; + uint8_t byte = 0; + for (int c = 0; c < 8; c++) { + if (rb_buf[(rb_head + ((6 + packet_length) * 8 + 9 + t * 8 + c) * g_srate) % RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7 - c); + } + tmp_buf[t] = byte; + } + packet_crc = tmp_buf[0] << 8 | tmp_buf[1]; + + /* NRF24L01+ packet found, dump information */ + // if (packet_addr_l==0xE7E7E7E7) + if (packet_crc == calced_crc) { + data_message.is_data = false; + data_message.value = 'A'; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = packet_addr_l; + shared_memory.application_queue.push(data_message); + + for (int c = 0; c < 7; c++) { + data_message.is_data = true; + data_message.value = packet_addr_l >> 8; + shared_memory.application_queue.push(data_message); + } + /*data_message.is_data = true; + data_message.value = packet_addr_l; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = packet_addr_l >> 8; + shared_memory.application_queue.push(data_message);*/ + + data_message.is_data = false; + data_message.value = 'B'; + shared_memory.application_queue.push(data_message); + + for (int c = 0; c < packet_length; c++) { + data_message.is_data = true; + data_message.value = packet_data[c]; + shared_memory.application_queue.push(data_message); + } + + data_message.is_data = false; + data_message.value = 'C'; + shared_memory.application_queue.push(data_message); + + packet_detected = true; + } else + packet_detected = false; + } + + if (packet_detected) { + skipSamples = 20; + } + } + } } void NRFRxProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::NRFRxConfigure) - configure(*reinterpret_cast(message)); + if (message->id == Message::ID::NRFRxConfigure) + configure(*reinterpret_cast(message)); } -void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) { - (void)message; //avoir unused warning - decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); - decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); - demod.configure(audio_fs, 5000); +void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) { + (void)message; // avoir unused warning + decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); + decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); + demod.configure(audio_fs, 5000); - configured = true; + configured = true; } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_nrfrx.hpp b/firmware/baseband/proc_nrfrx.hpp index bfd9e348b..e909d2680 100644 --- a/firmware/baseband/proc_nrfrx.hpp +++ b/firmware/baseband/proc_nrfrx.hpp @@ -37,60 +37,55 @@ #include "message.hpp" class NRFRxProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 4000000; - static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - std::array spectrum { }; - const buffer_c16_t spectrum_buffer { - spectrum.data(), - spectrum.size() - }; - - const buffer_s16_t work_audio_buffer { - (int16_t*)dst.data(), - sizeof(dst) / sizeof(int16_t) - }; - - - // Array size ok down to 375 bauds (24000 / 375) - std::array delay_line { 0 }; - std::array rb_buf { 0 }; - - /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; - dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; - - dsp::demodulate::FM demod { }; - int rb_head {-1}; - int32_t g_threshold {0}; - //uint8_t g_srate {8}; - uint8_t channel_number {38}; - int skipSamples {1000}; - int RB_SIZE {1000}; - - bool configured { false }; - - - void configure(const NRFRxConfigureMessage& message); - - AFSKDataMessage data_message { false, 0 }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 4000000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + std::array spectrum{}; + const buffer_c16_t spectrum_buffer{ + spectrum.data(), + spectrum.size()}; + + const buffer_s16_t work_audio_buffer{ + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t)}; + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line{0}; + std::array rb_buf{0}; + + /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + + dsp::demodulate::FM demod{}; + int rb_head{-1}; + int32_t g_threshold{0}; + // uint8_t g_srate {8}; + uint8_t channel_number{38}; + int skipSamples{1000}; + int RB_SIZE{1000}; + + bool configured{false}; + + void configure(const NRFRxConfigureMessage& message); + + AFSKDataMessage data_message{false, 0}; }; -#endif/*__PROC_NRFRX_H__*/ +#endif /*__PROC_NRFRX_H__*/ diff --git a/firmware/baseband/proc_ook.cpp b/firmware/baseband/proc_ook.cpp index 821fc7b36..863982982 100644 --- a/firmware/baseband/proc_ook.cpp +++ b/firmware/baseband/proc_ook.cpp @@ -28,234 +28,234 @@ #include inline void OOKProcessor::write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i) { - int8_t re, im; + int8_t re, im; - if (bit_value) { - phase = (phase + 200); // What ? - sphase = phase + (64 << 18); + if (bit_value) { + phase = (phase + 200); // What ? + sphase = phase + (64 << 18); - re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]); - im = (sine_table_i8[(phase & 0x03FC0000) >> 18]); - } else { - re = 0; - im = 0; - } + re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]); + im = (sine_table_i8[(phase & 0x03FC0000) >> 18]); + } else { + re = 0; + im = 0; + } - buffer.p[i] = {re, im}; + buffer.p[i] = {re, im}; } bool OOKProcessor::scan_init(unsigned int order) { - if (order > MAX_DE_BRUIJN_ORDER) - return false; + if (order > MAX_DE_BRUIJN_ORDER) + return false; - scan_done = false; - scan_progress = 0; + scan_done = false; + scan_progress = 0; - k = 0; - idx = 1; + k = 0; + idx = 1; - duval_symbols = 2; // 2 for binary, 3 for ternary encoders - duval_length = 0; - duval_bit = 0; - duval_sample_bit = 0; - duval_symbol = 0; + duval_symbols = 2; // 2 for binary, 3 for ternary encoders + duval_length = 0; + duval_bit = 0; + duval_sample_bit = 0; + duval_symbol = 0; - memset(v, 0, sizeof(v)); - return true; + memset(v, 0, sizeof(v)); + return true; } bool OOKProcessor::scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr) { - // encode data: 0 = 1000, 1 = 1110 - // @TODO: make this user-configurable - const uint8_t sym[] = { 0b0001, 0b0111 }; - constexpr auto symbol_length = 4; - - // iterate over every symbol in the sequence and convert it to bits with required bitrate - for (; duval_bit < duval_length; duval_bit++) { - auto val = v_tmp[duval_bit]; - for (; duval_symbol < symbol_length; duval_symbol++) { - auto s = sym[val] & (1 << duval_symbol); - for (; duval_sample_bit < samples_per_bit; duval_sample_bit++) { - if (buf_ptr >= buffer.count) { - // buffer is full - continue next time - txprogress_message.done = false; - txprogress_message.progress = scan_progress++; - shared_memory.application_queue.push(txprogress_message); - return false; - } - write_sample(buffer, s, buf_ptr++); - } - duval_sample_bit = 0; - } - duval_symbol = 0; - } - duval_bit = 0; - return true; + // encode data: 0 = 1000, 1 = 1110 + // @TODO: make this user-configurable + const uint8_t sym[] = {0b0001, 0b0111}; + constexpr auto symbol_length = 4; + + // iterate over every symbol in the sequence and convert it to bits with required bitrate + for (; duval_bit < duval_length; duval_bit++) { + auto val = v_tmp[duval_bit]; + for (; duval_symbol < symbol_length; duval_symbol++) { + auto s = sym[val] & (1 << duval_symbol); + for (; duval_sample_bit < samples_per_bit; duval_sample_bit++) { + if (buf_ptr >= buffer.count) { + // buffer is full - continue next time + txprogress_message.done = false; + txprogress_message.progress = scan_progress++; + shared_memory.application_queue.push(txprogress_message); + return false; + } + write_sample(buffer, s, buf_ptr++); + } + duval_sample_bit = 0; + } + duval_symbol = 0; + } + duval_bit = 0; + return true; } inline size_t OOKProcessor::duval_algo_step() { - size_t buf_ptr = 0; - const unsigned int w = de_bruijn_length; + size_t buf_ptr = 0; + const unsigned int w = de_bruijn_length; - // Duval's algorithm for generating de Bruijn sequence - while (idx) { - if (w % idx == 0) { - for (unsigned int k = 0; k < idx; k++) - v_tmp[buf_ptr++] = v[k]; - k = 0; - } + // Duval's algorithm for generating de Bruijn sequence + while (idx) { + if (w % idx == 0) { + for (unsigned int k = 0; k < idx; k++) + v_tmp[buf_ptr++] = v[k]; + k = 0; + } - for (unsigned int j = 0; j < w - idx; j++) - v[idx + j] = v[j]; + for (unsigned int j = 0; j < w - idx; j++) + v[idx + j] = v[j]; - for (idx = w; (idx > 0) && (v[idx - 1] >= duval_symbols - 1); idx--) ; + for (idx = w; (idx > 0) && (v[idx - 1] >= duval_symbols - 1); idx--) + ; - if (idx) - v[idx - 1]++; + if (idx) + v[idx - 1]++; - if (buf_ptr) { - // we fill at most de_bruijn_length number of elements - return buf_ptr; - } - } + if (buf_ptr) { + // we fill at most de_bruijn_length number of elements + return buf_ptr; + } + } - return 0; + return 0; } void OOKProcessor::scan_process(const buffer_c8_t& buffer) { - size_t buf_ptr = 0; - - // transmit any leftover bits from previous step - if (!scan_encode(buffer, buf_ptr)) - return; - - while (1) { - // calculate next chunk of deBruijn sequence - duval_length = duval_algo_step(); - - if (duval_length == 0) { - // last chunk - done - if (!scan_done) { - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - } - scan_done = 1; - - // clear the remaining buffer in case we have any bytes left - for (size_t i = buf_ptr; i < buffer.count; i++) - buffer.p[i] = { 0, 0 }; - - break; - } - - duval_bit = 0; - duval_sample_bit = 0; - duval_symbol = 0; - - // encode the sequence into required format - if (!scan_encode(buffer, buf_ptr)) - break; - } + size_t buf_ptr = 0; + + // transmit any leftover bits from previous step + if (!scan_encode(buffer, buf_ptr)) + return; + + while (1) { + // calculate next chunk of deBruijn sequence + duval_length = duval_algo_step(); + + if (duval_length == 0) { + // last chunk - done + if (!scan_done) { + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + } + scan_done = 1; + + // clear the remaining buffer in case we have any bytes left + for (size_t i = buf_ptr; i < buffer.count; i++) + buffer.p[i] = {0, 0}; + + break; + } + + duval_bit = 0; + duval_sample_bit = 0; + duval_symbol = 0; + + // encode the sequence into required format + if (!scan_encode(buffer, buf_ptr)) + break; + } } void OOKProcessor::execute(const buffer_c8_t& buffer) { - // This is called at 2.28M/2048 = 1113Hz - - if (!configured) return; - - if (de_bruijn_length) { - scan_process(buffer); - return; - } - - for (size_t i = 0; i < buffer.count; i++) { - - // Synthesis at 2.28M/10 = 228kHz - if (!s) { - s = 10 - 1; - if (sample_count >= samples_per_bit) { - if (configured) { - if (bit_pos >= length) { - // End of data - if (pause_counter == 0) { - pause_counter = pause; - cur_bit = 0; - } else if (pause_counter == 1) { - if (repeat_counter < repeat) { - // Repeat - cur_bit = shared_memory.bb_data.data[0] & 0x80; - txprogress_message.progress = repeat_counter + 1; - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - bit_pos = 1; - repeat_counter++; - } else { - // Stop - cur_bit = 0; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - configured = false; - } - pause_counter = 0; - } else { - pause_counter--; - } - } else { - cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80; - bit_pos++; - } - } - - sample_count = 0; - } else { - sample_count++; - } - } else { - s--; - } - - write_sample(buffer, cur_bit, i); - } + // This is called at 2.28M/2048 = 1113Hz + + if (!configured) return; + + if (de_bruijn_length) { + scan_process(buffer); + return; + } + + for (size_t i = 0; i < buffer.count; i++) { + // Synthesis at 2.28M/10 = 228kHz + if (!s) { + s = 10 - 1; + if (sample_count >= samples_per_bit) { + if (configured) { + if (bit_pos >= length) { + // End of data + if (pause_counter == 0) { + pause_counter = pause; + cur_bit = 0; + } else if (pause_counter == 1) { + if (repeat_counter < repeat) { + // Repeat + cur_bit = shared_memory.bb_data.data[0] & 0x80; + txprogress_message.progress = repeat_counter + 1; + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + bit_pos = 1; + repeat_counter++; + } else { + // Stop + cur_bit = 0; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + configured = false; + } + pause_counter = 0; + } else { + pause_counter--; + } + } else { + cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80; + bit_pos++; + } + } + + sample_count = 0; + } else { + sample_count++; + } + } else { + s--; + } + + write_sample(buffer, cur_bit, i); + } } void OOKProcessor::on_message(const Message* const p) { - const auto message = *reinterpret_cast(p); - - if (message.id == Message::ID::OOKConfigure) { - configured = false; - - repeat = message.repeat - 1; - length = message.stream_length; - pause = message.pause_symbols + 1; - de_bruijn_length = message.de_bruijn_length; - samples_per_bit = message.samples_per_bit; - - if (!length && !samples_per_bit) { - // shutdown - return; - } - - if (de_bruijn_length) { - if (!scan_init(de_bruijn_length)) - return; - } else { - samples_per_bit /= 10; - } - - pause_counter = 0; - s = 0; - sample_count = samples_per_bit; - repeat_counter = 0; - bit_pos = 0; - cur_bit = 0; - txprogress_message.progress = 0; - txprogress_message.done = false; - configured = true; - } + const auto message = *reinterpret_cast(p); + + if (message.id == Message::ID::OOKConfigure) { + configured = false; + + repeat = message.repeat - 1; + length = message.stream_length; + pause = message.pause_symbols + 1; + de_bruijn_length = message.de_bruijn_length; + samples_per_bit = message.samples_per_bit; + + if (!length && !samples_per_bit) { + // shutdown + return; + } + + if (de_bruijn_length) { + if (!scan_init(de_bruijn_length)) + return; + } else { + samples_per_bit /= 10; + } + + pause_counter = 0; + s = 0; + sample_count = samples_per_bit; + repeat_counter = 0; + bit_pos = 0; + cur_bit = 0; + txprogress_message.progress = 0; + txprogress_message.done = false; + configured = true; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_ook.hpp b/firmware/baseband/proc_ook.hpp index eda493d2d..24db78c2c 100644 --- a/firmware/baseband/proc_ook.hpp +++ b/firmware/baseband/proc_ook.hpp @@ -27,51 +27,51 @@ #include "baseband_thread.hpp" class OOKProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; + public: + void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const p) override; + void on_message(const Message* const p) override; -private: - bool configured = false; + private: + bool configured = false; - BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; + BasebandThread baseband_thread{2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; - uint32_t samples_per_bit { 0 }; - uint8_t repeat { 0 }; - uint32_t length { 0 }; - uint32_t pause { 0 }; - uint8_t de_bruijn_length { 0 }; + uint32_t samples_per_bit{0}; + uint8_t repeat{0}; + uint32_t length{0}; + uint32_t pause{0}; + uint8_t de_bruijn_length{0}; - uint32_t pause_counter { 0 }; - uint8_t repeat_counter { 0 }; - uint8_t s { 0 }; - uint16_t bit_pos { 0 }; - uint8_t cur_bit { 0 }; - uint32_t sample_count { 0 }; - uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 }; - int32_t tone_sample { 0 }, sig { 0 }, frq { 0 }; + uint32_t pause_counter{0}; + uint8_t repeat_counter{0}; + uint8_t s{0}; + uint16_t bit_pos{0}; + uint8_t cur_bit{0}; + uint32_t sample_count{0}; + uint32_t tone_phase{0}, phase{0}, sphase{0}; + int32_t tone_sample{0}, sig{0}, frq{0}; - TXProgressMessage txprogress_message { }; + TXProgressMessage txprogress_message{}; - static constexpr auto MAX_DE_BRUIJN_ORDER = 24; - uint8_t v[MAX_DE_BRUIJN_ORDER]; - uint8_t v_tmp[MAX_DE_BRUIJN_ORDER]; - unsigned int idx { 0 }; - unsigned int k { 0 }; - unsigned int duval_symbols { 0 }; - unsigned int duval_length { 0 }; - unsigned int duval_bit { 0 }; - unsigned int duval_sample_bit { 0 }; - unsigned int duval_symbol { 0 }; - size_t scan_progress{ 0 }; - uint8_t scan_done { true }; + static constexpr auto MAX_DE_BRUIJN_ORDER = 24; + uint8_t v[MAX_DE_BRUIJN_ORDER]; + uint8_t v_tmp[MAX_DE_BRUIJN_ORDER]; + unsigned int idx{0}; + unsigned int k{0}; + unsigned int duval_symbols{0}; + unsigned int duval_length{0}; + unsigned int duval_bit{0}; + unsigned int duval_sample_bit{0}; + unsigned int duval_symbol{0}; + size_t scan_progress{0}; + uint8_t scan_done{true}; - size_t duval_algo_step(); - void scan_process(const buffer_c8_t& buffer); - bool scan_init(unsigned int order); - bool scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr); - void write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i); + size_t duval_algo_step(); + void scan_process(const buffer_c8_t& buffer); + bool scan_init(unsigned int order); + bool scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr); + void write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i); }; #endif diff --git a/firmware/baseband/proc_pocsag.cpp b/firmware/baseband/proc_pocsag.cpp index 4f45fb2b7..6b5b492b4 100644 --- a/firmware/baseband/proc_pocsag.cpp +++ b/firmware/baseband/proc_pocsag.cpp @@ -28,87 +28,80 @@ #include #include -#include // std::max +#include // std::max #include - void POCSAGProcessor::execute(const buffer_c8_t& buffer) { - // This is called at 1500Hz - - if (!configured) return; - - // Get 24kHz audio - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); - auto audio = demod.execute(channel_out, audio_buffer); - smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate - audio_output.write(audio); - - processDemodulatedSamples(audio.p, 16); - extractFrames(); + // This is called at 1500Hz + + if (!configured) return; + + // Get 24kHz audio + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); + auto audio = demod.execute(channel_out, audio_buffer); + smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate + audio_output.write(audio); + processDemodulatedSamples(audio.p, 16); + extractFrames(); } // ==================================================================== // // ==================================================================== -int POCSAGProcessor::OnDataWord(uint32_t word, int pos) -{ - packet.set(pos, word); - return 0; +int POCSAGProcessor::OnDataWord(uint32_t word, int pos) { + packet.set(pos, word); + return 0; } // ==================================================================== // // ==================================================================== -int POCSAGProcessor::OnDataFrame(int len, int baud) -{ - if (len > 0) - { - packet.set_bitrate(baud); - packet.set_flag(pocsag::PacketFlag::NORMAL); - packet.set_timestamp(Timestamp::now()); - const POCSAGPacketMessage message(packet); - shared_memory.application_queue.push(message); - } - return 0; +int POCSAGProcessor::OnDataFrame(int len, int baud) { + if (len > 0) { + packet.set_bitrate(baud); + packet.set_flag(pocsag::PacketFlag::NORMAL); + packet.set_timestamp(Timestamp::now()); + const POCSAGPacketMessage message(packet); + shared_memory.application_queue.push(message); + } + return 0; } void POCSAGProcessor::on_message(const Message* const message) { - if (message->id == Message::ID::POCSAGConfigure) - configure(); + if (message->id == Message::ID::POCSAGConfigure) + configure(); } void POCSAGProcessor::configure() { - constexpr size_t decim_0_input_fs = baseband_fs; - constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; - - constexpr size_t decim_1_input_fs = decim_0_output_fs; - constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - - constexpr size_t channel_filter_input_fs = decim_1_output_fs; - const size_t channel_filter_output_fs = channel_filter_input_fs / 2; - - const size_t demod_input_fs = channel_filter_output_fs; - - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); - channel_filter.configure(taps_11k0_channel.taps, 2); - demod.configure(demod_input_fs, 4500); - // Smoothing should be roughly sample rate over max baud - // 24k / 3.2k is 7.5 - smooth.SetSize(8); - audio_output.configure(false); - - // Set up the frame extraction, limits of baud - setFrameExtractParams(demod_input_fs, 4000, 300, 32); - - // Mark the class as ready to accept data - configured = true; -} + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_1_output_fs; + const size_t channel_filter_output_fs = channel_filter_input_fs / 2; + const size_t demod_input_fs = channel_filter_output_fs; + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); + channel_filter.configure(taps_11k0_channel.taps, 2); + demod.configure(demod_input_fs, 4500); + // Smoothing should be roughly sample rate over max baud + // 24k / 3.2k is 7.5 + smooth.SetSize(8); + audio_output.configure(false); + + // Set up the frame extraction, limits of baud + setFrameExtractParams(demod_input_fs, 4000, 300, 32); + + // Mark the class as ready to accept data + configured = true; +} // ----------------------------- // Frame extractraction methods @@ -118,422 +111,381 @@ void POCSAGProcessor::configure() { #define MAX_WITHOUT_SINGLE (64) #define MAX_BAD_TRANS (10) -#define M_SYNC (0x7cd215d8) -#define M_NOTSYNC (0x832dea27) +#define M_SYNC (0x7cd215d8) +#define M_NOTSYNC (0x832dea27) -#define M_IDLE (0x7a89c197) +#define M_IDLE (0x7a89c197) // ==================================================================== // // ==================================================================== -inline int bitsDiff(unsigned long left, unsigned long right) -{ - unsigned long xord = left ^ right; - int count = 0; - for (int i = 0; i < 32; i++) - { - if ((xord & 0x01) != 0) ++count; - xord = xord >> 1; - } - return(count); - +inline int bitsDiff(unsigned long left, unsigned long right) { + unsigned long xord = left ^ right; + int count = 0; + for (int i = 0; i < 32; i++) { + if ((xord & 0x01) != 0) ++count; + xord = xord >> 1; + } + return (count); } - // ==================================================================== // // ==================================================================== -void POCSAGProcessor::initFrameExtraction() -{ - m_averageSymbolLen_1024 = m_maxSymSamples_1024; - m_lastStableSymbolLen_1024 = m_minSymSamples_1024; +void POCSAGProcessor::initFrameExtraction() { + m_averageSymbolLen_1024 = m_maxSymSamples_1024; + m_lastStableSymbolLen_1024 = m_minSymSamples_1024; - m_badTransitions = 0; - m_bitsStart = 0; - m_bitsEnd = 0; - m_inverted = false; + m_badTransitions = 0; + m_bitsStart = 0; + m_bitsEnd = 0; + m_inverted = false; - resetVals(); + resetVals(); } // ==================================================================== // // ==================================================================== -void POCSAGProcessor::resetVals() -{ - // Reset the parameters - // -------------------- - m_goodTransitions = 0; - m_badTransitions = 0; - m_averageSymbolLen_1024 = m_maxSymSamples_1024; - m_shortestGoodTrans_1024 = m_maxSymSamples_1024; - m_valMid = 0; - - // And reset the counts - // -------------------- - m_lastTransPos_1024 = 0; - m_lastBitPos_1024 = 0; - m_lastSample = 0; - m_sampleNo = 0; - m_nextBitPos_1024 = m_maxSymSamples_1024; - m_nextBitPosInt = (long)m_nextBitPos_1024; - - // Extraction - m_fifo.numBits = 0; - m_gotSync = false; - m_numCode = 0; +void POCSAGProcessor::resetVals() { + // Reset the parameters + // -------------------- + m_goodTransitions = 0; + m_badTransitions = 0; + m_averageSymbolLen_1024 = m_maxSymSamples_1024; + m_shortestGoodTrans_1024 = m_maxSymSamples_1024; + m_valMid = 0; + + // And reset the counts + // -------------------- + m_lastTransPos_1024 = 0; + m_lastBitPos_1024 = 0; + m_lastSample = 0; + m_sampleNo = 0; + m_nextBitPos_1024 = m_maxSymSamples_1024; + m_nextBitPosInt = (long)m_nextBitPos_1024; + + // Extraction + m_fifo.numBits = 0; + m_gotSync = false; + m_numCode = 0; } // ==================================================================== // // ==================================================================== -void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue) -{ - m_samplesPerSec = a_samplesPerSec; - m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud); - m_maxSymSamples_1024 = (uint32_t)(1024.0f*(float)a_samplesPerSec / (float)a_minBaud); - m_maxRunOfSameValue = maxRunOfSameValue; +void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue) { + m_samplesPerSec = a_samplesPerSec; + m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud); + m_maxSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_minBaud); + m_maxRunOfSameValue = maxRunOfSameValue; - m_shortestGoodTrans_1024 = m_maxSymSamples_1024; - m_averageSymbolLen_1024 = m_maxSymSamples_1024; - m_lastStableSymbolLen_1024 = m_minSymSamples_1024; + m_shortestGoodTrans_1024 = m_maxSymSamples_1024; + m_averageSymbolLen_1024 = m_maxSymSamples_1024; + m_lastStableSymbolLen_1024 = m_minSymSamples_1024; - m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2; - m_nextBitPosInt = m_nextBitPos_1024 >> 10; + m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2; + m_nextBitPosInt = m_nextBitPos_1024 >> 10; - initFrameExtraction(); + initFrameExtraction(); } // ==================================================================== // // ==================================================================== -int POCSAGProcessor::processDemodulatedSamples(float * sampleBuff, int noOfSamples) -{ - bool transition = false; - uint32_t samplePos_1024 = 0; - uint32_t len_1024 = 0; - - // Loop through the block of data - // ------------------------------ - for (int pos = 0; pos < noOfSamples; ++pos) - { - m_sample = sampleBuff[pos]; - m_valMid += (m_sample - m_valMid) / 1024.0f; - - ++m_sampleNo; - - // Detect Transition - // ----------------- - transition = ! ((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed - - // If this is a transition - // ----------------------- - if (transition) - { - // Calculate samples since last trans - // ---------------------------------- - int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid)*1024) / (m_sample - m_lastSample)); - if (fractional_1024 < 0) { fractional_1024 = -fractional_1024; } - - samplePos_1024 = (m_sampleNo<<10)-fractional_1024; - len_1024 = samplePos_1024 - m_lastTransPos_1024; - m_lastTransPos_1024 = samplePos_1024; - - // If symbol is large enough to be valid - // ------------------------------------- - if (len_1024 > m_minSymSamples_1024) - { - // Check for shortest good transition - // ---------------------------------- - if ((len_1024 < m_shortestGoodTrans_1024) && - (m_goodTransitions < BAUD_STABLE)) // detect change of symbol size - { - int32_t fractionOfShortest_1024 = (len_1024<<10) / m_shortestGoodTrans_1024; - - // If currently at half the baud rate - // ---------------------------------- - if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6 - { - m_averageSymbolLen_1024 /= 2; - m_shortestGoodTrans_1024 = len_1024; - } - // If currently at the wrong baud rate - // ----------------------------------- - else if (fractionOfShortest_1024 < 768) // 0.75 - { - m_averageSymbolLen_1024 = len_1024; - m_shortestGoodTrans_1024 = len_1024; - m_goodTransitions = 0; - m_lastSingleBitPos_1024 = samplePos_1024 - len_1024; - } - } - - // Calc the number of bits since events - // ------------------------------------ - int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2; - int bitsSinceLastTrans = max((uint32_t)1, (len_1024+halfSymbol_1024) / m_averageSymbolLen_1024 ); - int bitsSinceLastSingle = (((m_sampleNo<<10)-m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024; - - // Check for single bit - // -------------------- - if (bitsSinceLastTrans == 1) - { - m_lastSingleBitPos_1024 = samplePos_1024; - } - - // If too long since last transition - // --------------------------------- - if (bitsSinceLastTrans > MAX_CONSEC_SAME) - { - resetVals(); - } - // If too long sice last single bit - // -------------------------------- - else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE) - { - resetVals(); - } - else - { - // If this is a good transition - // ---------------------------- - int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans*m_averageSymbolLen_1024); - if (offsetFromExtectedTransition_1024 < 0) { offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024; } - if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good - { - ++m_goodTransitions; - uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions); - - uint32_t propFromPrevious = m_averageSymbolLen_1024*bitsCount; - uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans); - m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1); - m_badTransitions = 0; - //if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;} - // Store the old symbol size - if (m_goodTransitions >= BAUD_STABLE) - { - m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024; - } - } - } - - // Set the point of the last bit if not yet stable - // ----------------------------------------------- - if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0)) - { - m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2); - } - - // Calculate the exact positiom of the next bit - // -------------------------------------------- - int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024/2); - int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024; - m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16); - - // Check for bad pos error - // ----------------------- - if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024; - - // Calculate integer sample after next bit - // --------------------------------------- - m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1; - - } // symbol is large enough to be valid - else - { - // Bad transition, so reset the counts - // ----------------------------------- - ++m_badTransitions; - if (m_badTransitions > MAX_BAD_TRANS) - { - resetVals(); - } - } - } // end of if transition - - // Reached the point of the next bit - // --------------------------------- - if (m_sampleNo >= m_nextBitPosInt) - { - // Everything is good so extract a bit - // ----------------------------------- - if (m_goodTransitions > 20) - { - // Store value at the center of bit - // -------------------------------- - storeBit(); - } - // Check for long 1 or zero - // ------------------------ - uint32_t bitsSinceLastTrans = ((m_sampleNo<<10) - m_lastTransPos_1024) / m_averageSymbolLen_1024; - if (bitsSinceLastTrans > m_maxRunOfSameValue) - { - resetVals(); - } - - // Store the point of the last bit - // ------------------------------- - m_lastBitPos_1024 = m_nextBitPos_1024; - - // Calculate the exact point of the next bit - // ----------------------------------------- - m_nextBitPos_1024 += m_averageSymbolLen_1024; - - // Look for the bit after the next bit pos - // --------------------------------------- - m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1; - - } // Reached the point of the next bit - - m_lastSample = m_sample; - - } // Loop through the block of data - - return getNoOfBits(); +int POCSAGProcessor::processDemodulatedSamples(float* sampleBuff, int noOfSamples) { + bool transition = false; + uint32_t samplePos_1024 = 0; + uint32_t len_1024 = 0; + + // Loop through the block of data + // ------------------------------ + for (int pos = 0; pos < noOfSamples; ++pos) { + m_sample = sampleBuff[pos]; + m_valMid += (m_sample - m_valMid) / 1024.0f; + + ++m_sampleNo; + + // Detect Transition + // ----------------- + transition = !((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed + + // If this is a transition + // ----------------------- + if (transition) { + // Calculate samples since last trans + // ---------------------------------- + int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid) * 1024) / (m_sample - m_lastSample)); + if (fractional_1024 < 0) { + fractional_1024 = -fractional_1024; + } + + samplePos_1024 = (m_sampleNo << 10) - fractional_1024; + len_1024 = samplePos_1024 - m_lastTransPos_1024; + m_lastTransPos_1024 = samplePos_1024; + + // If symbol is large enough to be valid + // ------------------------------------- + if (len_1024 > m_minSymSamples_1024) { + // Check for shortest good transition + // ---------------------------------- + if ((len_1024 < m_shortestGoodTrans_1024) && + (m_goodTransitions < BAUD_STABLE)) // detect change of symbol size + { + int32_t fractionOfShortest_1024 = (len_1024 << 10) / m_shortestGoodTrans_1024; + + // If currently at half the baud rate + // ---------------------------------- + if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6 + { + m_averageSymbolLen_1024 /= 2; + m_shortestGoodTrans_1024 = len_1024; + } + // If currently at the wrong baud rate + // ----------------------------------- + else if (fractionOfShortest_1024 < 768) // 0.75 + { + m_averageSymbolLen_1024 = len_1024; + m_shortestGoodTrans_1024 = len_1024; + m_goodTransitions = 0; + m_lastSingleBitPos_1024 = samplePos_1024 - len_1024; + } + } + + // Calc the number of bits since events + // ------------------------------------ + int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2; + int bitsSinceLastTrans = max((uint32_t)1, (len_1024 + halfSymbol_1024) / m_averageSymbolLen_1024); + int bitsSinceLastSingle = (((m_sampleNo << 10) - m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024; + + // Check for single bit + // -------------------- + if (bitsSinceLastTrans == 1) { + m_lastSingleBitPos_1024 = samplePos_1024; + } + + // If too long since last transition + // --------------------------------- + if (bitsSinceLastTrans > MAX_CONSEC_SAME) { + resetVals(); + } + // If too long sice last single bit + // -------------------------------- + else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE) { + resetVals(); + } else { + // If this is a good transition + // ---------------------------- + int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans * m_averageSymbolLen_1024); + if (offsetFromExtectedTransition_1024 < 0) { + offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024; + } + if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good + { + ++m_goodTransitions; + uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions); + + uint32_t propFromPrevious = m_averageSymbolLen_1024 * bitsCount; + uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans); + m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1); + m_badTransitions = 0; + // if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;} + // Store the old symbol size + if (m_goodTransitions >= BAUD_STABLE) { + m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024; + } + } + } + + // Set the point of the last bit if not yet stable + // ----------------------------------------------- + if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0)) { + m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2); + } + + // Calculate the exact positiom of the next bit + // -------------------------------------------- + int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024 / 2); + int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024; + m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16); + + // Check for bad pos error + // ----------------------- + if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024; + + // Calculate integer sample after next bit + // --------------------------------------- + m_nextBitPosInt = (m_nextBitPos_1024 >> 10) + 1; + + } // symbol is large enough to be valid + else { + // Bad transition, so reset the counts + // ----------------------------------- + ++m_badTransitions; + if (m_badTransitions > MAX_BAD_TRANS) { + resetVals(); + } + } + } // end of if transition + + // Reached the point of the next bit + // --------------------------------- + if (m_sampleNo >= m_nextBitPosInt) { + // Everything is good so extract a bit + // ----------------------------------- + if (m_goodTransitions > 20) { + // Store value at the center of bit + // -------------------------------- + storeBit(); + } + // Check for long 1 or zero + // ------------------------ + uint32_t bitsSinceLastTrans = ((m_sampleNo << 10) - m_lastTransPos_1024) / m_averageSymbolLen_1024; + if (bitsSinceLastTrans > m_maxRunOfSameValue) { + resetVals(); + } + + // Store the point of the last bit + // ------------------------------- + m_lastBitPos_1024 = m_nextBitPos_1024; + + // Calculate the exact point of the next bit + // ----------------------------------------- + m_nextBitPos_1024 += m_averageSymbolLen_1024; + + // Look for the bit after the next bit pos + // --------------------------------------- + m_nextBitPosInt = (m_nextBitPos_1024 >> 10) + 1; + + } // Reached the point of the next bit + + m_lastSample = m_sample; + + } // Loop through the block of data + + return getNoOfBits(); } // ==================================================================== // // ==================================================================== -void POCSAGProcessor::storeBit() -{ - if (++m_bitsStart >= BIT_BUF_SIZE) { m_bitsStart = 0; } - - // Calculate the bit value - float sample = (m_sample + m_lastSample) / 2; - //int32_t sample_1024 = m_sample_1024; - bool bit = sample > m_valMid; - - // If buffer not full - if (m_bitsStart != m_bitsEnd) - { - // Decide on output val - if (bit) - { - m_bits[m_bitsStart] = 0; - } - else - { - m_bits[m_bitsStart] = 1; - } - } - // Throw away bits if the buffer is full - else - { - if (--m_bitsStart <= -1) - { - m_bitsStart = BIT_BUF_SIZE - 1; - } - } +void POCSAGProcessor::storeBit() { + if (++m_bitsStart >= BIT_BUF_SIZE) { + m_bitsStart = 0; + } + + // Calculate the bit value + float sample = (m_sample + m_lastSample) / 2; + // int32_t sample_1024 = m_sample_1024; + bool bit = sample > m_valMid; + + // If buffer not full + if (m_bitsStart != m_bitsEnd) { + // Decide on output val + if (bit) { + m_bits[m_bitsStart] = 0; + } else { + m_bits[m_bitsStart] = 1; + } + } + // Throw away bits if the buffer is full + else { + if (--m_bitsStart <= -1) { + m_bitsStart = BIT_BUF_SIZE - 1; + } + } } // ==================================================================== // // ==================================================================== -int POCSAGProcessor::extractFrames() -{ - int msgCnt = 0; - // While there is unread data in the bits buffer - //---------------------------------------------- - while (getNoOfBits() > 0) - { - m_fifo.codeword = (m_fifo.codeword << 1) + getBit(); - m_fifo.numBits++; - - // If number of bits in fifo equals 32 - //------------------------------------ - if (m_fifo.numBits >= 32) - { - // Not got sync - // ------------ - if (!m_gotSync) - { - if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2) - { - m_inverted = false; - m_gotSync = true; - m_numCode = -1; - m_fifo.numBits = 0; - } - else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2) - { - m_inverted = true; - m_gotSync = true; - m_numCode = -1; - m_fifo.numBits = 0; - } - else - { - // Cause it to load one more bit - m_fifo.numBits = 31; - } - } // Not got sync - else - { - // Increment the word count - // ------------------------ - ++m_numCode; // It got set to -1 when a sync was found, now count the 16 words - uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword; - OnDataWord(val, m_numCode); - - // If at the end of a 16 word block - // -------------------------------- - if (m_numCode >= 15) - { - msgCnt += OnDataFrame(m_numCode+1, (m_samplesPerSec<<10) / m_lastStableSymbolLen_1024); - m_gotSync = false; - m_numCode = -1; - } - m_fifo.numBits = 0; - } - } // If number of bits in fifo equals 32 - } // While there is unread data in the bits buffer - return msgCnt; -} // extractFrames +int POCSAGProcessor::extractFrames() { + int msgCnt = 0; + // While there is unread data in the bits buffer + //---------------------------------------------- + while (getNoOfBits() > 0) { + m_fifo.codeword = (m_fifo.codeword << 1) + getBit(); + m_fifo.numBits++; + + // If number of bits in fifo equals 32 + //------------------------------------ + if (m_fifo.numBits >= 32) { + // Not got sync + // ------------ + if (!m_gotSync) { + if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2) { + m_inverted = false; + m_gotSync = true; + m_numCode = -1; + m_fifo.numBits = 0; + } else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2) { + m_inverted = true; + m_gotSync = true; + m_numCode = -1; + m_fifo.numBits = 0; + } else { + // Cause it to load one more bit + m_fifo.numBits = 31; + } + } // Not got sync + else { + // Increment the word count + // ------------------------ + ++m_numCode; // It got set to -1 when a sync was found, now count the 16 words + uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword; + OnDataWord(val, m_numCode); + + // If at the end of a 16 word block + // -------------------------------- + if (m_numCode >= 15) { + msgCnt += OnDataFrame(m_numCode + 1, (m_samplesPerSec << 10) / m_lastStableSymbolLen_1024); + m_gotSync = false; + m_numCode = -1; + } + m_fifo.numBits = 0; + } + } // If number of bits in fifo equals 32 + } // While there is unread data in the bits buffer + return msgCnt; +} // extractFrames // ==================================================================== // // ==================================================================== -short POCSAGProcessor::getBit() -{ - if (m_bitsEnd != m_bitsStart) - { - if (++m_bitsEnd >= BIT_BUF_SIZE) - { - m_bitsEnd = 0; - } - return m_bits[m_bitsEnd]; - } - else - { - return -1; - } +short POCSAGProcessor::getBit() { + if (m_bitsEnd != m_bitsStart) { + if (++m_bitsEnd >= BIT_BUF_SIZE) { + m_bitsEnd = 0; + } + return m_bits[m_bitsEnd]; + } else { + return -1; + } } // ==================================================================== // // ==================================================================== -int POCSAGProcessor::getNoOfBits() -{ - int bits = m_bitsEnd - m_bitsStart; - if (bits < 0) { bits += BIT_BUF_SIZE; } - return bits; +int POCSAGProcessor::getNoOfBits() { + int bits = m_bitsEnd - m_bitsStart; + if (bits < 0) { + bits += BIT_BUF_SIZE; + } + return bits; } // ==================================================================== // // ==================================================================== -uint32_t POCSAGProcessor::getRate() -{ - return ((m_samplesPerSec<<10)+512) / m_lastStableSymbolLen_1024; +uint32_t POCSAGProcessor::getRate() { + return ((m_samplesPerSec << 10) + 512) / m_lastStableSymbolLen_1024; } - // ==================================================================== // // ==================================================================== int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_pocsag.hpp b/firmware/baseband/proc_pocsag.hpp index f848e40e6..d4689da4e 100644 --- a/firmware/baseband/proc_pocsag.hpp +++ b/firmware/baseband/proc_pocsag.hpp @@ -40,196 +40,185 @@ #include "portapack_shared_memory.hpp" #include -#include +#include using namespace std; // Class used to smooth demodulated waveform prior to decoding // ----------------------------------------------------------- template -class SmoothVals -{ -protected: - ValType * m_lastVals; // Previoius N values - int m_size; // The size N - CalcType m_sumVal; // Running sum of lastVals - int m_pos; // Current position in last vals ring buffer - int m_count; // - -public: - SmoothVals() : m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0) - { - m_lastVals = new ValType[m_size]; - } - - // -------------------------------------------------- - // -------------------------------------------------- - virtual ~SmoothVals() - { - delete[] m_lastVals; - } - - SmoothVals(const SmoothVals&) - { +class SmoothVals { + protected: + ValType* m_lastVals; // Previoius N values + int m_size; // The size N + CalcType m_sumVal; // Running sum of lastVals + int m_pos; // Current position in last vals ring buffer + int m_count; // + + public: + SmoothVals() + : m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0) { + m_lastVals = new ValType[m_size]; + } + // -------------------------------------------------- + // -------------------------------------------------- + virtual ~SmoothVals() { + delete[] m_lastVals; } - SmoothVals & operator=(const SmoothVals&) - { - return *this ; + SmoothVals(const SmoothVals&) { } - // -------------------------------------------------- - // Set size of smoothing - // -------------------------------------------------- - void SetSize(int size) - { - m_size = std::max(size, 1); - m_pos = 0; - delete[] m_lastVals; - m_lastVals = new ValType[m_size]; - m_sumVal = 0; - } - - // -------------------------------------------------- - // Get size of smoothing - // -------------------------------------------------- - int Size() { return m_size; } - - // -------------------------------------------------- - // In place processing - // -------------------------------------------------- - void Process(ValType * valBuff, int numVals) - { - ValType tmpVal; - - if (m_count > (1024*10)) - { - // Recalculate the sum value occasionaly, stops accumulated errors when using float - m_count = 0; - m_sumVal = 0; - for (int i = 0; i < m_size; ++i) { m_sumVal += (CalcType)m_lastVals[i]; } - } - - // Use a rolling smoothed value while processing the buffer - for (int buffPos = 0; buffPos < numVals; ++buffPos) - { - m_pos = (m_pos + 1); // Increment the position in the stored values - if (m_pos >= m_size) { m_pos = 0; } // loop if reached the end of the stored values - - m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value - m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value - m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value - - tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed - valBuff[buffPos] = tmpVal; - } - - m_count += numVals; - } -}; + SmoothVals& operator=(const SmoothVals&) { + return *this; + } + // -------------------------------------------------- + // Set size of smoothing + // -------------------------------------------------- + void SetSize(int size) { + m_size = std::max(size, 1); + m_pos = 0; + delete[] m_lastVals; + m_lastVals = new ValType[m_size]; + m_sumVal = 0; + } + // -------------------------------------------------- + // Get size of smoothing + // -------------------------------------------------- + int Size() { return m_size; } + + // -------------------------------------------------- + // In place processing + // -------------------------------------------------- + void Process(ValType* valBuff, int numVals) { + ValType tmpVal; + + if (m_count > (1024 * 10)) { + // Recalculate the sum value occasionaly, stops accumulated errors when using float + m_count = 0; + m_sumVal = 0; + for (int i = 0; i < m_size; ++i) { + m_sumVal += (CalcType)m_lastVals[i]; + } + } + + // Use a rolling smoothed value while processing the buffer + for (int buffPos = 0; buffPos < numVals; ++buffPos) { + m_pos = (m_pos + 1); // Increment the position in the stored values + if (m_pos >= m_size) { + m_pos = 0; + } // loop if reached the end of the stored values + + m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value + m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value + m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value + + tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed + valBuff[buffPos] = tmpVal; + } + + m_count += numVals; + } +}; // -------------------------------------------------- // Class to process base band data to pocsag frames // -------------------------------------------------- -class POCSAGProcessor : public BasebandProcessor{ -public: - - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - - int OnDataFrame(int len, int baud); - int OnDataWord(uint32_t word, int pos); - -private: - static constexpr size_t baseband_fs = 3072000; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - std::array audio { }; - const buffer_f32_t audio_buffer { - audio.data(), - audio.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::decimate::FIRAndDecimateComplex channel_filter { }; - dsp::demodulate::FM demod { }; - SmoothVals smooth = { }; - - AudioOutput audio_output { }; - - bool configured = false; - pocsag::POCSAGPacket packet { }; - - void configure(); - - // ---------------------------------------- - // Frame extractraction methods and members - // ---------------------------------------- -private: - void initFrameExtraction(); - struct FIFOStruct { - unsigned long codeword; - int numBits; - }; - - #define BIT_BUF_SIZE (64) - - void resetVals(); - void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32); - - int processDemodulatedSamples(float * sampleBuff, int noOfSamples); - int extractFrames(); - - void storeBit(); - short getBit(); - - int getNoOfBits(); - uint32_t getRate(); - - uint32_t m_averageSymbolLen_1024{0}; - uint32_t m_lastStableSymbolLen_1024{0}; - - uint32_t m_samplesPerSec{0}; - uint32_t m_goodTransitions{0}; - uint32_t m_badTransitions{0}; - - uint32_t m_sampleNo{0}; - float m_sample{0}; - float m_valMid{0.0f}; - float m_lastSample{0.0f}; - - uint32_t m_lastTransPos_1024{0}; - uint32_t m_lastSingleBitPos_1024{0}; - - uint32_t m_nextBitPosInt{0}; // Integer rounded up version to save on ops - uint32_t m_nextBitPos_1024{0}; - uint32_t m_lastBitPos_1024{0}; - - uint32_t m_shortestGoodTrans_1024{0}; - uint32_t m_minSymSamples_1024{0}; - uint32_t m_maxSymSamples_1024{0}; - uint32_t m_maxRunOfSameValue{0}; - - bitset<(size_t)BIT_BUF_SIZE> m_bits{0}; - long m_bitsStart{0}; - long m_bitsEnd{0}; - - FIFOStruct m_fifo{0,0}; - bool m_gotSync{false}; - int m_numCode{0}; - bool m_inverted{false}; - +class POCSAGProcessor : public BasebandProcessor { + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + int OnDataFrame(int len, int baud); + int OnDataWord(uint32_t word, int pos); + + private: + static constexpr size_t baseband_fs = 3072000; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + std::array audio{}; + const buffer_f32_t audio_buffer{ + audio.data(), + audio.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + dsp::demodulate::FM demod{}; + SmoothVals smooth = {}; + + AudioOutput audio_output{}; + + bool configured = false; + pocsag::POCSAGPacket packet{}; + + void configure(); + + // ---------------------------------------- + // Frame extractraction methods and members + // ---------------------------------------- + private: + void initFrameExtraction(); + struct FIFOStruct { + unsigned long codeword; + int numBits; + }; + +#define BIT_BUF_SIZE (64) + + void resetVals(); + void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32); + + int processDemodulatedSamples(float* sampleBuff, int noOfSamples); + int extractFrames(); + + void storeBit(); + short getBit(); + + int getNoOfBits(); + uint32_t getRate(); + + uint32_t m_averageSymbolLen_1024{0}; + uint32_t m_lastStableSymbolLen_1024{0}; + + uint32_t m_samplesPerSec{0}; + uint32_t m_goodTransitions{0}; + uint32_t m_badTransitions{0}; + + uint32_t m_sampleNo{0}; + float m_sample{0}; + float m_valMid{0.0f}; + float m_lastSample{0.0f}; + + uint32_t m_lastTransPos_1024{0}; + uint32_t m_lastSingleBitPos_1024{0}; + + uint32_t m_nextBitPosInt{0}; // Integer rounded up version to save on ops + uint32_t m_nextBitPos_1024{0}; + uint32_t m_lastBitPos_1024{0}; + + uint32_t m_shortestGoodTrans_1024{0}; + uint32_t m_minSymSamples_1024{0}; + uint32_t m_maxSymSamples_1024{0}; + uint32_t m_maxRunOfSameValue{0}; + + bitset<(size_t)BIT_BUF_SIZE> m_bits{0}; + long m_bitsStart{0}; + long m_bitsEnd{0}; + + FIFOStruct m_fifo{0, 0}; + bool m_gotSync{false}; + int m_numCode{0}; + bool m_inverted{false}; }; -#endif/*__PROC_POCSAG_H__*/ +#endif /*__PROC_POCSAG_H__*/ diff --git a/firmware/baseband/proc_rds.cpp b/firmware/baseband/proc_rds.cpp index 762d1b44f..381399950 100644 --- a/firmware/baseband/proc_rds.cpp +++ b/firmware/baseband/proc_rds.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,84 +28,86 @@ #include void RDSProcessor::execute(const buffer_c8_t& buffer) { - - for (size_t i = 0; i < buffer.count; i++) { - - // Sample generation at 2.28M / 10 = 228kHz - if (s >= 9) { - s = 0; - if (sample_count >= SAMPLES_PER_BIT) { - if (bit_pos >= message_length) { - bit_pos = 0; - cur_output = 0; - } - - cur_bit = (rdsdata[(bit_pos / 26) & 127] >> (25 - (bit_pos % 26))) & 1; - prev_output = cur_output; - cur_output = prev_output ^ cur_bit; - - const int32_t * src = waveform_biphase; - int idx = in_sample_index; - - for (int j = 0; j < FILTER_SIZE; j++) { - val = (*src++); - if (cur_output) val = -val; - sample_buffer[idx++] += val; - if (idx >= SAMPLE_BUFFER_SIZE) idx = 0; - } - - in_sample_index += SAMPLES_PER_BIT; - if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; - - bit_pos++; - - sample_count = 0; - } - - sample = sample_buffer[out_sample_index]; - sample_buffer[out_sample_index] = 0; - out_sample_index++; - if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; - - // AM @ 228k/4 = 57kHz - // 0, sample, 0, -sample... - switch (mphase & 3) { - case 0: - case 2: sample = 0; break; - case 1: break; - case 3: sample = -sample; // break; - } - mphase++; - - sample_count++; - } else { - s++; - } - - // FM - delta = (sample >> 16) * 386760; // ? - - phase += delta; - sphase = phase + (64 << 18); - - re = (sine_table_i8[(sphase & 0x03FF0000) >> 18]); - im = (sine_table_i8[(phase & 0x03FF0000) >> 18]); - - buffer.p[i] = {re, im}; - } + for (size_t i = 0; i < buffer.count; i++) { + // Sample generation at 2.28M / 10 = 228kHz + if (s >= 9) { + s = 0; + if (sample_count >= SAMPLES_PER_BIT) { + if (bit_pos >= message_length) { + bit_pos = 0; + cur_output = 0; + } + + cur_bit = (rdsdata[(bit_pos / 26) & 127] >> (25 - (bit_pos % 26))) & 1; + prev_output = cur_output; + cur_output = prev_output ^ cur_bit; + + const int32_t* src = waveform_biphase; + int idx = in_sample_index; + + for (int j = 0; j < FILTER_SIZE; j++) { + val = (*src++); + if (cur_output) val = -val; + sample_buffer[idx++] += val; + if (idx >= SAMPLE_BUFFER_SIZE) idx = 0; + } + + in_sample_index += SAMPLES_PER_BIT; + if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; + + bit_pos++; + + sample_count = 0; + } + + sample = sample_buffer[out_sample_index]; + sample_buffer[out_sample_index] = 0; + out_sample_index++; + if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; + + // AM @ 228k/4 = 57kHz + // 0, sample, 0, -sample... + switch (mphase & 3) { + case 0: + case 2: + sample = 0; + break; + case 1: + break; + case 3: + sample = -sample; // break; + } + mphase++; + + sample_count++; + } else { + s++; + } + + // FM + delta = (sample >> 16) * 386760; // ? + + phase += delta; + sphase = phase + (64 << 18); + + re = (sine_table_i8[(sphase & 0x03FF0000) >> 18]); + im = (sine_table_i8[(phase & 0x03FF0000) >> 18]); + + buffer.p[i] = {re, im}; + } } void RDSProcessor::on_message(const Message* const msg) { - if (msg->id == Message::ID::RDSConfigure) { - const auto message = *reinterpret_cast(msg); - rdsdata = (uint32_t*)shared_memory.bb_data.data; - message_length = message.length; - configured = true; - } + if (msg->id == Message::ID::RDSConfigure) { + const auto message = *reinterpret_cast(msg); + rdsdata = (uint32_t*)shared_memory.bb_data.data; + message_length = message.length; + configured = true; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_rds.hpp b/firmware/baseband/proc_rds.hpp index 169b2cc20..a39cf45f5 100644 --- a/firmware/baseband/proc_rds.hpp +++ b/firmware/baseband/proc_rds.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -31,109 +31,107 @@ #define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE class RDSProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const msg) override; + + private: + uint32_t* rdsdata{}; + + BasebandThread baseband_thread{2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; -private: - uint32_t * rdsdata { }; - - BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - uint16_t message_length { 0 }; - int8_t re { 0 }, im { 0 }; - uint8_t mphase { 0 }, s { 0 }; - uint32_t bit_pos { 0 }; + uint16_t message_length{0}; + int8_t re{0}, im{0}; + uint8_t mphase{0}, s{0}; + uint32_t bit_pos{0}; int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; - int32_t val { 0 }; - uint8_t prev_output { 0 }; - uint8_t cur_output { 0 }; - uint8_t cur_bit { 0 }; + int32_t val{0}; + uint8_t prev_output{0}; + uint8_t cur_output{0}; + uint8_t cur_bit{0}; int sample_count = SAMPLES_PER_BIT; int in_sample_index = 0; - int32_t sample { 0 }; + int32_t sample{0}; int out_sample_index = SAMPLE_BUFFER_SIZE - 1; - uint32_t phase { 0 }, sphase { 0 }; - int32_t delta { 0 }; - - bool configured { false }; + uint32_t phase{0}, sphase{0}; + int32_t delta{0}; - const int32_t waveform_biphase[576] = { - 165,167,168,168,167,166,163,160, - 157,152,147,141,134,126,118,109, - 99,88,77,66,53,41,27,14, - 0,-14,-29,-44,-59,-74,-89,-105, - -120,-135,-150,-165,-179,-193,-206,-218, - -231,-242,-252,-262,-271,-279,-286,-291, - -296,-299,-301,-302,-302,-300,-297,-292, - -286,-278,-269,-259,-247,-233,-219,-202, - -185,-166,-145,-124,-101,-77,-52,-26, - 0,27,56,85,114,144,175,205, - 236,266,296,326,356,384,412,439, - 465,490,513,535,555,574,590,604, - 616,626,633,637,639,638,633,626, - 616,602,586,565,542,515,485,451, - 414,373,329,282,232,178,121,62, - 0,-65,-132,-202,-274,-347,-423,-500, - -578,-656,-736,-815,-894,-973,-1051,-1128, - -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, - -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, - -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, - -1334,-1217,-1087,-943,-785,-611,-423,-219, - 0,235,487,755,1040,1341,1659,1994, - 2346,2715,3101,3504,3923,4359,4811,5280, - 5764,6264,6780,7310,7856,8415,8987,9573, - 10172,10782,11404,12036,12678,13329,13989,14656, - 15330,16009,16694,17382,18074,18767,19461,20155, - 20848,21539,22226,22909,23586,24256,24918,25571, - 26214,26845,27464,28068,28658,29231,29787,30325, - 30842,31339,31814,32266,32694,33097,33473,33823, - 34144,34437,34699,34931,35131,35299,35434,35535, - 35602,35634,35630,35591,35515,35402,35252,35065, - 34841,34579,34279,33941,33566,33153,32702,32214, - 31689,31128,30530,29897,29228,28525,27788,27017, - 26214,25379,24513,23617,22693,21740,20761,19755, - 18725,17672,16597,15501,14385,13251,12101,10935, - 9755,8563,7360,6148,4927,3701,2470,1235, - 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, - -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, - -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, - -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, - -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, - -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, - -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, - -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, - -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, - -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, - -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, - -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, - -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, - -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, - -2346,-1994,-1659,-1341,-1040,-755,-487,-235, - 0,219,423,611,785,943,1087,1217, - 1334,1437,1527,1605,1670,1724,1767,1800, - 1822,1835,1838,1833,1820,1799,1771,1736, - 1695,1648,1596,1540,1479,1415,1347,1276, - 1203,1128,1051,973,894,815,736,656, - 578,500,423,347,274,202,132,65, - 0,-62,-121,-178,-232,-282,-329,-373, - -414,-451,-485,-515,-542,-565,-586,-602, - -616,-626,-633,-638,-639,-637,-633,-626, - -616,-604,-590,-574,-555,-535,-513,-490, - -465,-439,-412,-384,-356,-326,-296,-266, - -236,-205,-175,-144,-114,-85,-56,-27, - 0,26,52,77,101,124,145,166, - 185,202,219,233,247,259,269,278, - 286,292,297,300,302,302,301,299, - 296,291,286,279,271,262,252,242, - 231,218,206,193,179,165,150,135, - 120,105,89,74,59,44,29,14, - 0,-14,-27,-41,-53,-66,-77,-88, - -99,-109,-118,-126,-134,-141,-147,-152, - -157,-160,-163,-166,-167,-168,-168,-167 - }; + bool configured{false}; + const int32_t waveform_biphase[576] = { + 165, 167, 168, 168, 167, 166, 163, 160, + 157, 152, 147, 141, 134, 126, 118, 109, + 99, 88, 77, 66, 53, 41, 27, 14, + 0, -14, -29, -44, -59, -74, -89, -105, + -120, -135, -150, -165, -179, -193, -206, -218, + -231, -242, -252, -262, -271, -279, -286, -291, + -296, -299, -301, -302, -302, -300, -297, -292, + -286, -278, -269, -259, -247, -233, -219, -202, + -185, -166, -145, -124, -101, -77, -52, -26, + 0, 27, 56, 85, 114, 144, 175, 205, + 236, 266, 296, 326, 356, 384, 412, 439, + 465, 490, 513, 535, 555, 574, 590, 604, + 616, 626, 633, 637, 639, 638, 633, 626, + 616, 602, 586, 565, 542, 515, 485, 451, + 414, 373, 329, 282, 232, 178, 121, 62, + 0, -65, -132, -202, -274, -347, -423, -500, + -578, -656, -736, -815, -894, -973, -1051, -1128, + -1203, -1276, -1347, -1415, -1479, -1540, -1596, -1648, + -1695, -1736, -1771, -1799, -1820, -1833, -1838, -1835, + -1822, -1800, -1767, -1724, -1670, -1605, -1527, -1437, + -1334, -1217, -1087, -943, -785, -611, -423, -219, + 0, 235, 487, 755, 1040, 1341, 1659, 1994, + 2346, 2715, 3101, 3504, 3923, 4359, 4811, 5280, + 5764, 6264, 6780, 7310, 7856, 8415, 8987, 9573, + 10172, 10782, 11404, 12036, 12678, 13329, 13989, 14656, + 15330, 16009, 16694, 17382, 18074, 18767, 19461, 20155, + 20848, 21539, 22226, 22909, 23586, 24256, 24918, 25571, + 26214, 26845, 27464, 28068, 28658, 29231, 29787, 30325, + 30842, 31339, 31814, 32266, 32694, 33097, 33473, 33823, + 34144, 34437, 34699, 34931, 35131, 35299, 35434, 35535, + 35602, 35634, 35630, 35591, 35515, 35402, 35252, 35065, + 34841, 34579, 34279, 33941, 33566, 33153, 32702, 32214, + 31689, 31128, 30530, 29897, 29228, 28525, 27788, 27017, + 26214, 25379, 24513, 23617, 22693, 21740, 20761, 19755, + 18725, 17672, 16597, 15501, 14385, 13251, 12101, 10935, + 9755, 8563, 7360, 6148, 4927, 3701, 2470, 1235, + 0, -1235, -2470, -3701, -4927, -6148, -7360, -8563, + -9755, -10935, -12101, -13251, -14385, -15501, -16597, -17672, + -18725, -19755, -20761, -21740, -22693, -23617, -24513, -25379, + -26214, -27017, -27788, -28525, -29228, -29897, -30530, -31128, + -31689, -32214, -32702, -33153, -33566, -33941, -34279, -34579, + -34841, -35065, -35252, -35402, -35515, -35591, -35630, -35634, + -35602, -35535, -35434, -35299, -35131, -34931, -34699, -34437, + -34144, -33823, -33473, -33097, -32694, -32266, -31814, -31339, + -30842, -30325, -29787, -29231, -28658, -28068, -27464, -26845, + -26214, -25571, -24918, -24256, -23586, -22909, -22226, -21539, + -20848, -20155, -19461, -18767, -18074, -17382, -16694, -16009, + -15330, -14656, -13989, -13329, -12678, -12036, -11404, -10782, + -10172, -9573, -8987, -8415, -7856, -7310, -6780, -6264, + -5764, -5280, -4811, -4359, -3923, -3504, -3101, -2715, + -2346, -1994, -1659, -1341, -1040, -755, -487, -235, + 0, 219, 423, 611, 785, 943, 1087, 1217, + 1334, 1437, 1527, 1605, 1670, 1724, 1767, 1800, + 1822, 1835, 1838, 1833, 1820, 1799, 1771, 1736, + 1695, 1648, 1596, 1540, 1479, 1415, 1347, 1276, + 1203, 1128, 1051, 973, 894, 815, 736, 656, + 578, 500, 423, 347, 274, 202, 132, 65, + 0, -62, -121, -178, -232, -282, -329, -373, + -414, -451, -485, -515, -542, -565, -586, -602, + -616, -626, -633, -638, -639, -637, -633, -626, + -616, -604, -590, -574, -555, -535, -513, -490, + -465, -439, -412, -384, -356, -326, -296, -266, + -236, -205, -175, -144, -114, -85, -56, -27, + 0, 26, 52, 77, 101, 124, 145, 166, + 185, 202, 219, 233, 247, 259, 269, 278, + 286, 292, 297, 300, 302, 302, 301, 299, + 296, 291, 286, 279, 271, 262, 252, 242, + 231, 218, 206, 193, 179, 165, 150, 135, + 120, 105, 89, 74, 59, 44, 29, 14, + 0, -14, -27, -41, -53, -66, -77, -88, + -99, -109, -118, -126, -134, -141, -147, -152, + -157, -160, -163, -166, -167, -168, -168, -167}; }; #endif diff --git a/firmware/baseband/proc_replay.cpp b/firmware/baseband/proc_replay.cpp index 60999f4d9..52378fabb 100644 --- a/firmware/baseband/proc_replay.cpp +++ b/firmware/baseband/proc_replay.cpp @@ -29,103 +29,102 @@ #include "utility.hpp" ReplayProcessor::ReplayProcessor() { - channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000; - channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000; - channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000; - - spectrum_samples = 0; - - channel_spectrum.set_decimation_factor(1); - - configured = false; + channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000; + channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000; + channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000; + + spectrum_samples = 0; + + channel_spectrum.set_decimation_factor(1); + + configured = false; } void ReplayProcessor::execute(const buffer_c8_t& buffer) { - /* 4MHz, 2048 samples */ - - if (!configured) return; - - // File data is in C16 format, we need C8 - // File samplerate is 500kHz, we're at 4MHz - // iq_buffer can only be 512 C16 samples (RAM limitation) - // To fill up the 2048-sample C8 buffer, we need: - // 2048 samples * 2 bytes per sample = 4096 bytes - // Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each - // So 256 * 4 bytes per sample (C16) = 1024 bytes from the file - if( stream ) { - const size_t bytes_to_read = sizeof(*buffer.p) * 2 * (buffer.count / 8); // *2 (C16), /8 (oversampling) should be == 1024 - bytes_read += stream->read(iq_buffer.p, bytes_to_read); - } - - // Fill and "stretch" - for (size_t i = 0; i < buffer.count; i++) { - if (i & 3) { - buffer.p[i] = buffer.p[i - 1]; - } else { - auto re_out = iq_buffer.p[i >> 3].real() >> 8; - auto im_out = iq_buffer.p[i >> 3].imag() >> 8; - buffer.p[i] = { (int8_t)re_out, (int8_t)im_out }; - } - } - - spectrum_samples += buffer.count; - if( spectrum_samples >= spectrum_interval_samples ) { - spectrum_samples -= spectrum_interval_samples; - channel_spectrum.feed(iq_buffer, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); - - txprogress_message.progress = bytes_read; // Inform UI about progress - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } + /* 4MHz, 2048 samples */ + + if (!configured) return; + + // File data is in C16 format, we need C8 + // File samplerate is 500kHz, we're at 4MHz + // iq_buffer can only be 512 C16 samples (RAM limitation) + // To fill up the 2048-sample C8 buffer, we need: + // 2048 samples * 2 bytes per sample = 4096 bytes + // Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each + // So 256 * 4 bytes per sample (C16) = 1024 bytes from the file + if (stream) { + const size_t bytes_to_read = sizeof(*buffer.p) * 2 * (buffer.count / 8); // *2 (C16), /8 (oversampling) should be == 1024 + bytes_read += stream->read(iq_buffer.p, bytes_to_read); + } + + // Fill and "stretch" + for (size_t i = 0; i < buffer.count; i++) { + if (i & 3) { + buffer.p[i] = buffer.p[i - 1]; + } else { + auto re_out = iq_buffer.p[i >> 3].real() >> 8; + auto im_out = iq_buffer.p[i >> 3].imag() >> 8; + buffer.p[i] = {(int8_t)re_out, (int8_t)im_out}; + } + } + + spectrum_samples += buffer.count; + if (spectrum_samples >= spectrum_interval_samples) { + spectrum_samples -= spectrum_interval_samples; + channel_spectrum.feed(iq_buffer, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); + + txprogress_message.progress = bytes_read; // Inform UI about progress + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + } } void ReplayProcessor::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::SamplerateConfig: - samplerate_config(*reinterpret_cast(message)); - break; - - case Message::ID::ReplayConfig: - configured = false; - bytes_read = 0; - replay_config(*reinterpret_cast(message)); - break; - - // App has prefilled the buffers, we're ready to go now - case Message::ID::FIFOData: - configured = true; - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::SamplerateConfig: + samplerate_config(*reinterpret_cast(message)); + break; + + case Message::ID::ReplayConfig: + configured = false; + bytes_read = 0; + replay_config(*reinterpret_cast(message)); + break; + + // App has prefilled the buffers, we're ready to go now + case Message::ID::FIFOData: + configured = true; + break; + + default: + break; + } } void ReplayProcessor::samplerate_config(const SamplerateConfigMessage& message) { - baseband_fs = message.sample_rate; - baseband_thread.set_sampling_rate(baseband_fs); - spectrum_interval_samples = baseband_fs / spectrum_rate_hz; + baseband_fs = message.sample_rate; + baseband_thread.set_sampling_rate(baseband_fs); + spectrum_interval_samples = baseband_fs / spectrum_rate_hz; } void ReplayProcessor::replay_config(const ReplayConfigMessage& message) { - if( message.config ) { - - stream = std::make_unique(message.config); - - // Tell application that the buffers and FIFO pointers are ready, prefill - shared_memory.application_queue.push(sig_message); - } else { - stream.reset(); - } + if (message.config) { + stream = std::make_unique(message.config); + + // Tell application that the buffers and FIFO pointers are ready, prefill + shared_memory.application_queue.push(sig_message); + } else { + stream.reset(); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_replay.hpp b/firmware/baseband/proc_replay.hpp index df46315db..d06e0b06a 100644 --- a/firmware/baseband/proc_replay.hpp +++ b/firmware/baseband/proc_replay.hpp @@ -34,44 +34,43 @@ #include class ReplayProcessor : public BasebandProcessor { -public: - ReplayProcessor(); - - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - size_t baseband_fs = 0; - static constexpr auto spectrum_rate_hz = 50.0f; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - std::array iq { }; - const buffer_c16_t iq_buffer { - iq.data(), - iq.size(), - baseband_fs / 8 - }; - - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; - - std::unique_ptr stream { }; - - SpectrumCollector channel_spectrum { }; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; - - bool configured { false }; - uint32_t bytes_read { 0 }; - - void samplerate_config(const SamplerateConfigMessage& message); - void replay_config(const ReplayConfigMessage& message); - - TXProgressMessage txprogress_message { }; - RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest }; + public: + ReplayProcessor(); + + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + size_t baseband_fs = 0; + static constexpr auto spectrum_rate_hz = 50.0f; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + std::array iq{}; + const buffer_c16_t iq_buffer{ + iq.data(), + iq.size(), + baseband_fs / 8}; + + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; + + std::unique_ptr stream{}; + + SpectrumCollector channel_spectrum{}; + size_t spectrum_interval_samples = 0; + size_t spectrum_samples = 0; + + bool configured{false}; + uint32_t bytes_read{0}; + + void samplerate_config(const SamplerateConfigMessage& message); + void replay_config(const ReplayConfigMessage& message); + + TXProgressMessage txprogress_message{}; + RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest}; }; -#endif/*__PROC_REPLAY_HPP__*/ +#endif /*__PROC_REPLAY_HPP__*/ diff --git a/firmware/baseband/proc_sigfrx.cpp b/firmware/baseband/proc_sigfrx.cpp index 1dbfbed73..cb24561e2 100644 --- a/firmware/baseband/proc_sigfrx.cpp +++ b/firmware/baseband/proc_sigfrx.cpp @@ -25,5 +25,5 @@ #include void SIGFRXProcessor::execute(const buffer_c8_t& buffer) { - /* Called every 2048/3072000 second -- 1500Hz. */ + /* Called every 2048/3072000 second -- 1500Hz. */ } diff --git a/firmware/baseband/proc_sigfrx.hpp b/firmware/baseband/proc_sigfrx.hpp index 866fa3bf4..7f8e49c69 100644 --- a/firmware/baseband/proc_sigfrx.hpp +++ b/firmware/baseband/proc_sigfrx.hpp @@ -28,11 +28,10 @@ #include "dsp_demodulate.hpp" class SIGFRXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - -private: + public: + void execute(const buffer_c8_t& buffer) override; + private: }; #endif diff --git a/firmware/baseband/proc_siggen.cpp b/firmware/baseband/proc_siggen.cpp index 5a42d588c..c27b32981 100644 --- a/firmware/baseband/proc_siggen.cpp +++ b/firmware/baseband/proc_siggen.cpp @@ -28,115 +28,113 @@ #include void SigGenProcessor::execute(const buffer_c8_t& buffer) { - if (!configured) return; - - for (size_t i = 0; i < buffer.count; i++) { - - if (!sample_count && auto_off) { - configured = false; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - } else - sample_count--; - - if (tone_shape == 0) { - // CW - re = 127; - im = 0; - } else { - - if (tone_shape == 1) { - // Sine - sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]); - } else if (tone_shape == 2) { - // Triangle - int8_t a = (tone_phase & 0xFF000000) >> 24; - sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80; - } else if (tone_shape == 3) { - // Saw up - sample = ((tone_phase & 0xFF000000) >> 24); - } else if (tone_shape == 4) { - // Saw down - sample = ((tone_phase & 0xFF000000) >> 24) ^ 0xFF; - } else if (tone_shape == 5) { - // Square - sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; - } else if (tone_shape == 6) { - // Noise generator, pseudo random noise generator, 16 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci. - // https://en.wikipedia.org/wiki/Linear-feedback_shift_register - // 16 bits LFSR .taps: 16, 15, 13, 4 ;feedback polynomial: x^16 + x^15 + x^13 + x^4 + 1 - // Periode 65535= 2^n-1, quite continuous . - if (counter == 0) { // we slow down the shift register, because the pseudo random noise clock freq was too high for modulator. - bit_16 = ((lfsr_16 >> 0) ^ (lfsr_16 >> 1) ^ (lfsr_16 >> 3) ^ (lfsr_16 >> 4) ^ ((lfsr_16 >> 12) & 1)); - lfsr_16 = (lfsr_16 >> 1) | (bit_16 << 15); - sample = (lfsr_16 & 0x00FF); // main pseudo random noise generator. - } - if (counter == 5) { // after many empiric test, that combination mix of >>4 and >>5, gives a reasonable trade off white noise / good rf power level . - sample = ((lfsr_16 & 0b0000111111110000) >> 4); // just changing the spectrum shape . - } - if (counter == 10) { - sample = ((lfsr_16 & 0b0001111111100000) >> 5); // just changing the spectrum shape . - } - counter++; - if (counter ==15) { - counter=0; - } - } - - if (tone_shape < 6) { // we are in periodic signals, we need tone phases update. - tone_phase += tone_delta; - } - - // Do FM modulation - delta = sample * fm_delta; - - phase += delta; - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); - im = (sine_table_i8[( phase & 0xFF000000) >> 24]); - } - - buffer.p[i] = {re, im}; - } + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + if (!sample_count && auto_off) { + configured = false; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + } else + sample_count--; + + if (tone_shape == 0) { + // CW + re = 127; + im = 0; + } else { + if (tone_shape == 1) { + // Sine + sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]); + } else if (tone_shape == 2) { + // Triangle + int8_t a = (tone_phase & 0xFF000000) >> 24; + sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80; + } else if (tone_shape == 3) { + // Saw up + sample = ((tone_phase & 0xFF000000) >> 24); + } else if (tone_shape == 4) { + // Saw down + sample = ((tone_phase & 0xFF000000) >> 24) ^ 0xFF; + } else if (tone_shape == 5) { + // Square + sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; + } else if (tone_shape == 6) { + // Noise generator, pseudo random noise generator, 16 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci. + // https://en.wikipedia.org/wiki/Linear-feedback_shift_register + // 16 bits LFSR .taps: 16, 15, 13, 4 ;feedback polynomial: x^16 + x^15 + x^13 + x^4 + 1 + // Periode 65535= 2^n-1, quite continuous . + if (counter == 0) { // we slow down the shift register, because the pseudo random noise clock freq was too high for modulator. + bit_16 = ((lfsr_16 >> 0) ^ (lfsr_16 >> 1) ^ (lfsr_16 >> 3) ^ (lfsr_16 >> 4) ^ ((lfsr_16 >> 12) & 1)); + lfsr_16 = (lfsr_16 >> 1) | (bit_16 << 15); + sample = (lfsr_16 & 0x00FF); // main pseudo random noise generator. + } + if (counter == 5) { // after many empiric test, that combination mix of >>4 and >>5, gives a reasonable trade off white noise / good rf power level . + sample = ((lfsr_16 & 0b0000111111110000) >> 4); // just changing the spectrum shape . + } + if (counter == 10) { + sample = ((lfsr_16 & 0b0001111111100000) >> 5); // just changing the spectrum shape . + } + counter++; + if (counter == 15) { + counter = 0; + } + } + + if (tone_shape < 6) { // we are in periodic signals, we need tone phases update. + tone_phase += tone_delta; + } + + // Do FM modulation + delta = sample * fm_delta; + + phase += delta; + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000) >> 24]); + } + + buffer.p[i] = {re, im}; + } }; void SigGenProcessor::on_message(const Message* const msg) { - const auto message = *reinterpret_cast(msg); - - switch(msg->id) { - case Message::ID::SigGenConfig: - if (!message.bw) { - configured = false; - return; - } - - if (message.duration) { - sample_count = message.duration; - auto_off = true; - } else - auto_off = false; - - fm_delta = message.bw * (0xFFFFFFULL / 1536000); - tone_shape = message.shape; - - // lfsr = seed_value ; // Finally not used , init lfsr 8 bits. - lfsr_16 = seed_value_16; // init lfsr 16 bits. - - configured = true; - break; - - case Message::ID::SigGenTone: - tone_delta = reinterpret_cast(msg)->tone_delta; - break; - - default: - break; - } + const auto message = *reinterpret_cast(msg); + + switch (msg->id) { + case Message::ID::SigGenConfig: + if (!message.bw) { + configured = false; + return; + } + + if (message.duration) { + sample_count = message.duration; + auto_off = true; + } else + auto_off = false; + + fm_delta = message.bw * (0xFFFFFFULL / 1536000); + tone_shape = message.shape; + + // lfsr = seed_value ; // Finally not used , init lfsr 8 bits. + lfsr_16 = seed_value_16; // init lfsr 16 bits. + + configured = true; + break; + + case Message::ID::SigGenTone: + tone_delta = reinterpret_cast(msg)->tone_delta; + break; + + default: + break; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_siggen.hpp b/firmware/baseband/proc_siggen.hpp index 0bbcf36f7..c59490654 100644 --- a/firmware/baseband/proc_siggen.hpp +++ b/firmware/baseband/proc_siggen.hpp @@ -28,29 +28,29 @@ #include "portapack_shared_memory.hpp" class SigGenProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const msg) override; - -private: - bool configured { false }; - - BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - uint32_t tone_delta { 0 }, fm_delta { },tone_phase { 0 }; - uint8_t tone_shape { }; - uint32_t sample_count { 0 }; - bool auto_off { }; - int32_t phase { 0 }, sphase { 0 }, delta { 0 }; // they may have sign in the pseudo random sample generation. - int8_t sample { 0 }, re { 0 }, im { 0 }; // they have sign + and -. - uint16_t seed_value_16 = {0xACE1}; // seed 16 bits lfsr : any nonzero start state will work. - uint16_t lfsr_16 { }, bit_16 { }; // bit must be 16-bit to allow bit<<15 later in the code */ - uint8_t counter {0}; - // uint8_t seed_value = {0x56}; // Finally not used lfsr of 8 bits , seed 8blfsr : any nonzero start state will work. - // uint8_t lfsr { }, bit { }; // Finally not used lfsr of 8 bits , bit must be 8-bit to allow bit<<7 later in the code */ - - TXProgressMessage txprogress_message { }; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const msg) override; + + private: + bool configured{false}; + + BasebandThread baseband_thread{1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + uint32_t tone_delta{0}, fm_delta{}, tone_phase{0}; + uint8_t tone_shape{}; + uint32_t sample_count{0}; + bool auto_off{}; + int32_t phase{0}, sphase{0}, delta{0}; // they may have sign in the pseudo random sample generation. + int8_t sample{0}, re{0}, im{0}; // they have sign + and -. + uint16_t seed_value_16 = {0xACE1}; // seed 16 bits lfsr : any nonzero start state will work. + uint16_t lfsr_16{}, bit_16{}; // bit must be 16-bit to allow bit<<15 later in the code */ + uint8_t counter{0}; + // uint8_t seed_value = {0x56}; // Finally not used lfsr of 8 bits , seed 8blfsr : any nonzero start state will work. + // uint8_t lfsr { }, bit { }; // Finally not used lfsr of 8 bits , bit must be 8-bit to allow bit<<7 later in the code */ + + TXProgressMessage txprogress_message{}; }; #endif diff --git a/firmware/baseband/proc_sonde.cpp b/firmware/baseband/proc_sonde.cpp index d33b6ea44..fbf5431d7 100644 --- a/firmware/baseband/proc_sonde.cpp +++ b/firmware/baseband/proc_sonde.cpp @@ -29,122 +29,119 @@ #include "audio_output.hpp" SondeProcessor::SondeProcessor() { + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); + audio_output.configure(false); - audio_output.configure(false); - - tone_gen.configure(BEEP_BASE_FREQ, 1.0, ToneGen::tone_type::sine, AUDIO_SAMPLE_RATE); + tone_gen.configure(BEEP_BASE_FREQ, 1.0, ToneGen::tone_type::sine, AUDIO_SAMPLE_RATE); } void SondeProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ - - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto decimator_out = decim_1_out; - - /* 38.4kHz, 32 samples */ - feed_channel_stats(decimator_out); - - for (size_t i=0; iid) { - case Message::ID::RequestSignal: - if ((*reinterpret_cast(msg)).signal == RequestSignalMessage::Signal::BeepRequest) { - float rssi_ratio = (float) last_rssi / (float) RSSI_CEILING; - int beep_duration = 0; - - if(rssi_ratio <= PROPORTIONAL_BEEP_THRES) { - beep_duration = BEEP_MIN_DURATION; - } - else if(rssi_ratio < 1) { - beep_duration = (int) rssi_ratio * BEEP_DURATION_RANGE + BEEP_MIN_DURATION; - } - else { - beep_duration = BEEP_DURATION_RANGE + BEEP_MIN_DURATION; - } - - play_beep(); - chThdSleepMilliseconds(beep_duration); - stop_beep(); - } - break; - - case Message::ID::PitchRSSIConfigure: - pitch_rssi_config(*reinterpret_cast(msg)); - break; - - default: - break; - } + switch (msg->id) { + case Message::ID::RequestSignal: + if ((*reinterpret_cast(msg)).signal == RequestSignalMessage::Signal::BeepRequest) { + float rssi_ratio = (float)last_rssi / (float)RSSI_CEILING; + int beep_duration = 0; + + if (rssi_ratio <= PROPORTIONAL_BEEP_THRES) { + beep_duration = BEEP_MIN_DURATION; + } else if (rssi_ratio < 1) { + beep_duration = (int)rssi_ratio * BEEP_DURATION_RANGE + BEEP_MIN_DURATION; + } else { + beep_duration = BEEP_DURATION_RANGE + BEEP_MIN_DURATION; + } + + play_beep(); + chThdSleepMilliseconds(beep_duration); + stop_beep(); + } + break; + + case Message::ID::PitchRSSIConfigure: + pitch_rssi_config(*reinterpret_cast(msg)); + break; + + default: + break; + } } void SondeProcessor::play_beep() { - beep_play = true; - silence_play = false; + beep_play = true; + silence_play = false; } void SondeProcessor::stop_beep() { - beep_play = false; - silence_play = true; + beep_play = false; + silence_play = true; } void SondeProcessor::generate_beep() { - // here we let the samples be created using the ToneGen class: + // here we let the samples be created using the ToneGen class: - for(uint8_t i = 0; i < sizeof(audio_buffer.p); i++) { - audio_buffer.p[i] = (int16_t) ((tone_gen.process(0) >> 16) & 0x0000FFFF); - } + for (uint8_t i = 0; i < sizeof(audio_buffer.p); i++) { + audio_buffer.p[i] = (int16_t)((tone_gen.process(0) >> 16) & 0x0000FFFF); + } - audio_output.write(audio_buffer); + audio_output.write(audio_buffer); } void SondeProcessor::generate_silence() { - for(uint8_t i = 0; i < sizeof(audio_buffer.p); i++) { - audio_buffer.p[i] = 0; - } + for (uint8_t i = 0; i < sizeof(audio_buffer.p); i++) { + audio_buffer.p[i] = 0; + } - audio_output.write(audio_buffer); + audio_output.write(audio_buffer); } void SondeProcessor::pitch_rssi_config(const PitchRSSIConfigureMessage& message) { - pitch_rssi_enabled = message.enabled; + pitch_rssi_enabled = message.enabled; - uint32_t freq = (int) ((float) message.rssi * (float) RSSI_PITCH_WEIGHT + (float) BEEP_BASE_FREQ); + uint32_t freq = (int)((float)message.rssi * (float)RSSI_PITCH_WEIGHT + (float)BEEP_BASE_FREQ); - last_rssi = message.rssi; - tone_gen.configure(freq, 1.0, ToneGen::tone_type::sine, AUDIO_SAMPLE_RATE); + last_rssi = message.rssi; + tone_gen.configure(freq, 1.0, ToneGen::tone_type::sine, AUDIO_SAMPLE_RATE); } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); - return 0; + return 0; } diff --git a/firmware/baseband/proc_sonde.hpp b/firmware/baseband/proc_sonde.hpp index e0c1cb954..67914d277 100644 --- a/firmware/baseband/proc_sonde.hpp +++ b/firmware/baseband/proc_sonde.hpp @@ -24,49 +24,49 @@ /* Notes to self (or others, welcome !): * Sharebrained wrote in matched_filter.hpp that taps should be those of a complex low-pass filter combined with a complex sinusoid, so * that the filter shifts the spectrum where we want (signal of interest around 0Hz). - * + * * In this baseband processor, after decim_0 and decim_1, the signal ends up being sampled at 38400Hz (2457600 / 8 / 8) * Since the applied shift in ui_sonde.cpp is -fs/4 = -2457600/4 = -614400Hz to avoid the DC spike, the FSK signal ends up being * shifted by 614400 / 8 / 8 = 9600Hz. So decim_1_out should look like this: - * + * * _______________|______/'\______ * -C A B C - * + * * A is the DC spike at 0Hz * B is the FSK signal shifted right at 9600Hz * C is the bandwidth edge at 19200Hz - * + * * Taps should be computed to shift the whole spectrum by -9600Hz ("left") so that it looks like this: - * + * * ______________/'\______________ * -C D C - * + * * Anything unwanted (like A) should have been filtered off * D is B around 0Hz now - * + * * Then the clock_recovery function should be happy :) - * + * * Mathworks.com says: * In the case of a single-rate FIR design, we simply multiply each set of coefficients by (aka 'heterodyne with') a complex exponential. - * + * * Can SciPy's remez function be used for this ? See tools/firtest.py * GnuRadio's firdes only outputs an odd number of taps - * + * * --------------------------------------------------------------------- - * + * * Looking at the AIS baseband processor: - * + * * Copied everything necessary to get decim_1_out (so same 8 * 8 = 64 decimation factor) * The samplerate is also the same (2457600) * After the matching filter, the data is decimated by 2 so the final samplerate for clock_recovery is 38400 / 2 = 19200Hz. * Like here, the shift used is fs/4, so decim_1_out should be looking similar. * The AIS signal deviates by 2400 (4800Hz signal width), the symbol rate is 9600. - * + * * The matched filter's input samplerate is 38400Hz, to get a 9600Hz shift it must use 4 taps ? * To obtain unity gain, the sinusoid length must be / by the number of taps ? - * + * * See ais_baseband.hpp - * + * * */ #ifndef __PROC_SONDE_H__ @@ -97,107 +97,104 @@ #include #include - -#define BEEP_MIN_DURATION 60 -#define BEEP_DURATION_RANGE 100 -#define BEEP_BASE_FREQ 200 -#define RSSI_CEILING 1000 -#define PROPORTIONAL_BEEP_THRES 0.8 -#define RSSI_PITCH_WEIGHT 0.5 -#define AUDIO_SAMPLE_RATE 24000 +#define BEEP_MIN_DURATION 60 +#define BEEP_DURATION_RANGE 100 +#define BEEP_BASE_FREQ 200 +#define RSSI_CEILING 1000 +#define PROPORTIONAL_BEEP_THRES 0.8 +#define RSSI_PITCH_WEIGHT 0.5 +#define AUDIO_SAMPLE_RATE 24000 class SondeProcessor : public BasebandProcessor { -public: - SondeProcessor(); - - void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const msg); -private: - - static constexpr size_t baseband_fs = 2457600; - - std::array audio { }; - - const buffer_s16_t audio_buffer { - (int16_t*) audio.data(), - sizeof(audio) / sizeof(int16_t) - }; - - AudioOutput audio_output { }; - - bool beep_play { false }; - bool silence_play { false }; - bool pitch_rssi_enabled { false }; - - uint32_t last_rssi { 0 }; - - ToneGen tone_gen { }; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; - - // Actually 4800bits/s but the Manchester coding doubles the symbol rate - clock_recovery::ClockRecovery clock_recovery_fsk_9600 { - 19200, 9600, { 0.0555f }, - [this](const float raw_symbol) { - const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - this->packet_builder_fsk_9600_Meteomodem.execute(sliced_symbol); - } - }; - PacketBuilder packet_builder_fsk_9600_Meteomodem { - { 0b00110011001100110101100110110011, 32, 1 }, - { }, - { 88 * 2 * 8 }, - [this](const baseband::Packet& packet) { - const SondePacketMessage message { sonde::Packet::Type::Meteomodem_unknown, packet }; - shared_memory.application_queue.push(message); - } - }; - - clock_recovery::ClockRecovery clock_recovery_fsk_4800 { - 19200, 4800, { 0.0555f }, - [this](const float raw_symbol) { - const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - this->packet_builder_fsk_4800_Vaisala.execute(sliced_symbol); - } - }; - PacketBuilder packet_builder_fsk_4800_Vaisala { - { 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet) - //{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes. - { }, - { 320 * 8 }, - [this](const baseband::Packet& packet) { - const SondePacketMessage message { sonde::Packet::Type::Vaisala_RS41_SG, packet }; - shared_memory.application_queue.push(message); - } - }; - - void play_beep(); - void stop_beep(); - - /** - * Used for filling the audio buffer with the waveform - * generated by the ToneGen class: - * - */ - void generate_beep(); - - /** - * Used for filling the audio buffer with silence: - */ - void generate_silence(); - - void pitch_rssi_config(const PitchRSSIConfigureMessage& message); + public: + SondeProcessor(); + + void execute(const buffer_c8_t& buffer) override; + void on_message(const Message* const msg); + + private: + static constexpr size_t baseband_fs = 2457600; + + std::array audio{}; + + const buffer_s16_t audio_buffer{ + (int16_t*)audio.data(), + sizeof(audio) / sizeof(int16_t)}; + + AudioOutput audio_output{}; + + bool beep_play{false}; + bool silence_play{false}; + bool pitch_rssi_enabled{false}; + + uint32_t last_rssi{0}; + + ToneGen tone_gen{}; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::matched_filter::MatchedFilter mf{baseband::ais::square_taps_38k4_1t_p, 2}; + + // Actually 4800bits/s but the Manchester coding doubles the symbol rate + clock_recovery::ClockRecovery clock_recovery_fsk_9600{ + 19200, + 9600, + {0.0555f}, + [this](const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + this->packet_builder_fsk_9600_Meteomodem.execute(sliced_symbol); + }}; + PacketBuilder packet_builder_fsk_9600_Meteomodem{ + {0b00110011001100110101100110110011, 32, 1}, + {}, + {88 * 2 * 8}, + [this](const baseband::Packet& packet) { + const SondePacketMessage message{sonde::Packet::Type::Meteomodem_unknown, packet}; + shared_memory.application_queue.push(message); + }}; + + clock_recovery::ClockRecovery clock_recovery_fsk_4800{ + 19200, + 4800, + {0.0555f}, + [this](const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + this->packet_builder_fsk_4800_Vaisala.execute(sliced_symbol); + }}; + PacketBuilder packet_builder_fsk_4800_Vaisala{ + {0b00001000011011010101001110001000, 32, 1}, // euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet) + //{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes. + {}, + {320 * 8}, + [this](const baseband::Packet& packet) { + const SondePacketMessage message{sonde::Packet::Type::Vaisala_RS41_SG, packet}; + shared_memory.application_queue.push(message); + }}; + + void play_beep(); + void stop_beep(); + + /** + * Used for filling the audio buffer with the waveform + * generated by the ToneGen class: + * + */ + void generate_beep(); + + /** + * Used for filling the audio buffer with silence: + */ + void generate_silence(); + + void pitch_rssi_config(const PitchRSSIConfigureMessage& message); }; -#endif/*__PROC_ERT_H__*/ +#endif /*__PROC_ERT_H__*/ diff --git a/firmware/baseband/proc_spectrum_painter.cpp b/firmware/baseband/proc_spectrum_painter.cpp index 87512d477..1de2247ae 100644 --- a/firmware/baseband/proc_spectrum_painter.cpp +++ b/firmware/baseband/proc_spectrum_painter.cpp @@ -27,152 +27,144 @@ #include // TODO move to class members SpectrumPainterProcessor -complex16_t *current_line_data = nullptr; -complex16_t * volatile next_line_data = nullptr; +complex16_t* current_line_data = nullptr; +complex16_t* volatile next_line_data = nullptr; uint32_t current_line_index = 0; uint32_t current_line_width = 0; int32_t current_bw = 0; -std::vector fifo_data[1 << SpectrumPainterBufferConfigureResponseMessage::fifo_k] { }; -SpectrumPainterFIFO fifo { fifo_data, SpectrumPainterBufferConfigureResponseMessage::fifo_k }; +std::vector fifo_data[1 << SpectrumPainterBufferConfigureResponseMessage::fifo_k]{}; +SpectrumPainterFIFO fifo{fifo_data, SpectrumPainterBufferConfigureResponseMessage::fifo_k}; int max_val = 127; // This is called at 3072000/2048 = 1500Hz void SpectrumPainterProcessor::execute(const buffer_c8_t& buffer) { - - for (uint32_t i = 0; i < buffer.count; i++) { - if (current_line_data != nullptr) { - auto data = current_line_data[(current_line_index++ * current_bw / 3072 ) % current_line_width]; - buffer.p[i] = {(int8_t) data.real(), (int8_t) data.imag()}; - } - else - buffer.p[i] = {0, 0}; - } - - // collect new data - if (next_line_data != nullptr) { - if (current_line_data != nullptr) - delete current_line_data; - - current_line_data = next_line_data; - next_line_data = nullptr; - } + for (uint32_t i = 0; i < buffer.count; i++) { + if (current_line_data != nullptr) { + auto data = current_line_data[(current_line_index++ * current_bw / 3072) % current_line_width]; + buffer.p[i] = {(int8_t)data.real(), (int8_t)data.imag()}; + } else + buffer.p[i] = {0, 0}; + } + + // collect new data + if (next_line_data != nullptr) { + if (current_line_data != nullptr) + delete current_line_data; + + current_line_data = next_line_data; + next_line_data = nullptr; + } } WORKING_AREA(thread_wa, 4096); void SpectrumPainterProcessor::run() { - int ui = 0; - init_genrand(22267); - - while (true) { - if (fifo.is_empty() == false && next_line_data == nullptr) { - std::vector data; - fifo.out(data); - - auto picture_width = data.size(); - - auto fft_width = picture_width * 2; - auto qu = fft_width/4; - - complex16_t *v = new complex16_t[fft_width]; - complex16_t *tmp = new complex16_t[fft_width]; - - for (uint32_t fft_index = 0; fft_index < fft_width; fft_index++) { - if (fft_index < qu) { - } - else if (fft_index < qu*3) { - //TODO: Improve index handling - auto image_index = fft_index-qu; - - auto bin_power = data[image_index]; // 0 to 255 - auto bin_phase = genrand_int31(); // 0 to 255 - - // rotate by random angle - auto phase_cos = (sine_table_i8[((int)(bin_phase + 0x40)) & 0xFF]); // -127 to 127 - auto phase_sin = (sine_table_i8[((int)(bin_phase)) & 0xFF]); - - auto real = (int16_t)((int16_t)phase_cos * bin_power / 255); // -127 to 127 - auto imag = (int16_t)((int16_t)phase_sin * bin_power / 255); // -127 to 127 - - auto fftshift_index = 0; - if (fft_index < qu*2) // first half (fft_index = qu; fft_index < qu*2) - fftshift_index = fft_index + 2*qu; // goes to back - else // 2nd half (fft_index = qu*2; fft_index < qu*3) - fftshift_index = fft_index - 2*qu; // goes to front - - v[fftshift_index] = {real, imag}; - } - } - - ifft(v, fft_width, tmp); - - // normalize - volatile int32_t maximum = 1; - for (uint32_t i = 0; i < fft_width; i++) { - if (v[i].real() > maximum) - maximum = v[i].real(); - if (v[i].real() < -maximum) - maximum = -v[i].real(); - if (v[i].imag() > maximum) - maximum = v[i].imag(); - if (v[i].imag() < -maximum) - maximum = -v[i].imag(); - } - - if (maximum == 1) { // a black line - for (uint32_t i = 0; i < fft_width; i++) - v[i] = {0, 0}; - } - else { - for (uint32_t i = 0; i < fft_width; i++) { - v[i] = { (int8_t)((int32_t)v[i].real() * 120 / maximum), (int8_t)((int32_t)v[i].imag() * 120 / maximum)}; - } - } - - delete tmp; - next_line_data = v; - ui++; - } - else { - chThdSleepMilliseconds(1); - } - } + int ui = 0; + init_genrand(22267); + + while (true) { + if (fifo.is_empty() == false && next_line_data == nullptr) { + std::vector data; + fifo.out(data); + + auto picture_width = data.size(); + + auto fft_width = picture_width * 2; + auto qu = fft_width / 4; + + complex16_t* v = new complex16_t[fft_width]; + complex16_t* tmp = new complex16_t[fft_width]; + + for (uint32_t fft_index = 0; fft_index < fft_width; fft_index++) { + if (fft_index < qu) { + } else if (fft_index < qu * 3) { + // TODO: Improve index handling + auto image_index = fft_index - qu; + + auto bin_power = data[image_index]; // 0 to 255 + auto bin_phase = genrand_int31(); // 0 to 255 + + // rotate by random angle + auto phase_cos = (sine_table_i8[((int)(bin_phase + 0x40)) & 0xFF]); // -127 to 127 + auto phase_sin = (sine_table_i8[((int)(bin_phase)) & 0xFF]); + + auto real = (int16_t)((int16_t)phase_cos * bin_power / 255); // -127 to 127 + auto imag = (int16_t)((int16_t)phase_sin * bin_power / 255); // -127 to 127 + + auto fftshift_index = 0; + if (fft_index < qu * 2) // first half (fft_index = qu; fft_index < qu*2) + fftshift_index = fft_index + 2 * qu; // goes to back + else // 2nd half (fft_index = qu*2; fft_index < qu*3) + fftshift_index = fft_index - 2 * qu; // goes to front + + v[fftshift_index] = {real, imag}; + } + } + + ifft(v, fft_width, tmp); + + // normalize + volatile int32_t maximum = 1; + for (uint32_t i = 0; i < fft_width; i++) { + if (v[i].real() > maximum) + maximum = v[i].real(); + if (v[i].real() < -maximum) + maximum = -v[i].real(); + if (v[i].imag() > maximum) + maximum = v[i].imag(); + if (v[i].imag() < -maximum) + maximum = -v[i].imag(); + } + + if (maximum == 1) { // a black line + for (uint32_t i = 0; i < fft_width; i++) + v[i] = {0, 0}; + } else { + for (uint32_t i = 0; i < fft_width; i++) { + v[i] = {(int8_t)((int32_t)v[i].real() * 120 / maximum), (int8_t)((int32_t)v[i].imag() * 120 / maximum)}; + } + } + + delete tmp; + next_line_data = v; + ui++; + } else { + chThdSleepMilliseconds(1); + } + } } void SpectrumPainterProcessor::on_message(const Message* const msg) { - - switch(msg->id) { - case Message::ID::SpectrumPainterBufferRequestConfigure: - { - const auto message = *reinterpret_cast(msg); - current_line_width = message.width; - current_bw = message.bw / 500; - - if (message.update == false) { - SpectrumPainterBufferConfigureResponseMessage response { &fifo }; - shared_memory.application_queue.push(response); - - if (configured == false) { - thread = chThdCreateStatic(thread_wa, sizeof(thread_wa), - NORMALPRIO, SpectrumPainterProcessor::fn, - this - ); - - configured = true; - } - } - break; - } - - default: - break; - } + switch (msg->id) { + case Message::ID::SpectrumPainterBufferRequestConfigure: { + const auto message = *reinterpret_cast(msg); + current_line_width = message.width; + current_bw = message.bw / 500; + + if (message.update == false) { + SpectrumPainterBufferConfigureResponseMessage response{&fifo}; + shared_memory.application_queue.push(response); + + if (configured == false) { + thread = chThdCreateStatic(thread_wa, sizeof(thread_wa), + NORMALPRIO, SpectrumPainterProcessor::fn, + this); + + configured = true; + } + } + break; + } + + default: + break; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_spectrum_painter.hpp b/firmware/baseband/proc_spectrum_painter.hpp index 92ee29fee..2b7bc041e 100644 --- a/firmware/baseband/proc_spectrum_painter.hpp +++ b/firmware/baseband/proc_spectrum_painter.hpp @@ -26,21 +26,21 @@ #include "baseband_thread.hpp" class SpectrumPainterProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const p) override; - void run(); + public: + void execute(const buffer_c8_t& buffer) override; + void on_message(const Message* const p) override; + void run(); -private: - bool configured { false }; - BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - Thread* thread {nullptr}; + private: + bool configured{false}; + BasebandThread baseband_thread{3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + Thread* thread{nullptr}; -protected: - static msg_t fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); + protected: + static msg_t fn(void* arg) { + auto obj = static_cast(arg); + obj->run(); - return 0; - } + return 0; + } }; diff --git a/firmware/baseband/proc_sstvtx.cpp b/firmware/baseband/proc_sstvtx.cpp index e11ede10e..c95747cb1 100644 --- a/firmware/baseband/proc_sstvtx.cpp +++ b/firmware/baseband/proc_sstvtx.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,131 +28,128 @@ // This is called at 3072000/2048 = 1500Hz void SSTVTXProcessor::execute(const buffer_c8_t& buffer) { - - if (!configured) return; - - for (size_t i = 0; i < buffer.count; i++) { - - if (!sample_count) { - - // This FSM is a mess. It seems to do a lot where it shouldn't (I/Q loop), - // but it actually doesn't do much. Used for sequencing the different parts - // of the scanline. Todo: simplify ! - - if (state == STATE_CALIBRATION) { - // Once per picture - tone_delta = calibration_sequence[substep].first; - sample_count = calibration_sequence[substep].second; - if (substep == 2) { - substep = 0; - state = STATE_VIS; - } else - substep++; - } else if (state == STATE_VIS) { - // Once per picture - if (substep == 10) { - current_scanline = &scanline_buffer[buffer_flip]; - buffer_flip ^= 1; - // Ask application for a new scanline - shared_memory.application_queue.push(sig_message); - // Do we have to transmit a start tone ? - if (current_scanline->start_tone.duration) { - state = STATE_SYNC; - tone_delta = current_scanline->start_tone.frequency; - sample_count = current_scanline->start_tone.duration; - } else { - state = STATE_PIXELS; - tone_delta = current_scanline->gap_tone.frequency; - sample_count = current_scanline->gap_tone.duration; - } - } else { - tone_delta = vis_code_sequence[substep]; - sample_count = SSTV_MS2S(30); // A VIS code bit is 30ms - substep++; - } - } else if (state == STATE_SYNC) { - // Once per scanline, optional - state = STATE_PIXELS; - tone_delta = current_scanline->gap_tone.frequency; - sample_count = current_scanline->gap_tone.duration; - } else if (state == STATE_PIXELS) { - // Many times per scanline - tone_delta = SSTV_F2D(1500 + ((current_scanline->luma[pixel_index] * 800) / 256)); - sample_count = pixel_duration; - pixel_index++; - - if (pixel_index >= 320) { - // Scanline done, (dirty) state jump - pixel_index = 0; - state = STATE_VIS; - substep = 10; - } - } - } else { - sample_count--; - } - - // Tone synth - tone_sample = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]); - tone_phase += tone_delta; - - // FM - delta = tone_sample * fm_delta; - - phase += delta; - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); - - buffer.p[i] = {re, im}; - } + if (!configured) return; + + for (size_t i = 0; i < buffer.count; i++) { + if (!sample_count) { + // This FSM is a mess. It seems to do a lot where it shouldn't (I/Q loop), + // but it actually doesn't do much. Used for sequencing the different parts + // of the scanline. Todo: simplify ! + + if (state == STATE_CALIBRATION) { + // Once per picture + tone_delta = calibration_sequence[substep].first; + sample_count = calibration_sequence[substep].second; + if (substep == 2) { + substep = 0; + state = STATE_VIS; + } else + substep++; + } else if (state == STATE_VIS) { + // Once per picture + if (substep == 10) { + current_scanline = &scanline_buffer[buffer_flip]; + buffer_flip ^= 1; + // Ask application for a new scanline + shared_memory.application_queue.push(sig_message); + // Do we have to transmit a start tone ? + if (current_scanline->start_tone.duration) { + state = STATE_SYNC; + tone_delta = current_scanline->start_tone.frequency; + sample_count = current_scanline->start_tone.duration; + } else { + state = STATE_PIXELS; + tone_delta = current_scanline->gap_tone.frequency; + sample_count = current_scanline->gap_tone.duration; + } + } else { + tone_delta = vis_code_sequence[substep]; + sample_count = SSTV_MS2S(30); // A VIS code bit is 30ms + substep++; + } + } else if (state == STATE_SYNC) { + // Once per scanline, optional + state = STATE_PIXELS; + tone_delta = current_scanline->gap_tone.frequency; + sample_count = current_scanline->gap_tone.duration; + } else if (state == STATE_PIXELS) { + // Many times per scanline + tone_delta = SSTV_F2D(1500 + ((current_scanline->luma[pixel_index] * 800) / 256)); + sample_count = pixel_duration; + pixel_index++; + + if (pixel_index >= 320) { + // Scanline done, (dirty) state jump + pixel_index = 0; + state = STATE_VIS; + substep = 10; + } + } + } else { + sample_count--; + } + + // Tone synth + tone_sample = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]); + tone_phase += tone_delta; + + // FM + delta = tone_sample * fm_delta; + + phase += delta; + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); + + buffer.p[i] = {re, im}; + } } void SSTVTXProcessor::on_message(const Message* const msg) { - const auto message = *reinterpret_cast(msg); - uint8_t vis_code; - - switch(msg->id) { - case Message::ID::SSTVConfigure: - pixel_duration = message.pixel_duration; - - if (!pixel_duration) { - configured = false; // Shutdown - return; - } - - vis_code = message.vis_code; - - // VIS code: - // 1200, (0=1300, 1=1100), 1200 - vis_code_sequence[0] = SSTV_VIS_SS; - for (uint32_t c = 0; c < 8; c++) - vis_code_sequence[c + 1] = ((vis_code >> c) & 1) ? SSTV_VIS_ONE : SSTV_VIS_ZERO; - vis_code_sequence[9] = SSTV_VIS_SS; - - fm_delta = 9000 * (0xFFFFFFULL / 3072000); // Fixed bw for now - - pixel_index = 0; - sample_count = 0; - tone_phase = 0; - state = STATE_CALIBRATION; - substep = 0; - - configured = true; - break; - - case Message::ID::FIFOData: - memcpy(&scanline_buffer[buffer_flip], static_cast(msg)->data, sizeof(sstv_scanline)); - break; - - default: - break; - } + const auto message = *reinterpret_cast(msg); + uint8_t vis_code; + + switch (msg->id) { + case Message::ID::SSTVConfigure: + pixel_duration = message.pixel_duration; + + if (!pixel_duration) { + configured = false; // Shutdown + return; + } + + vis_code = message.vis_code; + + // VIS code: + // 1200, (0=1300, 1=1100), 1200 + vis_code_sequence[0] = SSTV_VIS_SS; + for (uint32_t c = 0; c < 8; c++) + vis_code_sequence[c + 1] = ((vis_code >> c) & 1) ? SSTV_VIS_ONE : SSTV_VIS_ZERO; + vis_code_sequence[9] = SSTV_VIS_SS; + + fm_delta = 9000 * (0xFFFFFFULL / 3072000); // Fixed bw for now + + pixel_index = 0; + sample_count = 0; + tone_phase = 0; + state = STATE_CALIBRATION; + substep = 0; + + configured = true; + break; + + case Message::ID::FIFOData: + memcpy(&scanline_buffer[buffer_flip], static_cast(msg)->data, sizeof(sstv_scanline)); + break; + + default: + break; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_sstvtx.hpp b/firmware/baseband/proc_sstvtx.hpp index 9e217d6f7..2a14ffd4c 100644 --- a/firmware/baseband/proc_sstvtx.hpp +++ b/firmware/baseband/proc_sstvtx.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -31,53 +31,51 @@ using namespace sstv; class SSTVTXProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const p) override; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const p) override; + + private: + // 1900 300ms + // 1200 10ms + // 1900 300ms + const std::pair calibration_sequence[3] = { + {SSTV_F2D(1900), SSTV_MS2S(300)}, + {SSTV_F2D(1200), SSTV_MS2S(10)}, + {SSTV_F2D(1900), SSTV_MS2S(300)}}; + + enum state_t { + STATE_CALIBRATION = 0, + STATE_VIS, + STATE_SYNC, + STATE_PIXELS + }; + + state_t state{}; + + bool configured{false}; + + BasebandThread baseband_thread{3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; -private: + uint32_t vis_code_sequence[10]{}; + sstv_scanline scanline_buffer[2]{}; + uint8_t buffer_flip{0}, substep{0}; + uint32_t pixel_duration{}; - // 1900 300ms - // 1200 10ms - // 1900 300ms - const std::pair calibration_sequence[3] = { - { SSTV_F2D(1900), SSTV_MS2S(300) }, - { SSTV_F2D(1200), SSTV_MS2S(10) }, - { SSTV_F2D(1900), SSTV_MS2S(300) } - }; - - enum state_t { - STATE_CALIBRATION = 0, - STATE_VIS, - STATE_SYNC, - STATE_PIXELS - }; - - state_t state { }; - - bool configured { false }; - - BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; + sstv_scanline* current_scanline{}; - uint32_t vis_code_sequence[10] { }; - sstv_scanline scanline_buffer[2] { }; - uint8_t buffer_flip { 0 }, substep { 0 }; - uint32_t pixel_duration { }; + uint8_t pixel_luma{}; + uint32_t fm_delta{0}; + uint32_t tone_phase{0}; + uint32_t tone_delta{0}; + uint32_t pixel_index{0}; + uint32_t sample_count{0}; + uint32_t phase{0}, sphase{0}; + int32_t tone_sample{0}, delta{0}; + int8_t re{}, im{}; - sstv_scanline * current_scanline { }; - - uint8_t pixel_luma { }; - uint32_t fm_delta { 0 }; - uint32_t tone_phase { 0 }; - uint32_t tone_delta { 0 }; - uint32_t pixel_index { 0 }; - uint32_t sample_count { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - int32_t tone_sample { 0 }, delta { 0 }; - int8_t re { }, im { }; - - RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest }; + RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest}; }; #endif diff --git a/firmware/baseband/proc_test.cpp b/firmware/baseband/proc_test.cpp index d31da2ac6..46a422922 100644 --- a/firmware/baseband/proc_test.cpp +++ b/firmware/baseband/proc_test.cpp @@ -27,29 +27,29 @@ #include "event_m4.hpp" TestProcessor::TestProcessor() { - decim_0.configure(taps_11k0_decim_0.taps, 33554432); - decim_1.configure(taps_11k0_decim_1.taps, 131072); + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); } void TestProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ + /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto decimator_out = decim_1_out; + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decimator_out = decim_1_out; - /* 38.4kHz, 32 samples */ - feed_channel_stats(decimator_out); + /* 38.4kHz, 32 samples */ + feed_channel_stats(decimator_out); - for(size_t i=0; i() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_test.hpp b/firmware/baseband/proc_test.hpp index ae1e216ff..4f0889deb 100644 --- a/firmware/baseband/proc_test.hpp +++ b/firmware/baseband/proc_test.hpp @@ -44,43 +44,42 @@ #include class TestProcessor : public BasebandProcessor { -public: - TestProcessor(); - - void execute(const buffer_c8_t& buffer) override; + public: + TestProcessor(); -private: - static constexpr size_t baseband_fs = 2457600*2; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; + void execute(const buffer_c8_t& buffer) override; - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; + private: + static constexpr size_t baseband_fs = 2457600 * 2; - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; - dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; - clock_recovery::ClockRecovery clock_recovery_fsk_9600 { - 38400, 19192, { 0.00555f }, - [this](const float raw_symbol) { - const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - this->packet_builder_fsk_9600_CC1101.execute(sliced_symbol); - } - }; - PacketBuilder packet_builder_fsk_9600_CC1101 { - { 0b01010110010110100101101001101010, 32, 1 }, // Manchester 0x1337 - { }, - { 22 * 8 }, - [this](const baseband::Packet& packet) { - const TestAppPacketMessage message { packet }; - shared_memory.application_queue.push(message); - } - }; + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + dsp::matched_filter::MatchedFilter mf{baseband::ais::square_taps_38k4_1t_p, 2}; + + clock_recovery::ClockRecovery clock_recovery_fsk_9600{ + 38400, + 19192, + {0.00555f}, + [this](const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + this->packet_builder_fsk_9600_CC1101.execute(sliced_symbol); + }}; + PacketBuilder packet_builder_fsk_9600_CC1101{ + {0b01010110010110100101101001101010, 32, 1}, // Manchester 0x1337 + {}, + {22 * 8}, + [this](const baseband::Packet& packet) { + const TestAppPacketMessage message{packet}; + shared_memory.application_queue.push(message); + }}; }; -#endif/*__PROC_TEST_H__*/ +#endif /*__PROC_TEST_H__*/ diff --git a/firmware/baseband/proc_tones.cpp b/firmware/baseband/proc_tones.cpp index 2e93ef3d4..1af4bc473 100644 --- a/firmware/baseband/proc_tones.cpp +++ b/firmware/baseband/proc_tones.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,135 +28,133 @@ // This is called at 1536000/2048 = 750Hz void TonesProcessor::execute(const buffer_c8_t& buffer) { - - if (!configured) return; - - ai = 0; - - for (size_t i = 0; i < buffer.count; i++) { - - // Tone generation at full samplerate - if (silence_count) { - // Just occupy channel with carrier - silence_count--; - if (!silence_count) { - sample_count = 0; - tone_a_phase = 0; - tone_b_phase = 0; - } - tone_sample = 0; - re = 0; - im = 0; - } else { - if (!sample_count) { - digit = shared_memory.bb_data.tones_data.message[digit_pos]; - if (digit_pos >= message_length) { - configured = false; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - return; - } else { - txprogress_message.progress = digit_pos; // Inform UI about progress - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } - - digit_pos++; - - if (digit >= 32) { // || (tone_deltas[digit] == 0) - sample_count = shared_memory.bb_data.tones_data.silence; - } else { - if (!dual_tone) { - tone_a_delta = tone_deltas[digit]; - } else { - tone_a_delta = tone_deltas[digit << 1]; - tone_b_delta = tone_deltas[(digit << 1) + 1]; - } - sample_count = tone_durations[digit]; - } - } else { - sample_count--; - } - - // Ugly - if ((digit >= 32) || (tone_deltas[digit] == 0)) { - tone_sample = 0; - } else { - if (!dual_tone) { - tone_sample = (sine_table_i8[(tone_a_phase & 0xFF000000U) >> 24]); - tone_a_phase += tone_a_delta; - } else { - tone_sample = sine_table_i8[(tone_a_phase & 0xFF000000U) >> 24] >> 1; - tone_sample += sine_table_i8[(tone_b_phase & 0xFF000000U) >> 24] >> 1; - - tone_a_phase += tone_a_delta; - tone_b_phase += tone_b_delta; - } - } - - // FM - delta = tone_sample * fm_delta; - - phase += delta; - sphase = phase + (64 << 24); - - re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); - } - - // Headphone output sample generation: 1536000/24000 = 64 - if (audio_out) { - if (!as) { - as = 64; - audio_buffer.p[ai++] = tone_sample * 128; - } else { - as--; - } - } - - buffer.p[i] = {re, im}; - } - - if (audio_out) audio_output.write(audio_buffer); + if (!configured) return; + + ai = 0; + + for (size_t i = 0; i < buffer.count; i++) { + // Tone generation at full samplerate + if (silence_count) { + // Just occupy channel with carrier + silence_count--; + if (!silence_count) { + sample_count = 0; + tone_a_phase = 0; + tone_b_phase = 0; + } + tone_sample = 0; + re = 0; + im = 0; + } else { + if (!sample_count) { + digit = shared_memory.bb_data.tones_data.message[digit_pos]; + if (digit_pos >= message_length) { + configured = false; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + return; + } else { + txprogress_message.progress = digit_pos; // Inform UI about progress + txprogress_message.done = false; + shared_memory.application_queue.push(txprogress_message); + } + + digit_pos++; + + if (digit >= 32) { // || (tone_deltas[digit] == 0) + sample_count = shared_memory.bb_data.tones_data.silence; + } else { + if (!dual_tone) { + tone_a_delta = tone_deltas[digit]; + } else { + tone_a_delta = tone_deltas[digit << 1]; + tone_b_delta = tone_deltas[(digit << 1) + 1]; + } + sample_count = tone_durations[digit]; + } + } else { + sample_count--; + } + + // Ugly + if ((digit >= 32) || (tone_deltas[digit] == 0)) { + tone_sample = 0; + } else { + if (!dual_tone) { + tone_sample = (sine_table_i8[(tone_a_phase & 0xFF000000U) >> 24]); + tone_a_phase += tone_a_delta; + } else { + tone_sample = sine_table_i8[(tone_a_phase & 0xFF000000U) >> 24] >> 1; + tone_sample += sine_table_i8[(tone_b_phase & 0xFF000000U) >> 24] >> 1; + + tone_a_phase += tone_a_delta; + tone_b_phase += tone_b_delta; + } + } + + // FM + delta = tone_sample * fm_delta; + + phase += delta; + sphase = phase + (64 << 24); + + re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); + im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); + } + + // Headphone output sample generation: 1536000/24000 = 64 + if (audio_out) { + if (!as) { + as = 64; + audio_buffer.p[ai++] = tone_sample * 128; + } else { + as--; + } + } + + buffer.p[i] = {re, im}; + } + + if (audio_out) audio_output.write(audio_buffer); } void TonesProcessor::on_message(const Message* const p) { - const auto message = *reinterpret_cast(p); - if (message.id == Message::ID::TonesConfigure) { - message_length = message.tone_count; - - if (message_length) { - silence_count = message.pre_silence; // In samples - for (uint8_t c = 0; c < 32; c++) { - tone_deltas[c] = shared_memory.bb_data.tones_data.tone_defs[c].delta; - tone_durations[c] = shared_memory.bb_data.tones_data.tone_defs[c].duration; - } - fm_delta = message.fm_delta * (0xFFFFFFULL / 1536000); - audio_out = message.audio_out; - dual_tone = message.dual_tone; - - if (audio_out) audio_output.configure(false); - - txprogress_message.done = false; - txprogress_message.progress = 0; - - digit_pos = 0; - sample_count = 0; - tone_a_phase = 0; - tone_b_phase = 0; - as = 0; - - configured = true; - } else { - configured = false; - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - } - } + const auto message = *reinterpret_cast(p); + if (message.id == Message::ID::TonesConfigure) { + message_length = message.tone_count; + + if (message_length) { + silence_count = message.pre_silence; // In samples + for (uint8_t c = 0; c < 32; c++) { + tone_deltas[c] = shared_memory.bb_data.tones_data.tone_defs[c].delta; + tone_durations[c] = shared_memory.bb_data.tones_data.tone_defs[c].duration; + } + fm_delta = message.fm_delta * (0xFFFFFFULL / 1536000); + audio_out = message.audio_out; + dual_tone = message.dual_tone; + + if (audio_out) audio_output.configure(false); + + txprogress_message.done = false; + txprogress_message.progress = 0; + + digit_pos = 0; + sample_count = 0; + tone_a_phase = 0; + tone_b_phase = 0; + as = 0; + + configured = true; + } else { + configured = false; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + } + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_tones.hpp b/firmware/baseband/proc_tones.hpp index 85d9d1ba2..5c567ae03 100644 --- a/firmware/baseband/proc_tones.hpp +++ b/firmware/baseband/proc_tones.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -29,41 +29,40 @@ #include "audio_output.hpp" class TonesProcessor : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const p) override; + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const p) override; + + private: + bool configured = false; + + BasebandThread baseband_thread{1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit}; + + std::array audio{}; // 2048/64 + const buffer_s16_t audio_buffer{ + (int16_t*)audio.data(), + sizeof(audio) / sizeof(int16_t)}; + + uint32_t tone_deltas[32]; + uint32_t tone_durations[32]; -private: - bool configured = false; - - BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - - std::array audio { }; // 2048/64 - const buffer_s16_t audio_buffer { - (int16_t*)audio.data(), - sizeof(audio) / sizeof(int16_t) - }; + bool audio_out{false}; + bool dual_tone{false}; + uint32_t fm_delta{0}; + uint32_t tone_a_phase{0}, tone_b_phase{0}; + uint32_t tone_a_delta{0}, tone_b_delta{0}; + uint8_t digit_pos{0}; + uint8_t digit{0}; + uint32_t silence_count{0}, sample_count{0}; + uint32_t message_length{0}; + uint32_t phase{0}, sphase{0}; + int32_t tone_sample{0}, delta{0}; + int8_t re{0}, im{0}; + uint8_t as{0}, ai{0}; - uint32_t tone_deltas[32]; - uint32_t tone_durations[32]; - - bool audio_out { false }; - bool dual_tone { false }; - uint32_t fm_delta { 0 }; - uint32_t tone_a_phase { 0 }, tone_b_phase { 0 }; - uint32_t tone_a_delta { 0 }, tone_b_delta { 0 }; - uint8_t digit_pos { 0 }; - uint8_t digit { 0 }; - uint32_t silence_count { 0 }, sample_count { 0 }; - uint32_t message_length { 0 }; - uint32_t phase { 0 }, sphase { 0 }; - int32_t tone_sample { 0 }, delta { 0 }; - int8_t re { 0 }, im { 0 }; - uint8_t as { 0 }, ai { 0 }; - - TXProgressMessage txprogress_message { }; - AudioOutput audio_output { }; + TXProgressMessage txprogress_message{}; + AudioOutput audio_output{}; }; #endif diff --git a/firmware/baseband/proc_tpms.cpp b/firmware/baseband/proc_tpms.cpp index c9224c3c6..0495ae162 100644 --- a/firmware/baseband/proc_tpms.cpp +++ b/firmware/baseband/proc_tpms.cpp @@ -26,40 +26,40 @@ #include "event_m4.hpp" TPMSProcessor::TPMSProcessor() { - decim_0.configure(taps_200k_decim_0.taps, 33554432); - decim_1.configure(taps_200k_decim_1.taps, 131072); + decim_0.configure(taps_200k_decim_0.taps, 33554432); + decim_1.configure(taps_200k_decim_1.taps, 131072); } void TPMSProcessor::execute(const buffer_c8_t& buffer) { - /* 2.4576MHz, 2048 samples */ + /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decimator_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decimator_out = decim_1.execute(decim_0_out, dst_buffer); - /* 307.2kHz, 256 samples */ - feed_channel_stats(decimator_out); + /* 307.2kHz, 256 samples */ + feed_channel_stats(decimator_out); - for(size_t i=0; ipacket_builder_ook_8k192_schrader.execute(symbol); - }); - clock_recovery_ook_8k4(slicer_history, [this](const bool symbol) { - this->packet_builder_ook_8k4_schrader.execute(symbol); - }); - } + clock_recovery_ook_8k192(slicer_history, [this](const bool symbol) { + this->packet_builder_ook_8k192_schrader.execute(symbol); + }); + clock_recovery_ook_8k4(slicer_history, [this](const bool symbol) { + this->packet_builder_ook_8k4_schrader.execute(symbol); + }); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_tpms.hpp b/firmware/baseband/proc_tpms.hpp index 884baface..41438be30 100644 --- a/firmware/baseband/proc_tpms.hpp +++ b/firmware/baseband/proc_tpms.hpp @@ -46,98 +46,101 @@ // Translate+rectangular filter // sample=307.2k, deviation=38400, symbol=19200 // Length: 16 taps, 1 symbols, 2 cycles of sinusoid -constexpr std::array, 16> rect_taps_307k2_38k4_1t_19k2_p { { - { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, - { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, - { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, - { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, - { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, - { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, - { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, - { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, -} }; +constexpr std::array, 16> rect_taps_307k2_38k4_1t_19k2_p{{ + {6.2500000000e-02f, 0.0000000000e+00f}, + {4.4194173824e-02f, 4.4194173824e-02f}, + {0.0000000000e+00f, 6.2500000000e-02f}, + {-4.4194173824e-02f, 4.4194173824e-02f}, + {-6.2500000000e-02f, 0.0000000000e+00f}, + {-4.4194173824e-02f, -4.4194173824e-02f}, + {0.0000000000e+00f, -6.2500000000e-02f}, + {4.4194173824e-02f, -4.4194173824e-02f}, + {6.2500000000e-02f, 0.0000000000e+00f}, + {4.4194173824e-02f, 4.4194173824e-02f}, + {0.0000000000e+00f, 6.2500000000e-02f}, + {-4.4194173824e-02f, 4.4194173824e-02f}, + {-6.2500000000e-02f, 0.0000000000e+00f}, + {-4.4194173824e-02f, -4.4194173824e-02f}, + {0.0000000000e+00f, -6.2500000000e-02f}, + {4.4194173824e-02f, -4.4194173824e-02f}, +}}; class TPMSProcessor : public BasebandProcessor { -public: - TPMSProcessor(); - - void execute(const buffer_c8_t& buffer) override; - -private: - static constexpr size_t baseband_fs = 2457600; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; - dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; - - dsp::matched_filter::MatchedFilter mf_38k4_1t_19k2 { rect_taps_307k2_38k4_1t_19k2_p, 8 }; - - clock_recovery::ClockRecovery clock_recovery_fsk_19k2 { - 38400, 19200, { 0.0555f }, - [this](const float raw_symbol) { - const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - this->packet_builder_fsk_19k2_schrader.execute(sliced_symbol); - } - }; - PacketBuilder packet_builder_fsk_19k2_schrader { - { 0b010101010101010101010101010110, 30, 1 }, - { }, - { 160 }, - [this](const baseband::Packet& packet) { - const TPMSPacketMessage message { tpms::SignalType::FSK_19k2_Schrader, packet }; - shared_memory.application_queue.push(message); - } - }; - - static constexpr float channel_rate_in = 307200.0f; - static constexpr size_t channel_decimation = 2; - static constexpr float channel_sample_rate = channel_rate_in / channel_decimation; - OOKSlicerMagSquaredInt ook_slicer_5sps { channel_sample_rate / 8400 + 1}; - uint32_t slicer_history { 0 }; - - OOKClockRecovery clock_recovery_ook_8k192 { - channel_sample_rate / 8192.0f - }; - - PacketBuilder packet_builder_ook_8k192_schrader { - /* Preamble: 11*2, 01*14, 11, 10 - * Payload: 37 Manchester-encoded bits - * Bit rate: 4096 Hz - */ - { 0b010101010101010101011110, 24, 0 }, - { }, - { 37 * 2 }, - [](const baseband::Packet& packet) { - const TPMSPacketMessage message { tpms::SignalType::OOK_8k192_Schrader, packet }; - shared_memory.application_queue.push(message); - } - }; - - OOKClockRecovery clock_recovery_ook_8k4 { - channel_sample_rate / 8400.0f - }; - - PacketBuilder packet_builder_ook_8k4_schrader { - /* Preamble: 01*40, 01, 10, 01, 01 - * Payload: 76 Manchester-encoded bits - * Bit rate: 4200 Hz - */ - { 0b01010101010101010101010101100101, 32, 0 }, - { }, - { 76 * 2 }, - [](const baseband::Packet& packet) { - const TPMSPacketMessage message { tpms::SignalType::OOK_8k4_Schrader, packet }; - shared_memory.application_queue.push(message); - } - }; + public: + TPMSProcessor(); + + void execute(const buffer_c8_t& buffer) override; + + private: + static constexpr size_t baseband_fs = 2457600; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + + dsp::matched_filter::MatchedFilter mf_38k4_1t_19k2{rect_taps_307k2_38k4_1t_19k2_p, 8}; + + clock_recovery::ClockRecovery clock_recovery_fsk_19k2{ + 38400, + 19200, + {0.0555f}, + [this](const float raw_symbol) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + this->packet_builder_fsk_19k2_schrader.execute(sliced_symbol); + }}; + PacketBuilder packet_builder_fsk_19k2_schrader{ + {0b010101010101010101010101010110, 30, 1}, + {}, + {160}, + [this](const baseband::Packet& packet) { + const TPMSPacketMessage message{tpms::SignalType::FSK_19k2_Schrader, packet}; + shared_memory.application_queue.push(message); + }}; + + static constexpr float channel_rate_in = 307200.0f; + static constexpr size_t channel_decimation = 2; + static constexpr float channel_sample_rate = channel_rate_in / channel_decimation; + OOKSlicerMagSquaredInt ook_slicer_5sps{channel_sample_rate / 8400 + 1}; + uint32_t slicer_history{0}; + + OOKClockRecovery clock_recovery_ook_8k192{ + channel_sample_rate / 8192.0f}; + + PacketBuilder packet_builder_ook_8k192_schrader{ + /* Preamble: 11*2, 01*14, 11, 10 + * Payload: 37 Manchester-encoded bits + * Bit rate: 4096 Hz + */ + {0b010101010101010101011110, 24, 0}, + {}, + {37 * 2}, + [](const baseband::Packet& packet) { + const TPMSPacketMessage message{tpms::SignalType::OOK_8k192_Schrader, packet}; + shared_memory.application_queue.push(message); + }}; + + OOKClockRecovery clock_recovery_ook_8k4{ + channel_sample_rate / 8400.0f}; + + PacketBuilder packet_builder_ook_8k4_schrader{ + /* Preamble: 01*40, 01, 10, 01, 01 + * Payload: 76 Manchester-encoded bits + * Bit rate: 4200 Hz + */ + {0b01010101010101010101010101100101, 32, 0}, + {}, + {76 * 2}, + [](const baseband::Packet& packet) { + const TPMSPacketMessage message{tpms::SignalType::OOK_8k4_Schrader, packet}; + shared_memory.application_queue.push(message); + }}; }; -#endif/*__PROC_TPMS_H__*/ +#endif /*__PROC_TPMS_H__*/ diff --git a/firmware/baseband/proc_wfm_audio.cpp b/firmware/baseband/proc_wfm_audio.cpp index 79f27f374..c1b1e58e1 100644 --- a/firmware/baseband/proc_wfm_audio.cpp +++ b/firmware/baseband/proc_wfm_audio.cpp @@ -30,167 +30,165 @@ #include void WidebandFMAudio::execute(const buffer_c8_t& buffer) { - if( !configured ) { - return; - } - - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto channel = decim_1.execute(decim_0_out, dst_buffer); - - // TODO: Feed channel_stats post-decimation data? - feed_channel_stats(channel); - - spectrum_samples += channel.count; - if( spectrum_samples >= spectrum_interval_samples ) { - spectrum_samples -= spectrum_interval_samples; - channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); - } - - /* 384kHz complex[256] - * -> FM demodulation - * -> 384kHz int16_t[256] */ - /* TODO: To improve adjacent channel rejection, implement complex channel filter: - * pass < +/- 100kHz, stop > +/- 200kHz - */ - - auto audio_oversampled = demod.execute(channel, work_audio_buffer); - - /* 384kHz int16_t[256] - * -> 4th order CIC decimation by 2, gain of 1 - * -> 192kHz int16_t[128] */ - auto audio_4fs = audio_dec_1.execute(audio_oversampled, work_audio_buffer); - - /* 192kHz int16_t[128] - * -> 4th order CIC decimation by 2, gain of 1 - * -> 96kHz int16_t[64] */ - auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer); - - // Input: 96kHz int16_t[64] - // audio_spectrum_decimator piles up 256 samples before doing FFT computation - // This sends an AudioSpectrum every: sample rate/buffer size/refresh period = 3072000/2048/50 = 30 Hz - // When audio_spectrum_timer expires, the audio spectrum computation is triggered - - // 0~3: feed continuous audio - // 4~31: ignore, wrap at 31 - - audio_spectrum_timer++; - if (audio_spectrum_timer == 50) { - audio_spectrum_timer = 0; - audio_spectrum_state = FEED; - } - - switch (audio_spectrum_state) { - case FEED: - // Convert audio to "complex" just so the FFT can be done :/ - for (size_t i = 0; i < 64; i++) { - complex_audio[i] = { (int16_t)(work_audio_buffer.p[i] / 32), (int16_t)0 }; - } - audio_spectrum_decimator.feed( - complex_audio_buffer, - [this](const buffer_c16_t& data) { - this->post_message(data); - } - ); - break; - case FFT: - // Spread the FFT workload in time to avoid making the audio skip - // "8" comes from the log2() of the size of audio_spectrum: log2(256) = 8 - if (fft_step < 8) { - fft_c_preswapped(audio_spectrum, fft_step, fft_step + 1); - fft_step++; - } else { - const size_t spectrum_end = spectrum.db.size(); - for(size_t i=0; i FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1 - * -> 48kHz int16_t[32] */ - auto audio = audio_filter.execute(audio_2fs, work_audio_buffer); - - /* -> 48kHz int16_t[32] */ - audio_output.write(audio); - + if (!configured) { + return; + } + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto channel = decim_1.execute(decim_0_out, dst_buffer); + + // TODO: Feed channel_stats post-decimation data? + feed_channel_stats(channel); + + spectrum_samples += channel.count; + if (spectrum_samples >= spectrum_interval_samples) { + spectrum_samples -= spectrum_interval_samples; + channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition); + } + + /* 384kHz complex[256] + * -> FM demodulation + * -> 384kHz int16_t[256] */ + /* TODO: To improve adjacent channel rejection, implement complex channel filter: + * pass < +/- 100kHz, stop > +/- 200kHz + */ + + auto audio_oversampled = demod.execute(channel, work_audio_buffer); + + /* 384kHz int16_t[256] + * -> 4th order CIC decimation by 2, gain of 1 + * -> 192kHz int16_t[128] */ + auto audio_4fs = audio_dec_1.execute(audio_oversampled, work_audio_buffer); + + /* 192kHz int16_t[128] + * -> 4th order CIC decimation by 2, gain of 1 + * -> 96kHz int16_t[64] */ + auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer); + + // Input: 96kHz int16_t[64] + // audio_spectrum_decimator piles up 256 samples before doing FFT computation + // This sends an AudioSpectrum every: sample rate/buffer size/refresh period = 3072000/2048/50 = 30 Hz + // When audio_spectrum_timer expires, the audio spectrum computation is triggered + + // 0~3: feed continuous audio + // 4~31: ignore, wrap at 31 + + audio_spectrum_timer++; + if (audio_spectrum_timer == 50) { + audio_spectrum_timer = 0; + audio_spectrum_state = FEED; + } + + switch (audio_spectrum_state) { + case FEED: + // Convert audio to "complex" just so the FFT can be done :/ + for (size_t i = 0; i < 64; i++) { + complex_audio[i] = {(int16_t)(work_audio_buffer.p[i] / 32), (int16_t)0}; + } + audio_spectrum_decimator.feed( + complex_audio_buffer, + [this](const buffer_c16_t& data) { + this->post_message(data); + }); + break; + case FFT: + // Spread the FFT workload in time to avoid making the audio skip + // "8" comes from the log2() of the size of audio_spectrum: log2(256) = 8 + if (fft_step < 8) { + fft_c_preswapped(audio_spectrum, fft_step, fft_step + 1); + fft_step++; + } else { + const size_t spectrum_end = spectrum.db.size(); + for (size_t i = 0; i < spectrum_end; i++) { + // const auto corrected_sample = spectrum_window_hamming_3(audio_spectrum, i); + const auto corrected_sample = audio_spectrum[i]; + const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f)); + const float db = mag2_to_dbv_norm(mag2); + constexpr float mag_scale = 5.0f; + const unsigned int v = (db * mag_scale) + 255.0f; + spectrum.db[i] = std::max(0U, std::min(255U, v)); + } + AudioSpectrumMessage message{&spectrum}; + shared_memory.application_queue.push(message); + audio_spectrum_state = IDLE; + } + break; + default: + break; + } + + /* 96kHz int16_t[64] + * -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1 + * -> 48kHz int16_t[32] */ + auto audio = audio_filter.execute(audio_2fs, work_audio_buffer); + + /* -> 48kHz int16_t[32] */ + audio_output.write(audio); } void WidebandFMAudio::post_message(const buffer_c16_t& data) { - // This is called when audio_spectrum_decimator is filled up to 256 samples - fft_swap(data, audio_spectrum); - audio_spectrum_state = FFT; - fft_step = 0; + // This is called when audio_spectrum_decimator is filled up to 256 samples + fft_swap(data, audio_spectrum); + audio_spectrum_state = FFT; + fft_step = 0; } void WidebandFMAudio::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); - break; - - case Message::ID::WFMConfigure: - configure(*reinterpret_cast(message)); - break; - - case Message::ID::CaptureConfig: - capture_config(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::WFMConfigure: + configure(*reinterpret_cast(message)); + break; + + case Message::ID::CaptureConfig: + capture_config(*reinterpret_cast(message)); + break; + + default: + break; + } } void WidebandFMAudio::configure(const WFMConfigureMessage& message) { - constexpr size_t decim_0_input_fs = baseband_fs; - constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; - constexpr size_t decim_1_input_fs = decim_0_output_fs; - constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - constexpr size_t demod_input_fs = decim_1_output_fs; + constexpr size_t demod_input_fs = decim_1_output_fs; - spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz; - spectrum_samples = 0; + spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz; + spectrum_samples = 0; - decim_0.configure(message.decim_0_filter.taps, 33554432); - decim_1.configure(message.decim_1_filter.taps, 131072); - channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs; - channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs; - channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs; - demod.configure(demod_input_fs, message.deviation); - audio_filter.configure(message.audio_filter.taps); - audio_output.configure(message.audio_hpf_config, message.audio_deemph_config); + decim_0.configure(message.decim_0_filter.taps, 33554432); + decim_1.configure(message.decim_1_filter.taps, 131072); + channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs; + channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs; + channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs; + demod.configure(demod_input_fs, message.deviation); + audio_filter.configure(message.audio_filter.taps); + audio_output.configure(message.audio_hpf_config, message.audio_deemph_config); - channel_spectrum.set_decimation_factor(1); + channel_spectrum.set_decimation_factor(1); - configured = true; + configured = true; } void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) { - if( message.config ) { - audio_output.set_stream(std::make_unique(message.config)); - } else { - audio_output.set_stream(nullptr); - } + if (message.config) { + audio_output.set_stream(std::make_unique(message.config)); + } else { + audio_output.set_stream(nullptr); + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_wfm_audio.hpp b/firmware/baseband/proc_wfm_audio.hpp index 734ed9ef7..85e3faff1 100644 --- a/firmware/baseband/proc_wfm_audio.hpp +++ b/firmware/baseband/proc_wfm_audio.hpp @@ -36,69 +36,66 @@ #include "spectrum_collector.hpp" class WidebandFMAudio : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; - - void on_message(const Message* const message) override; - -private: - static constexpr size_t baseband_fs = 3072000; - static constexpr auto spectrum_rate_hz = 50.0f; - - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; - - std::array dst { }; - const buffer_c16_t dst_buffer { - dst.data(), - dst.size() - }; - // work_audio_buffer and dst_buffer use the same data pointer - const buffer_s16_t work_audio_buffer { - (int16_t*)dst.data(), - sizeof(dst) / sizeof(int16_t) - }; - - std::array complex_audio { }; - const buffer_c16_t complex_audio_buffer { - complex_audio.data(), - complex_audio.size() - }; - - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; - dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; - int32_t channel_filter_low_f = 0; - int32_t channel_filter_high_f = 0; - int32_t channel_filter_transition = 0; - - dsp::demodulate::FM demod { }; - dsp::decimate::DecimateBy2CIC4Real audio_dec_1 { }; - dsp::decimate::DecimateBy2CIC4Real audio_dec_2 { }; - dsp::decimate::FIR64AndDecimateBy2Real audio_filter { }; - - AudioOutput audio_output { }; - - // For fs=96kHz FFT streaming - BlockDecimator audio_spectrum_decimator { 1 }; - std::array, 256> audio_spectrum { }; - uint32_t audio_spectrum_timer { 0 }; - enum AudioSpectrumState { - IDLE = 0, - FEED, - FFT - }; - AudioSpectrumState audio_spectrum_state { IDLE }; - AudioSpectrum spectrum { }; - uint32_t fft_step { 0 }; - - SpectrumCollector channel_spectrum { }; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; - - bool configured { false }; - void configure(const WFMConfigureMessage& message); - void capture_config(const CaptureConfigMessage& message); - void post_message(const buffer_c16_t& data); + public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + + private: + static constexpr size_t baseband_fs = 3072000; + static constexpr auto spectrum_rate_hz = 50.0f; + + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive}; + RSSIThread rssi_thread{NORMALPRIO + 10}; + + std::array dst{}; + const buffer_c16_t dst_buffer{ + dst.data(), + dst.size()}; + // work_audio_buffer and dst_buffer use the same data pointer + const buffer_s16_t work_audio_buffer{ + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t)}; + + std::array complex_audio{}; + const buffer_c16_t complex_audio_buffer{ + complex_audio.data(), + complex_audio.size()}; + + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + int32_t channel_filter_low_f = 0; + int32_t channel_filter_high_f = 0; + int32_t channel_filter_transition = 0; + + dsp::demodulate::FM demod{}; + dsp::decimate::DecimateBy2CIC4Real audio_dec_1{}; + dsp::decimate::DecimateBy2CIC4Real audio_dec_2{}; + dsp::decimate::FIR64AndDecimateBy2Real audio_filter{}; + + AudioOutput audio_output{}; + + // For fs=96kHz FFT streaming + BlockDecimator audio_spectrum_decimator{1}; + std::array, 256> audio_spectrum{}; + uint32_t audio_spectrum_timer{0}; + enum AudioSpectrumState { + IDLE = 0, + FEED, + FFT + }; + AudioSpectrumState audio_spectrum_state{IDLE}; + AudioSpectrum spectrum{}; + uint32_t fft_step{0}; + + SpectrumCollector channel_spectrum{}; + size_t spectrum_interval_samples = 0; + size_t spectrum_samples = 0; + + bool configured{false}; + void configure(const WFMConfigureMessage& message); + void capture_config(const CaptureConfigMessage& message); + void post_message(const buffer_c16_t& data); }; -#endif/*__PROC_WFM_AUDIO_H__*/ +#endif /*__PROC_WFM_AUDIO_H__*/ diff --git a/firmware/baseband/proc_wideband_spectrum.cpp b/firmware/baseband/proc_wideband_spectrum.cpp index 641391660..a7377b0b8 100644 --- a/firmware/baseband/proc_wideband_spectrum.cpp +++ b/firmware/baseband/proc_wideband_spectrum.cpp @@ -29,62 +29,60 @@ #include void WidebandSpectrum::execute(const buffer_c8_t& buffer) { - // 2048 complex8_t samples per buffer. - // 102.4us per buffer. 20480 instruction cycles per buffer. - - if (!configured) return; + // 2048 complex8_t samples per buffer. + // 102.4us per buffer. 20480 instruction cycles per buffer. - if( phase == 0 ) { - std::fill(spectrum.begin(), spectrum.end(), 0); - } + if (!configured) return; - for(size_t i=0; i(msg); - - switch(msg->id) { - case Message::ID::UpdateSpectrum: - case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(msg); - break; - - case Message::ID::WidebandSpectrumConfig: - baseband_fs = message.sampling_rate; - trigger = message.trigger; - baseband_thread.set_sampling_rate(baseband_fs); - phase = 0; - configured = true; - break; + const WidebandSpectrumConfigMessage message = *reinterpret_cast(msg); + + switch (msg->id) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(msg); + break; + + case Message::ID::WidebandSpectrumConfig: + baseband_fs = message.sampling_rate; + trigger = message.trigger; + baseband_thread.set_sampling_rate(baseband_fs); + phase = 0; + configured = true; + break; - default: - break; - } + default: + break; + } } int main() { - EventDispatcher event_dispatcher { std::make_unique() }; - event_dispatcher.run(); - return 0; + EventDispatcher event_dispatcher{std::make_unique()}; + event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_wideband_spectrum.hpp b/firmware/baseband/proc_wideband_spectrum.hpp index 416374802..e4c7d7260 100644 --- a/firmware/baseband/proc_wideband_spectrum.hpp +++ b/firmware/baseband/proc_wideband_spectrum.hpp @@ -35,24 +35,24 @@ #include class WidebandSpectrum : public BasebandProcessor { -public: - void execute(const buffer_c8_t& buffer) override; + public: + void execute(const buffer_c8_t& buffer) override; - void on_message(const Message* const message) override; + void on_message(const Message* const message) override; -private: - bool configured = false; - - size_t baseband_fs = 20000000; + private: + bool configured = false; - BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; - RSSIThread rssi_thread { NORMALPRIO + 10 }; + size_t baseband_fs = 20000000; - SpectrumCollector channel_spectrum { }; + BasebandThread baseband_thread{baseband_fs, this, NORMALPRIO + 20}; + RSSIThread rssi_thread{NORMALPRIO + 10}; - std::array spectrum { }; + SpectrumCollector channel_spectrum{}; - size_t phase = 0, trigger = 127; + std::array spectrum{}; + + size_t phase = 0, trigger = 127; }; -#endif/*__PROC_WIDEBAND_SPECTRUM_H__*/ +#endif /*__PROC_WIDEBAND_SPECTRUM_H__*/ diff --git a/firmware/baseband/rssi.cpp b/firmware/baseband/rssi.cpp index 1abf10a17..4f9122591 100644 --- a/firmware/baseband/rssi.cpp +++ b/firmware/baseband/rssi.cpp @@ -41,33 +41,33 @@ namespace rssi { constexpr uint8_t adc1_sel = (1 << portapack::adc1_rssi_input); const auto adc1_interrupt_mask = flp2(adc1_sel); -constexpr adc::CR adc1_cr { - .sel = adc1_sel, - .clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */ - .resolution = 9, /* Ten clocks */ - .edge = 0, +constexpr adc::CR adc1_cr{ + .sel = adc1_sel, + .clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */ + .resolution = 9, /* Ten clocks */ + .edge = 0, }; -constexpr adc::Config adc1_config { - .cr = adc1_cr, +constexpr adc::Config adc1_config{ + .cr = adc1_cr, }; void init() { - adc1::clock_enable(); - adc1::interrupts_disable(); - adc1::power_up(adc1_config); - adc1::interrupts_enable(adc1_interrupt_mask); + adc1::clock_enable(); + adc1::interrupts_disable(); + adc1::power_up(adc1_config); + adc1::interrupts_enable(adc1_interrupt_mask); - dma::init(); + dma::init(); } void start() { - dma::enable(); - adc1::start_burst(); + dma::enable(); + adc1::start_burst(); } void stop() { - dma::disable(); - adc1::stop_burst(); + dma::disable(); + adc1::stop_burst(); } } /* namespace rssi */ diff --git a/firmware/baseband/rssi.hpp b/firmware/baseband/rssi.hpp index d340f338b..4c6d7320a 100644 --- a/firmware/baseband/rssi.hpp +++ b/firmware/baseband/rssi.hpp @@ -40,4 +40,4 @@ void stop(); } /* namespace rssi */ } /* namespace rf */ -#endif/*__RSSI_H__*/ +#endif /*__RSSI_H__*/ diff --git a/firmware/baseband/rssi_dma.cpp b/firmware/baseband/rssi_dma.cpp index 355698ad4..fbd603706 100644 --- a/firmware/baseband/rssi_dma.cpp +++ b/firmware/baseband/rssi_dma.cpp @@ -52,123 +52,123 @@ constexpr uint32_t gpdma_src_peripheral = gpdma_peripheral; constexpr uint32_t gpdma_dest_peripheral = gpdma_peripheral; constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) { - return { - .lm = gpdma_ahb_master_lli_fetch, - .r = 0, - .lli = reinterpret_cast(lli), - }; + return { + .lm = gpdma_ahb_master_lli_fetch, + .r = 0, + .lli = reinterpret_cast(lli), + }; } constexpr gpdma::channel::Control control(const size_t number_of_transfers) { - return { - .transfersize = number_of_transfers, - .sbsize = 0, /* Burst size: 1 transfer */ - .dbsize = 0, /* Burst size: 1 transfer */ - .swidth = 0, /* Source transfer width: byte (8 bits) */ - .dwidth = 2, /* Destination transfer width: word (32 bits) */ - .s = gpdma_ahb_master_peripheral, - .d = gpdma_ahb_master_memory, - .si = 0, - .di = 1, - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = 1, - }; + return { + .transfersize = number_of_transfers, + .sbsize = 0, /* Burst size: 1 transfer */ + .dbsize = 0, /* Burst size: 1 transfer */ + .swidth = 0, /* Source transfer width: byte (8 bits) */ + .dwidth = 2, /* Destination transfer width: word (32 bits) */ + .s = gpdma_ahb_master_peripheral, + .d = gpdma_ahb_master_memory, + .si = 0, + .di = 1, + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = 1, + }; } constexpr gpdma::channel::Config config() { - return { - .e = 0, - .srcperipheral = gpdma_src_peripheral, - .destperipheral = gpdma_dest_peripheral, - .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, - .ie = 1, - .itc = 1, - .l = 0, - .a = 0, - .h = 0, - }; + return { + .e = 0, + .srcperipheral = gpdma_src_peripheral, + .destperipheral = gpdma_dest_peripheral, + .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, + .ie = 1, + .itc = 1, + .l = 0, + .a = 0, + .h = 0, + }; } struct buffers_config_t { - size_t count; - size_t items_per_buffer; + size_t count; + size_t items_per_buffer; }; -static buffers_config_t buffers_config; +static buffers_config_t buffers_config; -static sample_t *samples { nullptr }; -static gpdma::channel::LLI *lli { nullptr }; +static sample_t* samples{nullptr}; +static gpdma::channel::LLI* lli{nullptr}; static ThreadWait thread_wait; static void transfer_complete() { - const auto next_lli_index = gpdma_channel.next_lli() - &lli[0]; - thread_wait.wake_from_interrupt(next_lli_index); + const auto next_lli_index = gpdma_channel.next_lli() - &lli[0]; + thread_wait.wake_from_interrupt(next_lli_index); } static void dma_error() { - thread_wait.wake_from_interrupt(-1); - disable(); + thread_wait.wake_from_interrupt(-1); + disable(); } void init() { - gpdma_channel.set_handlers(transfer_complete, dma_error); + gpdma_channel.set_handlers(transfer_complete, dma_error); - // LPC_GPDMA->SYNC |= (1 << gpdma_peripheral); + // LPC_GPDMA->SYNC |= (1 << gpdma_peripheral); } void allocate(size_t buffer_count, size_t items_per_buffer) { - buffers_config = { - .count = buffer_count, - .items_per_buffer = items_per_buffer, - }; - - const auto peripheral = reinterpret_cast(&LPC_ADC1->DR[portapack::adc1_rssi_input]) + 1; - const auto control_value = control(gpdma::buffer_words(buffers_config.items_per_buffer, 1)); - - samples = new sample_t[buffers_config.count * buffers_config.items_per_buffer]; - lli = new gpdma::channel::LLI[buffers_config.count]; - - for(size_t i=0; i(&samples[i * buffers_config.items_per_buffer]); - lli[i].srcaddr = peripheral; - lli[i].destaddr = memory; - lli[i].lli = lli_pointer(&lli[(i + 1) % buffers_config.count]); - lli[i].control = control_value; - } + buffers_config = { + .count = buffer_count, + .items_per_buffer = items_per_buffer, + }; + + const auto peripheral = reinterpret_cast(&LPC_ADC1->DR[portapack::adc1_rssi_input]) + 1; + const auto control_value = control(gpdma::buffer_words(buffers_config.items_per_buffer, 1)); + + samples = new sample_t[buffers_config.count * buffers_config.items_per_buffer]; + lli = new gpdma::channel::LLI[buffers_config.count]; + + for (size_t i = 0; i < buffers_config.count; i++) { + const auto memory = reinterpret_cast(&samples[i * buffers_config.items_per_buffer]); + lli[i].srcaddr = peripheral; + lli[i].destaddr = memory; + lli[i].lli = lli_pointer(&lli[(i + 1) % buffers_config.count]); + lli[i].control = control_value; + } } void free() { - delete samples; - delete lli; + delete samples; + delete lli; } void enable() { - const auto gpdma_config = config(); - gpdma_channel.configure(lli[0], gpdma_config); - gpdma_channel.enable(); + const auto gpdma_config = config(); + gpdma_channel.configure(lli[0], gpdma_config); + gpdma_channel.enable(); } bool is_enabled() { - return gpdma_channel.is_enabled(); + return gpdma_channel.is_enabled(); } void disable() { - gpdma_channel.disable(); + gpdma_channel.disable(); } rf::rssi::buffer_t wait_for_buffer() { - const auto next_index = thread_wait.sleep(); - - if( next_index >= 0 ) { - const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count; - return { reinterpret_cast(lli[free_index].destaddr), buffers_config.items_per_buffer }; - } else { - // TODO: Should I return here, or loop if RDY_RESET? - return { nullptr, 0 }; - } + const auto next_index = thread_wait.sleep(); + + if (next_index >= 0) { + const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count; + return {reinterpret_cast(lli[free_index].destaddr), buffers_config.items_per_buffer}; + } else { + // TODO: Should I return here, or loop if RDY_RESET? + return {nullptr, 0}; + } } } /* namespace dma */ diff --git a/firmware/baseband/rssi_dma.hpp b/firmware/baseband/rssi_dma.hpp index 263dcfcf9..d088adfe1 100644 --- a/firmware/baseband/rssi_dma.hpp +++ b/firmware/baseband/rssi_dma.hpp @@ -46,4 +46,4 @@ rf::rssi::buffer_t wait_for_buffer(); } /* namespace rssi */ } /* namespace rf */ -#endif/*__RSSI_DMA_H__*/ +#endif /*__RSSI_DMA_H__*/ diff --git a/firmware/baseband/rssi_stats_collector.hpp b/firmware/baseband/rssi_stats_collector.hpp index 68143768d..f1aa5df55 100644 --- a/firmware/baseband/rssi_stats_collector.hpp +++ b/firmware/baseband/rssi_stats_collector.hpp @@ -29,47 +29,47 @@ #include class RSSIStatisticsCollector { -public: - template - void process(const rf::rssi::buffer_t& buffer, Callback callback) { - auto p = buffer.p; - if( p == nullptr ) { - return; - } + public: + template + void process(const rf::rssi::buffer_t& buffer, Callback callback) { + auto p = buffer.p; + if (p == nullptr) { + return; + } - if( statistics.count == 0 ) { - const auto value_0 = *p; - statistics.min = value_0; - statistics.max = value_0; - } - - const auto end = &p[buffer.count]; - while(p < end) { - const uint32_t value = *(p++); + if (statistics.count == 0) { + const auto value_0 = *p; + statistics.min = value_0; + statistics.max = value_0; + } - if( statistics.min > value ) { - statistics.min = value; - } - if( statistics.max < value ) { - statistics.max = value; - } + const auto end = &p[buffer.count]; + while (p < end) { + const uint32_t value = *(p++); - statistics.accumulator += value; - } - statistics.count += buffer.count; + if (statistics.min > value) { + statistics.min = value; + } + if (statistics.max < value) { + statistics.max = value; + } - const size_t samples_per_update = buffer.sampling_rate * update_interval; + statistics.accumulator += value; + } + statistics.count += buffer.count; - if( statistics.count >= samples_per_update ) { - callback(statistics); - statistics.accumulator = 0; - statistics.count = 0; - } - } + const size_t samples_per_update = buffer.sampling_rate * update_interval; -private: - static constexpr float update_interval { 0.1f }; - RSSIStatistics statistics { }; + if (statistics.count >= samples_per_update) { + callback(statistics); + statistics.accumulator = 0; + statistics.count = 0; + } + } + + private: + static constexpr float update_interval{0.1f}; + RSSIStatistics statistics{}; }; -#endif/*__RSSI_STATS_COLLECTOR_H__*/ +#endif /*__RSSI_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband/rssi_thread.cpp b/firmware/baseband/rssi_thread.cpp index 81be13ecd..6dfcb02a5 100644 --- a/firmware/baseband/rssi_thread.cpp +++ b/firmware/baseband/rssi_thread.cpp @@ -33,42 +33,39 @@ WORKING_AREA(rssi_thread_wa, 128); Thread* RSSIThread::thread = nullptr; RSSIThread::RSSIThread(const tprio_t priority) { - thread = chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa), - priority, ThreadBase::fn, - this - ); + thread = chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa), + priority, ThreadBase::fn, + this); } RSSIThread::~RSSIThread() { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; } void RSSIThread::run() { - rf::rssi::init(); - rf::rssi::dma::allocate(4, 400); + rf::rssi::init(); + rf::rssi::dma::allocate(4, 400); - RSSIStatisticsCollector stats; + RSSIStatisticsCollector stats; - rf::rssi::start(); + rf::rssi::start(); - while( !chThdShouldTerminate() ) { - // TODO: Place correct sampling rate into buffer returned here: - const auto buffer_tmp = rf::rssi::dma::wait_for_buffer(); - const rf::rssi::buffer_t buffer { - buffer_tmp.p, buffer_tmp.count, sampling_rate - }; + while (!chThdShouldTerminate()) { + // TODO: Place correct sampling rate into buffer returned here: + const auto buffer_tmp = rf::rssi::dma::wait_for_buffer(); + const rf::rssi::buffer_t buffer{ + buffer_tmp.p, buffer_tmp.count, sampling_rate}; - stats.process( - buffer, - [](const RSSIStatistics& statistics) { - const RSSIStatisticsMessage message { statistics }; - shared_memory.application_queue.push(message); - } - ); - } + stats.process( + buffer, + [](const RSSIStatistics& statistics) { + const RSSIStatisticsMessage message{statistics}; + shared_memory.application_queue.push(message); + }); + } - rf::rssi::stop(); - rf::rssi::dma::free(); + rf::rssi::stop(); + rf::rssi::dma::free(); } diff --git a/firmware/baseband/rssi_thread.hpp b/firmware/baseband/rssi_thread.hpp index 46a0031ba..5bfe66c3d 100644 --- a/firmware/baseband/rssi_thread.hpp +++ b/firmware/baseband/rssi_thread.hpp @@ -29,16 +29,16 @@ #include class RSSIThread : public ThreadBase { -public: - RSSIThread(const tprio_t priority); - ~RSSIThread(); + public: + RSSIThread(const tprio_t priority); + ~RSSIThread(); -private: - void run() override; + private: + void run() override; - static Thread* thread; + static Thread* thread; - const uint32_t sampling_rate { 400000 }; + const uint32_t sampling_rate{400000}; }; -#endif/*__RSSI_THREAD_H__*/ +#endif /*__RSSI_THREAD_H__*/ diff --git a/firmware/baseband/sd_over_usb/diskio.c b/firmware/baseband/sd_over_usb/diskio.c index e3d5f8506..c86037690 100644 --- a/firmware/baseband/sd_over_usb/diskio.c +++ b/firmware/baseband/sd_over_usb/diskio.c @@ -29,10 +29,10 @@ uint32_t get_capacity(void) { return mmcsdGetCardCapacity(&SDCD1); } -bool_t read_block(uint32_t startblk, uint8_t *buf, uint32_t n) { +bool_t read_block(uint32_t startblk, uint8_t* buf, uint32_t n) { return sdcRead(&SDCD1, startblk, buf, n); } -bool_t write_block(uint32_t startblk, uint8_t *buf, uint32_t n) { +bool_t write_block(uint32_t startblk, uint8_t* buf, uint32_t n) { return sdcWrite(&SDCD1, startblk, buf, n); } diff --git a/firmware/baseband/sd_over_usb/diskio.h b/firmware/baseband/sd_over_usb/diskio.h index 2dde43e73..dc930070e 100644 --- a/firmware/baseband/sd_over_usb/diskio.h +++ b/firmware/baseband/sd_over_usb/diskio.h @@ -27,7 +27,7 @@ #include uint32_t get_capacity(void); -bool_t read_block(uint32_t startblk, uint8_t *buf, uint32_t n); -bool_t write_block(uint32_t startblk, uint8_t *buf, uint32_t n); +bool_t read_block(uint32_t startblk, uint8_t* buf, uint32_t n); +bool_t write_block(uint32_t startblk, uint8_t* buf, uint32_t n); #endif /* __DISKIO_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/hackrf_core.c b/firmware/baseband/sd_over_usb/hackrf_core.c index bf20577c4..063bdf884 100644 --- a/firmware/baseband/sd_over_usb/hackrf_core.c +++ b/firmware/baseband/sd_over_usb/hackrf_core.c @@ -42,18 +42,18 @@ #include #ifdef HACKRF_ONE - #include "portapack.h" +#include "portapack.h" #endif #include "gpio_lpc.h" /* GPIO Output PinMux */ static struct gpio_t gpio_led[] = { - GPIO(2, 1), - GPIO(2, 2), - GPIO(2, 8), + GPIO(2, 1), + GPIO(2, 2), + GPIO(2, 8), #ifdef RAD1O - GPIO(5, 26), + GPIO(5, 26), #endif }; @@ -146,17 +146,17 @@ static struct gpio_t gpio_h1r9_hw_sync_enable = GPIO(5, 5); // clang-format on i2c_bus_t i2c0 = { - .obj = (void*) I2C0_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, + .obj = (void*)I2C0_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, }; i2c_bus_t i2c1 = { - .obj = (void*) I2C1_BASE, - .start = i2c_lpc_start, - .stop = i2c_lpc_stop, - .transfer = i2c_lpc_transfer, + .obj = (void*)I2C1_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, }; // const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { @@ -164,407 +164,401 @@ i2c_bus_t i2c1 = { // }; const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { - .duty_cycle_count = 255, + .duty_cycle_count = 255, }; si5351c_driver_t clock_gen = { - .bus = &i2c0, - .i2c_address = 0x60, + .bus = &i2c0, + .i2c_address = 0x60, }; const ssp_config_t ssp_config_max283x = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_16BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, - .gpio_select = &gpio_max283x_select, + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_16BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, + .gpio_select = &gpio_max283x_select, }; const ssp_config_t ssp_config_max5864 = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, - .gpio_select = &gpio_max5864_select, + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, + .gpio_select = &gpio_max5864_select, }; spi_bus_t spi_bus_ssp1 = { - .obj = (void*) SSP1_BASE, - .config = &ssp_config_max5864, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, + .obj = (void*)SSP1_BASE, + .config = &ssp_config_max5864, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, }; max283x_driver_t max283x = {}; max5864_driver_t max5864 = { - .bus = &spi_bus_ssp1, - .target_init = max5864_target_init, + .bus = &spi_bus_ssp1, + .target_init = max5864_target_init, }; const ssp_config_t ssp_config_w25q80bv = { - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 2, - .clock_prescale_rate = 2, - .gpio_select = &gpio_w25q80bv_select, + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 2, + .clock_prescale_rate = 2, + .gpio_select = &gpio_w25q80bv_select, }; spi_bus_t spi_bus_ssp0 = { - .obj = (void*) SSP0_BASE, - .config = &ssp_config_w25q80bv, - .start = spi_ssp_start, - .stop = spi_ssp_stop, - .transfer = spi_ssp_transfer, - .transfer_gather = spi_ssp_transfer_gather, + .obj = (void*)SSP0_BASE, + .config = &ssp_config_w25q80bv, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, }; w25q80bv_driver_t spi_flash = { - .bus = &spi_bus_ssp0, - .gpio_hold = &gpio_w25q80bv_hold, - .gpio_wp = &gpio_w25q80bv_wp, - .target_init = w25q80bv_target_init, + .bus = &spi_bus_ssp0, + .gpio_hold = &gpio_w25q80bv_hold, + .gpio_wp = &gpio_w25q80bv_wp, + .target_init = w25q80bv_target_init, }; sgpio_config_t sgpio_config = { - .gpio_q_invert = &gpio_q_invert, - .gpio_hw_sync_enable = &gpio_hw_sync_enable, - .slice_mode_multislice = true, + .gpio_q_invert = &gpio_q_invert, + .gpio_hw_sync_enable = &gpio_hw_sync_enable, + .slice_mode_multislice = true, }; rf_path_t rf_path = { - .switchctrl = 0, + .switchctrl = 0, #ifdef HACKRF_ONE - .gpio_hp = &gpio_hp, - .gpio_lp = &gpio_lp, - .gpio_tx_mix_bp = &gpio_tx_mix_bp, - .gpio_no_mix_bypass = &gpio_no_mix_bypass, - .gpio_rx_mix_bp = &gpio_rx_mix_bp, - .gpio_tx_amp = &gpio_tx_amp, - .gpio_tx = &gpio_tx, - .gpio_mix_bypass = &gpio_mix_bypass, - .gpio_rx = &gpio_rx, - .gpio_no_tx_amp_pwr = &gpio_no_tx_amp_pwr, - .gpio_amp_bypass = &gpio_amp_bypass, - .gpio_rx_amp = &gpio_rx_amp, - .gpio_no_rx_amp_pwr = &gpio_no_rx_amp_pwr, + .gpio_hp = &gpio_hp, + .gpio_lp = &gpio_lp, + .gpio_tx_mix_bp = &gpio_tx_mix_bp, + .gpio_no_mix_bypass = &gpio_no_mix_bypass, + .gpio_rx_mix_bp = &gpio_rx_mix_bp, + .gpio_tx_amp = &gpio_tx_amp, + .gpio_tx = &gpio_tx, + .gpio_mix_bypass = &gpio_mix_bypass, + .gpio_rx = &gpio_rx, + .gpio_no_tx_amp_pwr = &gpio_no_tx_amp_pwr, + .gpio_amp_bypass = &gpio_amp_bypass, + .gpio_rx_amp = &gpio_rx_amp, + .gpio_no_rx_amp_pwr = &gpio_no_rx_amp_pwr, #endif #ifdef RAD1O - .gpio_tx_rx_n = &gpio_tx_rx_n, - .gpio_tx_rx = &gpio_tx_rx, - .gpio_by_mix = &gpio_by_mix, - .gpio_by_mix_n = &gpio_by_mix_n, - .gpio_by_amp = &gpio_by_amp, - .gpio_by_amp_n = &gpio_by_amp_n, - .gpio_mixer_en = &gpio_mixer_en, - .gpio_low_high_filt = &gpio_low_high_filt, - .gpio_low_high_filt_n = &gpio_low_high_filt_n, - .gpio_tx_amp = &gpio_tx_amp, - .gpio_rx_lna = &gpio_rx_lna, + .gpio_tx_rx_n = &gpio_tx_rx_n, + .gpio_tx_rx = &gpio_tx_rx, + .gpio_by_mix = &gpio_by_mix, + .gpio_by_mix_n = &gpio_by_mix_n, + .gpio_by_amp = &gpio_by_amp, + .gpio_by_amp_n = &gpio_by_amp_n, + .gpio_mixer_en = &gpio_mixer_en, + .gpio_low_high_filt = &gpio_low_high_filt, + .gpio_low_high_filt_n = &gpio_low_high_filt_n, + .gpio_tx_amp = &gpio_tx_amp, + .gpio_rx_lna = &gpio_rx_lna, #endif }; jtag_gpio_t jtag_gpio_cpld = { - .gpio_tms = &gpio_cpld_tms, - .gpio_tck = &gpio_cpld_tck, - .gpio_tdi = &gpio_cpld_tdi, - .gpio_tdo = &gpio_cpld_tdo, + .gpio_tms = &gpio_cpld_tms, + .gpio_tck = &gpio_cpld_tck, + .gpio_tdi = &gpio_cpld_tdi, + .gpio_tdo = &gpio_cpld_tdo, #ifdef HACKRF_ONE - .gpio_pp_tms = &gpio_cpld_pp_tms, - .gpio_pp_tdo = &gpio_cpld_pp_tdo, + .gpio_pp_tms = &gpio_cpld_pp_tms, + .gpio_pp_tdo = &gpio_cpld_pp_tdo, #endif }; jtag_t jtag_cpld = { - .gpio = &jtag_gpio_cpld, + .gpio = &jtag_gpio_cpld, }; -void delay(uint32_t duration) -{ - uint32_t i; +void delay(uint32_t duration) { + uint32_t i; - for (i = 0; i < duration; i++) { - __asm__("nop"); - } + for (i = 0; i < duration; i++) { + __asm__("nop"); + } } -void delay_us_at_mhz(uint32_t us, uint32_t mhz) -{ - // The loop below takes 3 cycles per iteration. - uint32_t loop_iterations = (us * mhz) / 3; - asm volatile("start%=:\n" - " subs %[ITERATIONS], #1\n" // 1 cycle - " bpl start%=\n" // 2 cycles - : - : [ITERATIONS] "r"(loop_iterations)); +void delay_us_at_mhz(uint32_t us, uint32_t mhz) { + // The loop below takes 3 cycles per iteration. + uint32_t loop_iterations = (us * mhz) / 3; + asm volatile( + "start%=:\n" + " subs %[ITERATIONS], #1\n" // 1 cycle + " bpl start%=\n" // 2 cycles + : + : [ITERATIONS] "r"(loop_iterations)); } /* GCD algo from wikipedia */ /* http://en.wikipedia.org/wiki/Greatest_common_divisor */ -static uint32_t gcd(uint32_t u, uint32_t v) -{ - int s; - - if (!u || !v) { - return u | v; - } - - for (s = 0; !((u | v) & 1); s++) { - u >>= 1; - v >>= 1; - } - - while (!(u & 1)) { - u >>= 1; - } - - do { - while (!(v & 1)) { - v >>= 1; - } - - if (u > v) { - uint32_t t; - t = v; - v = u; - u = t; - } - - v = v - u; - } while (v); - - return u << s; +static uint32_t gcd(uint32_t u, uint32_t v) { + int s; + + if (!u || !v) { + return u | v; + } + + for (s = 0; !((u | v) & 1); s++) { + u >>= 1; + v >>= 1; + } + + while (!(u & 1)) { + u >>= 1; + } + + do { + while (!(v & 1)) { + v >>= 1; + } + + if (u > v) { + uint32_t t; + t = v; + v = u; + u = t; + } + + v = v - u; + } while (v); + + return u << s; } -bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) -{ - const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */ - uint32_t MSx_P1, MSx_P2, MSx_P3; - uint32_t a, b, c; - uint32_t rem; - - hackrf_ui()->set_sample_rate(rate_num / 2); - - /* Find best config */ - a = (VCO_FREQ * rate_denom) / rate_num; - - rem = (VCO_FREQ * rate_denom) - (a * rate_num); - - if (!rem) { - /* Integer mode */ - b = 0; - c = 1; - } else { - /* Fractional */ - uint32_t g = gcd(rem, rate_num); - rem /= g; - rate_num /= g; - - if (rate_num < (1 << 20)) { - /* Perfect match */ - b = rem; - c = rate_num; - } else { - /* Approximate */ - c = (1 << 20) - 1; - b = ((uint64_t) c * (uint64_t) rem) / rate_num; - - g = gcd(b, c); - b /= g; - c /= g; - } - } - - bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); - - if (streaming) { - sgpio_cpld_stream_disable(&sgpio_config); - } - - /* Can we enable integer mode ? */ - if (a & 0x1 || b) { - si5351c_set_int_mode(&clock_gen, 0, 0); - } else { - si5351c_set_int_mode(&clock_gen, 0, 1); - } - - /* Final MS values */ - MSx_P1 = 128 * a + (128 * b / c) - 512; - MSx_P2 = (128 * b) % c; - MSx_P3 = c; - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0); - } else { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); - - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter - - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter - } - - if (streaming) { - sgpio_cpld_stream_enable(&sgpio_config); - } - - return true; +bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) { + const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */ + uint32_t MSx_P1, MSx_P2, MSx_P3; + uint32_t a, b, c; + uint32_t rem; + + hackrf_ui()->set_sample_rate(rate_num / 2); + + /* Find best config */ + a = (VCO_FREQ * rate_denom) / rate_num; + + rem = (VCO_FREQ * rate_denom) - (a * rate_num); + + if (!rem) { + /* Integer mode */ + b = 0; + c = 1; + } else { + /* Fractional */ + uint32_t g = gcd(rem, rate_num); + rem /= g; + rate_num /= g; + + if (rate_num < (1 << 20)) { + /* Perfect match */ + b = rem; + c = rate_num; + } else { + /* Approximate */ + c = (1 << 20) - 1; + b = ((uint64_t)c * (uint64_t)rem) / rate_num; + + g = gcd(b, c); + b /= g; + c /= g; + } + } + + bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); + + if (streaming) { + sgpio_cpld_stream_disable(&sgpio_config); + } + + /* Can we enable integer mode ? */ + if (a & 0x1 || b) { + si5351c_set_int_mode(&clock_gen, 0, 0); + } else { + si5351c_set_int_mode(&clock_gen, 0, 1); + } + + /* Final MS values */ + MSx_P1 = 128 * a + (128 * b / c) - 512; + MSx_P2 = (128 * b) % c; + MSx_P3 = c; + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0); + } else { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); // p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); // p1 doesn't matter + } + + if (streaming) { + sgpio_cpld_stream_enable(&sgpio_config); + } + + return true; } -bool sample_rate_set(const uint32_t sample_rate_hz) -{ - uint32_t p1 = 4608; - uint32_t p2 = 0; - uint32_t p3 = 0; - - switch (sample_rate_hz) { - case 8000000: - p1 = SI_INTDIV(50); // 800MHz / 50 = 16 MHz (SGPIO), 8 MHz (codec) - break; - - case 9216000: - // 43.40277777777778: a = 43; b = 29; c = 72 - p1 = 5043; - p2 = 40; - p3 = 72; - break; - - case 10000000: - p1 = SI_INTDIV(40); // 800MHz / 40 = 20 MHz (SGPIO), 10 MHz (codec) - break; - - case 12288000: - // 32.552083333333336: a = 32; b = 159; c = 288 - p1 = 3654; - p2 = 192; - p3 = 288; - break; - - case 12500000: - p1 = SI_INTDIV(32); // 800MHz / 32 = 25 MHz (SGPIO), 12.5 MHz (codec) - break; - - case 16000000: - p1 = SI_INTDIV(25); // 800MHz / 25 = 32 MHz (SGPIO), 16 MHz (codec) - break; - - case 18432000: - // 21.70138888889: a = 21; b = 101; c = 144 - p1 = 2265; - p2 = 112; - p3 = 144; - break; - - case 20000000: - p1 = SI_INTDIV(20); // 800MHz / 20 = 40 MHz (SGPIO), 20 MHz (codec) - break; - - default: - return false; - } - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); - } else { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth( - &clock_gen, - 1, - p1, - 0, - 1, - 0); //p1 doesn't matter - - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth( - &clock_gen, - 2, - p1, - 0, - 1, - 0); //p1 doesn't matter - } - - return true; +bool sample_rate_set(const uint32_t sample_rate_hz) { + uint32_t p1 = 4608; + uint32_t p2 = 0; + uint32_t p3 = 0; + + switch (sample_rate_hz) { + case 8000000: + p1 = SI_INTDIV(50); // 800MHz / 50 = 16 MHz (SGPIO), 8 MHz (codec) + break; + + case 9216000: + // 43.40277777777778: a = 43; b = 29; c = 72 + p1 = 5043; + p2 = 40; + p3 = 72; + break; + + case 10000000: + p1 = SI_INTDIV(40); // 800MHz / 40 = 20 MHz (SGPIO), 10 MHz (codec) + break; + + case 12288000: + // 32.552083333333336: a = 32; b = 159; c = 288 + p1 = 3654; + p2 = 192; + p3 = 288; + break; + + case 12500000: + p1 = SI_INTDIV(32); // 800MHz / 32 = 25 MHz (SGPIO), 12.5 MHz (codec) + break; + + case 16000000: + p1 = SI_INTDIV(25); // 800MHz / 25 = 32 MHz (SGPIO), 16 MHz (codec) + break; + + case 18432000: + // 21.70138888889: a = 21; b = 101; c = 144 + p1 = 2265; + p2 = 112; + p3 = 144; + break; + + case 20000000: + p1 = SI_INTDIV(20); // 800MHz / 20 = 40 MHz (SGPIO), 20 MHz (codec) + break; + + default: + return false; + } + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + } else { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth( + &clock_gen, + 1, + p1, + 0, + 1, + 0); // p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth( + &clock_gen, + 2, + p1, + 0, + 1, + 0); // p1 doesn't matter + } + + return true; } -bool baseband_filter_bandwidth_set(const uint32_t bandwidth_hz) -{ - uint32_t bandwidth_hz_real; - bandwidth_hz_real = max283x_set_lpf_bandwidth(&max283x, bandwidth_hz); +bool baseband_filter_bandwidth_set(const uint32_t bandwidth_hz) { + uint32_t bandwidth_hz_real; + bandwidth_hz_real = max283x_set_lpf_bandwidth(&max283x, bandwidth_hz); - if (bandwidth_hz_real) { - hackrf_ui()->set_filter_bw(bandwidth_hz_real); - } + if (bandwidth_hz_real) { + hackrf_ui()->set_filter_bw(bandwidth_hz_real); + } - return bandwidth_hz_real != 0; + return bandwidth_hz_real != 0; } -/* +/* Configure PLL1 (Main MCU Clock) to max speed (204MHz). Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. This function shall be called after cpu_clock_init(). */ -static void cpu_clock_pll1_max_speed(void) -{ - uint32_t reg_val; +static void cpu_clock_pll1_max_speed(void) { + uint32_t reg_val; - /* This function implements the sequence recommended in: - * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ + /* This function implements the sequence recommended in: + * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ - /* 1. Select the IRC as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); - CGU_BASE_M4_CLK = reg_val; + /* 1. Select the IRC as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); + CGU_BASE_M4_CLK = reg_val; - /* 2. Enable the crystal oscillator. */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; + /* 2. Enable the crystal oscillator. */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; - /* 3. Wait 250us. */ - delay_us_at_mhz(250, 12); + /* 3. Wait 250us. */ + delay_us_at_mhz(250, 12); - /* 4. Set the AUTOBLOCK bit. */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); + /* 4. Set the AUTOBLOCK bit. */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); - /* 5. Reconfigure PLL1 to produce the final output frequency, with the - * crystal oscillator as clock source. */ - reg_val = CGU_PLL1_CTRL; - // clang-format off + /* 5. Reconfigure PLL1 to produce the final output frequency, with the + * crystal oscillator as clock source. */ + reg_val = CGU_PLL1_CTRL; + // clang-format off reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | CGU_PLL1_CTRL_PD_MASK | CGU_PLL1_CTRL_FBSEL_MASK | @@ -581,504 +575,490 @@ static void cpu_clock_pll1_max_speed(void) CGU_PLL1_CTRL_MSEL(16) | CGU_PLL1_CTRL_FBSEL(0) | CGU_PLL1_CTRL_DIRECT(1); - // clang-format on - CGU_PLL1_CTRL = reg_val; + // clang-format on + CGU_PLL1_CTRL = reg_val; - /* 6. Wait for PLL1 to lock. */ - while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {} + /* 6. Wait for PLL1 to lock. */ + while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) { + } - /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ - CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; + /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ + CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; - /* 8. Select PLL1 as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); - CGU_BASE_M4_CLK = reg_val; + /* 8. Select PLL1 as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); + CGU_BASE_M4_CLK = reg_val; - /* 9. Wait 50us. */ - delay_us_at_mhz(50, 102); + /* 9. Wait 50us. */ + delay_us_at_mhz(50, 102); - /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; + /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; } /* clock startup for LPC4320 configure PLL1 to max speed (204MHz). Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ -void cpu_clock_init(void) -{ - /* use IRC as clock source for APB1 (including I2C0) */ - CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); - - /* use IRC as clock source for APB3 */ - CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); - - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - - si5351c_init(&clock_gen); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); - si5351c_set_crystal_configuration(&clock_gen); - si5351c_enable_xo_and_ms_fanout(&clock_gen); - si5351c_configure_pll_sources(&clock_gen); - si5351c_configure_pll_multisynth(&clock_gen); - - /* - * Clocks on HackRF One r9: - * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) - * CLK1 -> RFFC5072/MAX2839 - * CLK2 -> External Clock Output/LPC43xx (power down at boot) - * - * Clocks on other platforms: - * CLK0 -> MAX5864/CPLD - * CLK1 -> CPLD - * CLK2 -> SGPIO - * CLK3 -> External Clock Output (power down at boot) - * CLK4 -> RFFC5072 (MAX2837 on rad1o) - * CLK5 -> MAX2837 (MAX2871 on rad1o) - * CLK6 -> none - * CLK7 -> LPC43xx (uses a 12MHz crystal by default) - */ - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ - si5351c_configure_multisynth( - &clock_gen, - 0, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } else { - /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 4, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 5, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } - - /* MS6/CLK6 is unused. */ - /* MS7/CLK7 is unused. */ - - /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(10000000); - - si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); - // soft reset - si5351c_reset_pll(&clock_gen); - si5351c_enable_clock_outputs(&clock_gen); - - //FIXME disable I2C - /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ - i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); - - /* - * 12MHz clock is entering LPC XTAL1/OSC input now. - * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. - * Set up PLL1 to run from XTAL1 input. - */ - - //FIXME a lot of the details here should be in a CGU driver - - /* set xtal oscillator to low frequency mode */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; - - cpu_clock_pll1_max_speed(); - - /* use XTAL_OSC as clock source for APB1 */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for APB3 */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for PLL0USB */ - CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | - CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); - while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {} - - /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ - /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ - CGU_PLL0USB_MDIV = 0x06167FFA; - CGU_PLL0USB_NP_DIV = 0x00302062; - CGU_PLL0USB_CTRL |= - (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | - CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); - - /* power on PLL0USB and wait until stable */ - CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; - while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {} - - /* use PLL0USB as clock source for USB0 */ - CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | - CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); - - /* Switch peripheral clock over to use PLL1 (204MHz) */ - CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | - CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB1 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB3 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP0_CLK = - CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP1_CLK = - CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); +void cpu_clock_init(void) { + /* use IRC as clock source for APB1 (including I2C0) */ + CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); + + /* use IRC as clock source for APB3 */ + CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); + + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + si5351c_init(&clock_gen); + si5351c_disable_all_outputs(&clock_gen); + si5351c_disable_oeb_pin_control(&clock_gen); + si5351c_power_down_all_clocks(&clock_gen); + si5351c_set_crystal_configuration(&clock_gen); + si5351c_enable_xo_and_ms_fanout(&clock_gen); + si5351c_configure_pll_sources(&clock_gen); + si5351c_configure_pll_multisynth(&clock_gen); + + /* + * Clocks on HackRF One r9: + * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) + * CLK1 -> RFFC5072/MAX2839 + * CLK2 -> External Clock Output/LPC43xx (power down at boot) + * + * Clocks on other platforms: + * CLK0 -> MAX5864/CPLD + * CLK1 -> CPLD + * CLK2 -> SGPIO + * CLK3 -> External Clock Output (power down at boot) + * CLK4 -> RFFC5072 (MAX2837 on rad1o) + * CLK5 -> MAX2837 (MAX2871 on rad1o) + * CLK6 -> none + * CLK7 -> LPC43xx (uses a 12MHz crystal by default) + */ + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ + si5351c_configure_multisynth( + &clock_gen, + 0, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } else { + /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 4, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 5, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } + + /* MS6/CLK6 is unused. */ + /* MS7/CLK7 is unused. */ + + /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ + sample_rate_set(10000000); + + si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); + // soft reset + si5351c_reset_pll(&clock_gen); + si5351c_enable_clock_outputs(&clock_gen); + + // FIXME disable I2C + /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + /* + * 12MHz clock is entering LPC XTAL1/OSC input now. + * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. + * Set up PLL1 to run from XTAL1 input. + */ + + // FIXME a lot of the details here should be in a CGU driver + + /* set xtal oscillator to low frequency mode */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; + + cpu_clock_pll1_max_speed(); + + /* use XTAL_OSC as clock source for APB1 */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for APB3 */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for PLL0USB */ + CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | + CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); + while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) { + } + + /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ + /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ + CGU_PLL0USB_MDIV = 0x06167FFA; + CGU_PLL0USB_NP_DIV = 0x00302062; + CGU_PLL0USB_CTRL |= + (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | + CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); + + /* power on PLL0USB and wait until stable */ + CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; + while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) { + } + + /* use PLL0USB as clock source for USB0 */ + CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | + CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); + + /* Switch peripheral clock over to use PLL1 (204MHz) */ + CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | + CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB1 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB3 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP0_CLK = + CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP1_CLK = + CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); #if (defined JAWBREAKER || defined HACKRF_ONE) - /* Disable unused clocks */ - /* Start with PLLs */ - CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); - - /* Dividers */ - CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); - CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); - CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); - CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); - CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); - - /* Base clocks */ - CGU_BASE_SPIFI_CLK = CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ - CGU_BASE_USB1_CLK = CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ - CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); - CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); - CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); - CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); - //CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); // << BREAKS - CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); - CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); - CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); - CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); - CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); - CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); - CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); - CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); - - /* Disable unused peripheral clocks */ - CCU1_CLK_APB1_CAN1_CFG = 0; - CCU1_CLK_APB1_I2S_CFG = 0; - CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; - CCU1_CLK_APB3_ADC0_CFG = 0; - CCU1_CLK_APB3_ADC1_CFG = 0; - CCU1_CLK_APB3_CAN0_CFG = 0; - CCU1_CLK_APB3_DAC_CFG = 0; - //CCU1_CLK_M4_DMA_CFG = 0; - CCU1_CLK_M4_EMC_CFG = 0; - CCU1_CLK_M4_EMCDIV_CFG = 0; - CCU1_CLK_M4_ETHERNET_CFG = 0; - CCU1_CLK_M4_LCD_CFG = 0; - CCU1_CLK_M4_QEI_CFG = 0; - CCU1_CLK_M4_RITIMER_CFG = 0; - // CCU1_CLK_M4_SCT_CFG = 0; - //CCU1_CLK_M4_SDIO_CFG = 0; // << BREAKS - CCU1_CLK_M4_SPIFI_CFG = 0; - CCU1_CLK_M4_TIMER0_CFG = 0; - //CCU1_CLK_M4_TIMER1_CFG = 0; - //CCU1_CLK_M4_TIMER2_CFG = 0; - CCU1_CLK_M4_TIMER3_CFG = 0; - CCU1_CLK_M4_UART1_CFG = 0; - CCU1_CLK_M4_USART0_CFG = 0; - CCU1_CLK_M4_USART2_CFG = 0; - CCU1_CLK_M4_USART3_CFG = 0; - CCU1_CLK_M4_USB1_CFG = 0; - CCU1_CLK_M4_VADC_CFG = 0; - // CCU1_CLK_SPIFI_CFG = 0; - // CCU1_CLK_USB1_CFG = 0; - // CCU1_CLK_VADC_CFG = 0; - // CCU2_CLK_APB0_UART1_CFG = 0; - // CCU2_CLK_APB0_USART0_CFG = 0; - // CCU2_CLK_APB2_USART2_CFG = 0; - // CCU2_CLK_APB2_USART3_CFG = 0; - // CCU2_CLK_APLL_CFG = 0; - // CCU2_CLK_SDIO_CFG = 0; + /* Disable unused clocks */ + /* Start with PLLs */ + CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); + + /* Dividers */ + CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); + CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); + CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); + CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); + CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); + + /* Base clocks */ + CGU_BASE_SPIFI_CLK = CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ + CGU_BASE_USB1_CLK = CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ + CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); + CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); + CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); + CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); + // CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); // << BREAKS + CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); + CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); + CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); + CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); + CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); + CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); + CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); + CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); + + /* Disable unused peripheral clocks */ + CCU1_CLK_APB1_CAN1_CFG = 0; + CCU1_CLK_APB1_I2S_CFG = 0; + CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; + CCU1_CLK_APB3_ADC0_CFG = 0; + CCU1_CLK_APB3_ADC1_CFG = 0; + CCU1_CLK_APB3_CAN0_CFG = 0; + CCU1_CLK_APB3_DAC_CFG = 0; + // CCU1_CLK_M4_DMA_CFG = 0; + CCU1_CLK_M4_EMC_CFG = 0; + CCU1_CLK_M4_EMCDIV_CFG = 0; + CCU1_CLK_M4_ETHERNET_CFG = 0; + CCU1_CLK_M4_LCD_CFG = 0; + CCU1_CLK_M4_QEI_CFG = 0; + CCU1_CLK_M4_RITIMER_CFG = 0; + // CCU1_CLK_M4_SCT_CFG = 0; + // CCU1_CLK_M4_SDIO_CFG = 0; // << BREAKS + CCU1_CLK_M4_SPIFI_CFG = 0; + CCU1_CLK_M4_TIMER0_CFG = 0; + // CCU1_CLK_M4_TIMER1_CFG = 0; + // CCU1_CLK_M4_TIMER2_CFG = 0; + CCU1_CLK_M4_TIMER3_CFG = 0; + CCU1_CLK_M4_UART1_CFG = 0; + CCU1_CLK_M4_USART0_CFG = 0; + CCU1_CLK_M4_USART2_CFG = 0; + CCU1_CLK_M4_USART3_CFG = 0; + CCU1_CLK_M4_USB1_CFG = 0; + CCU1_CLK_M4_VADC_CFG = 0; + // CCU1_CLK_SPIFI_CFG = 0; + // CCU1_CLK_USB1_CFG = 0; + // CCU1_CLK_VADC_CFG = 0; + // CCU2_CLK_APB0_UART1_CFG = 0; + // CCU2_CLK_APB0_USART0_CFG = 0; + // CCU2_CLK_APB2_USART2_CFG = 0; + // CCU2_CLK_APB2_USART3_CFG = 0; + // CCU2_CLK_APLL_CFG = 0; + // CCU2_CLK_SDIO_CFG = 0; #endif - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - clkin_detect_init(); - } + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + clkin_detect_init(); + } } -clock_source_t activate_best_clock_source(void) -{ +clock_source_t activate_best_clock_source(void) { #ifdef HACKRF_ONE - /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(false); - } + /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(false); + } #endif - clock_source_t source = CLOCK_SOURCE_HACKRF; + clock_source_t source = CLOCK_SOURCE_HACKRF; - /* Check for external clock input. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_EXTERNAL; - } else { + /* Check for external clock input. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_EXTERNAL; + } else { #ifdef HACKRF_ONE - /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ - if (portapack_reference_oscillator && portapack()) { - portapack_reference_oscillator(true); - delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_PORTAPACK; - } else { - portapack_reference_oscillator(false); - } - } + /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(true); + delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_PORTAPACK; + } else { + portapack_reference_oscillator(false); + } + } #endif - /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ - } - - si5351c_set_clock_source( - &clock_gen, - (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); - hackrf_ui()->set_clock_source(source); - return source; + /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ + } + + si5351c_set_clock_source( + &clock_gen, + (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); + hackrf_ui()->set_clock_source(source); + return source; } -void ssp1_set_mode_max283x(void) -{ - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); +void ssp1_set_mode_max283x(void) { + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); } -void ssp1_set_mode_max5864(void) -{ - spi_bus_start(max5864.bus, &ssp_config_max5864); +void ssp1_set_mode_max5864(void) { + spi_bus_start(max5864.bus, &ssp_config_max5864); } -void pin_setup(void) -{ - /* Configure all GPIO as Input (safe state) */ - //gpio_init(); - - /* TDI and TMS pull-ups are required in all JTAG-compliant devices. - * - * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. - * - * The PortaPack may not be present, so pull up the PortaPack TMS pin from the - * microcontroller. - * - * TCK is recommended to be held low, so use microcontroller pull-down. - * - * TDO is undriven except when in Shift-IR or Shift-DR phases. - * Use the microcontroller to pull down to keep from floating. - * - * LPC43xx pull-up and pull-down resistors are approximately 53K. - */ +void pin_setup(void) { + /* Configure all GPIO as Input (safe state) */ + // gpio_init(); + + /* TDI and TMS pull-ups are required in all JTAG-compliant devices. + * + * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. + * + * The PortaPack may not be present, so pull up the PortaPack TMS pin from the + * microcontroller. + * + * TCK is recommended to be held low, so use microcontroller pull-down. + * + * TDO is undriven except when in Shift-IR or Shift-DR phases. + * Use the microcontroller to pull down to keep from floating. + * + * LPC43xx pull-up and pull-down resistors are approximately 53K. + */ #ifdef HACKRF_ONE - scu_pinmux(SCU_PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); - scu_pinmux(SCU_PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); #endif - scu_pinmux(SCU_PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(SCU_PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(SCU_PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); - scu_pinmux(SCU_PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - - /* Configure SCU Pin Mux as GPIO */ - scu_pinmux(SCU_PINMUX_LED1, SCU_GPIO_NOPULL); - scu_pinmux(SCU_PINMUX_LED2, SCU_GPIO_NOPULL); - scu_pinmux(SCU_PINMUX_LED3, SCU_GPIO_NOPULL); + scu_pinmux(SCU_PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(SCU_PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + /* Configure SCU Pin Mux as GPIO */ + scu_pinmux(SCU_PINMUX_LED1, SCU_GPIO_NOPULL); + scu_pinmux(SCU_PINMUX_LED2, SCU_GPIO_NOPULL); + scu_pinmux(SCU_PINMUX_LED3, SCU_GPIO_NOPULL); #ifdef RAD1O - scu_pinmux(SCU_PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); + scu_pinmux(SCU_PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); #endif - /* Configure USB indicators */ + /* Configure USB indicators */ #ifdef JAWBREAKER - scu_pinmux(SCU_PINMUX_USB_LED0, SCU_CONF_FUNCTION3); - scu_pinmux(SCU_PINMUX_USB_LED1, SCU_CONF_FUNCTION3); + scu_pinmux(SCU_PINMUX_USB_LED0, SCU_CONF_FUNCTION3); + scu_pinmux(SCU_PINMUX_USB_LED1, SCU_CONF_FUNCTION3); #endif - gpio_output(&gpio_led[0]); - gpio_output(&gpio_led[1]); - gpio_output(&gpio_led[2]); + gpio_output(&gpio_led[0]); + gpio_output(&gpio_led[1]); + gpio_output(&gpio_led[2]); #ifdef RAD1O - gpio_output(&gpio_led[3]); + gpio_output(&gpio_led[3]); #endif - disable_1v8_power(); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { + disable_1v8_power(); + if (detected_platform() == BOARD_ID_HACKRF1_R9) { #ifdef HACKRF_ONE - gpio_output(&gpio_h1r9_1v8_enable); - scu_pinmux(SCU_H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + gpio_output(&gpio_h1r9_1v8_enable); + scu_pinmux(SCU_H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); #endif - } else { - gpio_output(&gpio_1v8_enable); - scu_pinmux(SCU_PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } + } else { + gpio_output(&gpio_1v8_enable); + scu_pinmux(SCU_PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } #ifdef HACKRF_ONE - /* Safe state: start with VAA turned off: */ - disable_rf_power(); - - /* Configure RF power supply (VAA) switch control signal as output */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_output(&gpio_h1r9_vaa_disable); - } else { - gpio_output(&gpio_vaa_disable); - } + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_output(&gpio_h1r9_vaa_disable); + } else { + gpio_output(&gpio_vaa_disable); + } #endif #ifdef RAD1O - /* Safe state: start with VAA turned off: */ - disable_rf_power(); + /* Safe state: start with VAA turned off: */ + disable_rf_power(); - /* Configure RF power supply (VAA) switch control signal as output */ - gpio_output(&gpio_vaa_enable); + /* Configure RF power supply (VAA) switch control signal as output */ + gpio_output(&gpio_vaa_enable); - /* Disable unused clock outputs. They generate noise. */ - scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); - scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); + /* Disable unused clock outputs. They generate noise. */ + scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); + scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); - scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); #endif - /* enable input on SCL and SDA pins */ - SCU_SFSI2C0 = SCU_I2C0_NOMINAL; + /* enable input on SCL and SDA pins */ + SCU_SFSI2C0 = SCU_I2C0_NOMINAL; - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); - mixer_bus_setup(&mixer); + mixer_bus_setup(&mixer); #ifdef HACKRF_ONE - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - rf_path.gpio_rx = &gpio_h1r9_rx; - sgpio_config.gpio_hw_sync_enable = &gpio_h1r9_hw_sync_enable; - } + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + rf_path.gpio_rx = &gpio_h1r9_rx; + sgpio_config.gpio_hw_sync_enable = &gpio_h1r9_hw_sync_enable; + } #endif - rf_path_pin_setup(&rf_path); + rf_path_pin_setup(&rf_path); - /* Configure external clock in */ - scu_pinmux(SCU_PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); + /* Configure external clock in */ + scu_pinmux(SCU_PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); - sgpio_configure_pin_functions(&sgpio_config); + sgpio_configure_pin_functions(&sgpio_config); } -void enable_1v8_power(void) -{ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { +void enable_1v8_power(void) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { #ifdef HACKRF_ONE - gpio_set(&gpio_h1r9_1v8_enable); + gpio_set(&gpio_h1r9_1v8_enable); #endif - } else { - gpio_set(&gpio_1v8_enable); - } + } else { + gpio_set(&gpio_1v8_enable); + } } -void disable_1v8_power(void) -{ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { +void disable_1v8_power(void) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { #ifdef HACKRF_ONE - gpio_clear(&gpio_h1r9_1v8_enable); + gpio_clear(&gpio_h1r9_1v8_enable); #endif - } else { - gpio_clear(&gpio_1v8_enable); - } + } else { + gpio_clear(&gpio_1v8_enable); + } } #ifdef HACKRF_ONE -void enable_rf_power(void) -{ - uint32_t i; - - /* many short pulses to avoid one big voltage glitch */ - for (i = 0; i < 1000; i++) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(&gpio_h1r9_vaa_disable); - gpio_clear(&gpio_h1r9_vaa_disable); - } else { - gpio_set(&gpio_vaa_disable); - gpio_clear(&gpio_vaa_disable); - } - } +void enable_rf_power(void) { + uint32_t i; + + /* many short pulses to avoid one big voltage glitch */ + for (i = 0; i < 1000; i++) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(&gpio_h1r9_vaa_disable); + gpio_clear(&gpio_h1r9_vaa_disable); + } else { + gpio_set(&gpio_vaa_disable); + gpio_clear(&gpio_vaa_disable); + } + } } -void disable_rf_power(void) -{ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(&gpio_h1r9_vaa_disable); - } else { - gpio_set(&gpio_vaa_disable); - } +void disable_rf_power(void) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(&gpio_h1r9_vaa_disable); + } else { + gpio_set(&gpio_vaa_disable); + } } #endif #ifdef RAD1O -void enable_rf_power(void) -{ - gpio_set(&gpio_vaa_enable); +void enable_rf_power(void) { + gpio_set(&gpio_vaa_enable); - /* Let the voltage stabilize */ - delay(1000000); + /* Let the voltage stabilize */ + delay(1000000); } -void disable_rf_power(void) -{ - gpio_clear(&gpio_vaa_enable); +void disable_rf_power(void) { + gpio_clear(&gpio_vaa_enable); } #endif -void led_on(const led_t led) -{ - gpio_set(&gpio_led[led]); +void led_on(const led_t led) { + gpio_set(&gpio_led[led]); } -void led_off(const led_t led) -{ - gpio_clear(&gpio_led[led]); +void led_off(const led_t led) { + gpio_clear(&gpio_led[led]); } -void led_toggle(const led_t led) -{ - gpio_toggle(&gpio_led[led]); +void led_toggle(const led_t led) { + gpio_toggle(&gpio_led[led]); } -void set_leds(const uint8_t state) -{ - int num_leds = 3; +void set_leds(const uint8_t state) { + int num_leds = 3; #ifdef RAD1O - num_leds = 4; + num_leds = 4; #endif - for (int i = 0; i < num_leds; i++) { - gpio_write(&gpio_led[i], ((state >> i) & 1) == 1); - } + for (int i = 0; i < num_leds; i++) { + gpio_write(&gpio_led[i], ((state >> i) & 1) == 1); + } } -void hw_sync_enable(const hw_sync_mode_t hw_sync_mode) -{ - gpio_write(sgpio_config.gpio_hw_sync_enable, hw_sync_mode == 1); +void hw_sync_enable(const hw_sync_mode_t hw_sync_mode) { + gpio_write(sgpio_config.gpio_hw_sync_enable, hw_sync_mode == 1); } -void halt_and_flash(const uint32_t duration) -{ - /* blink LED1, LED2, and LED3 */ - while (1) { - led_on(LED1); - led_on(LED2); - led_on(LED3); - delay(duration); - led_off(LED1); - led_off(LED2); - led_off(LED3); - delay(duration); - } +void halt_and_flash(const uint32_t duration) { + /* blink LED1, LED2, and LED3 */ + while (1) { + led_on(LED1); + led_on(LED2); + led_on(LED3); + delay(duration); + led_off(LED1); + led_off(LED2); + led_off(LED3); + delay(duration); + } } diff --git a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp index 01bcef05d..13bf7f12e 100644 --- a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp +++ b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp @@ -29,21 +29,21 @@ void irq_usb(void); void usb_transfer(void); CH_IRQ_HANDLER(Vector60) { - irq_usb(); + irq_usb(); } } int main() { - sdcStart(&SDCD1, nullptr); - if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); + sdcStart(&SDCD1, nullptr); + if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); - start_usb(); + start_usb(); - while (true) { - usb_transfer(); - } + while (true) { + usb_transfer(); + } - return 0; + return 0; } void update_performance_counters() {} diff --git a/firmware/baseband/sd_over_usb/scsi.c b/firmware/baseband/sd_over_usb/scsi.c index d5d8b386b..5fae1f928 100644 --- a/firmware/baseband/sd_over_usb/scsi.c +++ b/firmware/baseband/sd_over_usb/scsi.c @@ -42,7 +42,8 @@ void usb_send_bulk(void* const data, const uint32_t maximum_length) { usb_bulk_block_cb, NULL); - while (!usb_bulk_block_done); + while (!usb_bulk_block_done) + ; } void usb_receive_bulk(void* const data, const uint32_t maximum_length) { @@ -55,37 +56,36 @@ void usb_receive_bulk(void* const data, const uint32_t maximum_length) { usb_bulk_block_cb, NULL); - while (!usb_bulk_block_done); + while (!usb_bulk_block_done) + ; } -void usb_send_csw(msd_cbw_t *msd_cbw_data, uint8_t status) { +void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) { msd_csw_t csw = { .signature = MSD_CSW_SIGNATURE, .tag = msd_cbw_data->tag, .data_residue = 0, - .status = status - }; + .status = status}; memcpy(&usb_bulk_buffer[0], &csw, sizeof(msd_csw_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(msd_csw_t)); } -uint8_t handle_inquiry(msd_cbw_t *msd_cbw_data) { +uint8_t handle_inquiry(msd_cbw_t* msd_cbw_data) { (void)msd_cbw_data; scsi_inquiry_response_t ret = { - 0x00, /* direct access block device */ - 0x80, /* removable */ - 0x00, //0x04, /* SPC-2 */ - 0x00, //0x02, /* response data format */ - 0x20, /* response has 0x20 + 4 bytes */ + 0x00, /* direct access block device */ + 0x80, /* removable */ + 0x00, // 0x04, /* SPC-2 */ + 0x00, // 0x02, /* response data format */ + 0x20, /* response has 0x20 + 4 bytes */ 0x00, 0x00, 0x00, "Mayhem", "Portapack MSD", - {'v','1','.','6'} - }; + {'v', '1', '.', '6'}}; memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_inquiry_response_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_inquiry_response_t)); @@ -93,7 +93,7 @@ uint8_t handle_inquiry(msd_cbw_t *msd_cbw_data) { return 0; } -uint8_t handle_inquiry_serial_number(msd_cbw_t *msd_cbw_data) { +uint8_t handle_inquiry_serial_number(msd_cbw_t* msd_cbw_data) { (void)msd_cbw_data; scsi_unit_serial_number_inquiry_response_t ret = { @@ -101,8 +101,7 @@ uint8_t handle_inquiry_serial_number(msd_cbw_t *msd_cbw_data) { .page_code = 0x80, .reserved = 0, .page_length = 0x08, - .serialNumber = "Mayhem" - }; + .serialNumber = "Mayhem"}; memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_unit_serial_number_inquiry_response_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_unit_serial_number_inquiry_response_t)); @@ -110,8 +109,7 @@ uint8_t handle_inquiry_serial_number(msd_cbw_t *msd_cbw_data) { return 0; } - -uint8_t read_format_capacities(msd_cbw_t *msd_cbw_data) { +uint8_t read_format_capacities(msd_cbw_t* msd_cbw_data) { uint16_t len = msd_cbw_data->cmd_data[7] << 8 | msd_cbw_data->cmd_data[8]; if (len != 0) { @@ -119,7 +117,7 @@ uint8_t read_format_capacities(msd_cbw_t *msd_cbw_data) { scsi_read_format_capacities_response_t ret = { .header = {0, 0, 0, 1 * 8 /* num_entries * 8 */}, - .blocknum = {((num_blocks) >> 24)& 0xff, ((num_blocks) >> 16)& 0xff, ((num_blocks) >> 8)& 0xff, num_blocks & 0xff}, + .blocknum = {((num_blocks) >> 24) & 0xff, ((num_blocks) >> 16) & 0xff, ((num_blocks) >> 8) & 0xff, num_blocks & 0xff}, .blocklen = {0b10 /* formated */, 0, (512) >> 8, 0}, }; @@ -130,15 +128,14 @@ uint8_t read_format_capacities(msd_cbw_t *msd_cbw_data) { return 0; } -uint8_t read_capacity10(msd_cbw_t *msd_cbw_data) { +uint8_t read_capacity10(msd_cbw_t* msd_cbw_data) { (void)msd_cbw_data; size_t num_blocks = get_capacity(); scsi_read_capacity10_response_t ret = { .last_block_addr = cpu_to_be32(num_blocks - 1), - .block_size = cpu_to_be32(512) - }; + .block_size = cpu_to_be32(512)}; memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_read_capacity10_response_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_read_capacity10_response_t)); @@ -146,16 +143,15 @@ uint8_t read_capacity10(msd_cbw_t *msd_cbw_data) { return 0; } -uint8_t request_sense(msd_cbw_t *msd_cbw_data) { +uint8_t request_sense(msd_cbw_t* msd_cbw_data) { (void)msd_cbw_data; scsi_sense_response_t ret = { - .byte = { 0x70, 0, SCSI_SENSE_KEY_GOOD, 0, - 0, 0, 0, 8, - 0, 0 ,0 ,0, - SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER, 0, 0, - 0, 0 } - }; + .byte = {0x70, 0, SCSI_SENSE_KEY_GOOD, 0, + 0, 0, 0, 8, + 0, 0, 0, 0, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER, 0, 0, + 0, 0}}; memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_sense_response_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_sense_response_t)); @@ -163,16 +159,15 @@ uint8_t request_sense(msd_cbw_t *msd_cbw_data) { return 0; } -uint8_t mode_sense6(msd_cbw_t *msd_cbw_data) { - (void)msd_cbw_data; +uint8_t mode_sense6(msd_cbw_t* msd_cbw_data) { + (void)msd_cbw_data; - scsi_mode_sense6_response_t ret = { - .byte = { + scsi_mode_sense6_response_t ret = { + .byte = { sizeof(scsi_mode_sense6_response_t) - 1, 0, 0, - 0 } - }; + 0}}; memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_mode_sense6_response_t)); usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_mode_sense6_response_t)); @@ -180,21 +175,21 @@ uint8_t mode_sense6(msd_cbw_t *msd_cbw_data) { return 0; } -static data_request_t decode_data_request(const uint8_t *cmd) { - data_request_t req; - uint32_t lba; - uint16_t blk; +static data_request_t decode_data_request(const uint8_t* cmd) { + data_request_t req; + uint32_t lba; + uint16_t blk; - memcpy(&lba, &cmd[2], sizeof(lba)); - memcpy(&blk, &cmd[7], sizeof(blk)); + memcpy(&lba, &cmd[2], sizeof(lba)); + memcpy(&blk, &cmd[7], sizeof(blk)); - req.first_lba = be32_to_cpu(lba); - req.blk_cnt = be16_to_cpu(blk); + req.first_lba = be32_to_cpu(lba); + req.blk_cnt = be16_to_cpu(blk); - return req; + return req; } -uint8_t data_read10(msd_cbw_t *msd_cbw_data) { +uint8_t data_read10(msd_cbw_t* msd_cbw_data) { data_request_t req = decode_data_request(msd_cbw_data->cmd_data); for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { @@ -205,7 +200,7 @@ uint8_t data_read10(msd_cbw_t *msd_cbw_data) { return 0; } -uint8_t data_write10(msd_cbw_t *msd_cbw_data) { +uint8_t data_write10(msd_cbw_t* msd_cbw_data) { data_request_t req = decode_data_request(msd_cbw_data->cmd_data); for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { @@ -216,18 +211,16 @@ uint8_t data_write10(msd_cbw_t *msd_cbw_data) { return 0; } -void scsi_command(msd_cbw_t *msd_cbw_data) { +void scsi_command(msd_cbw_t* msd_cbw_data) { uint8_t status = 1; switch (msd_cbw_data->cmd_data[0]) { case SCSI_CMD_INQUIRY: if ((msd_cbw_data->cmd_data[1] & 0b1) && msd_cbw_data->cmd_data[2] == 0x80) { status = handle_inquiry_serial_number(msd_cbw_data); - } - else if ((msd_cbw_data->cmd_data[1] & 0b11) || msd_cbw_data->cmd_data[2] != 0) { + } else if ((msd_cbw_data->cmd_data[1] & 0b11) || msd_cbw_data->cmd_data[2] != 0) { status = 1; - } - else { + } else { status = handle_inquiry(msd_cbw_data); } @@ -260,11 +253,11 @@ void scsi_command(msd_cbw_t *msd_cbw_data) { case SCSI_CMD_MODE_SENSE_6: status = mode_sense6(msd_cbw_data); break; - + case SCSI_CMD_READ_FORMAT_CAPACITIES: status = read_format_capacities(msd_cbw_data); break; - + case SCSI_CMD_VERIFY_10: status = 0; break; diff --git a/firmware/baseband/sd_over_usb/scsi.h b/firmware/baseband/sd_over_usb/scsi.h index 9381bdff3..5cde4e329 100644 --- a/firmware/baseband/sd_over_usb/scsi.h +++ b/firmware/baseband/sd_over_usb/scsi.h @@ -35,163 +35,169 @@ #include "hackrf_core.h" #include "usb_bulk_buffer.h" -#define MSD_CBW_SIGNATURE 0x43425355 -#define MSD_CSW_SIGNATURE 0x53425355 - -#define SCSI_CMD_TEST_UNIT_READY 0x00 -#define SCSI_CMD_REQUEST_SENSE 0x03 -#define SCSI_CMD_INQUIRY 0x12 -#define SCSI_CMD_MODE_SENSE_6 0x1A -#define SCSI_CMD_START_STOP_UNIT 0x1B -#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D -#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E -#define SCSI_CMD_READ_CAPACITY_10 0x25 -#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 -#define SCSI_CMD_READ_10 0x28 -#define SCSI_CMD_WRITE_10 0x2A -#define SCSI_CMD_VERIFY_10 0x2F - -#define SCSI_SENSE_KEY_GOOD 0x00 -#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01 -#define SCSI_SENSE_KEY_NOT_READY 0x02 -#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03 -#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04 -#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05 -#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06 -#define SCSI_SENSE_KEY_DATA_PROTECT 0x07 -#define SCSI_SENSE_KEY_BLANK_CHECK 0x08 -#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09 -#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A -#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B -#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D -#define SCSI_SENSE_KEY_MISCOMPARE 0x0E - -#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00 -#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04 -#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24 -#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28 -#define SCSI_ASENSE_WRITE_PROTECTED 0x27 -#define SCSI_ASENSE_FORMAT_ERROR 0x31 -#define SCSI_ASENSE_INVALID_COMMAND 0x20 -#define SCSI_ASENSE_LBA_OUT_OF_RANGE 0x21 -#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A - -#define SCSI_ASENSEQ_NO_QUALIFIER 0x00 -#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01 -#define SCSI_ASENSEQ_INIT_COMMAND_REQUIRED 0x02 -#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07 - -#define MSD_CBW_SIGNATURE 0x43425355 -#define MSD_CSW_SIGNATURE 0x53425355 +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_MODE_SENSE_6 0x1A +#define SCSI_CMD_START_STOP_UNIT 0x1B +#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D +#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_WRITE_10 0x2A +#define SCSI_CMD_VERIFY_10 0x2F + +#define SCSI_SENSE_KEY_GOOD 0x00 +#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_KEY_NOT_READY 0x02 +#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_KEY_DATA_PROTECT 0x07 +#define SCSI_SENSE_KEY_BLANK_CHECK 0x08 +#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09 +#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A +#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D +#define SCSI_SENSE_KEY_MISCOMPARE 0x0E + +#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00 +#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04 +#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28 +#define SCSI_ASENSE_WRITE_PROTECTED 0x27 +#define SCSI_ASENSE_FORMAT_ERROR 0x31 +#define SCSI_ASENSE_INVALID_COMMAND 0x20 +#define SCSI_ASENSE_LBA_OUT_OF_RANGE 0x21 +#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A + +#define SCSI_ASENSEQ_NO_QUALIFIER 0x00 +#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01 +#define SCSI_ASENSEQ_INIT_COMMAND_REQUIRED 0x02 +#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07 + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 #define USB_TRANSFER_SIZE 0x2000 typedef struct { - uint32_t signature; - uint32_t tag; - uint32_t data_len; - uint8_t flags; - uint8_t lun; - uint8_t cmd_len; - uint8_t cmd_data[16]; + uint32_t signature; + uint32_t tag; + uint32_t data_len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd_data[16]; } __attribute__((packed)) msd_cbw_t; typedef struct { - uint8_t peripheral; - uint8_t removable; - uint8_t version; - uint8_t response_data_format; - uint8_t additional_length; - uint8_t sccstp; - uint8_t bqueetc; - uint8_t cmdque; - uint8_t vendorID[8]; - uint8_t productID[16]; - uint8_t productRev[4]; + uint8_t peripheral; + uint8_t removable; + uint8_t version; + uint8_t response_data_format; + uint8_t additional_length; + uint8_t sccstp; + uint8_t bqueetc; + uint8_t cmdque; + uint8_t vendorID[8]; + uint8_t productID[16]; + uint8_t productRev[4]; } scsi_inquiry_response_t; typedef struct { - uint32_t signature; - uint32_t tag; - uint32_t data_residue; - uint8_t status; + uint32_t signature; + uint32_t tag; + uint32_t data_residue; + uint8_t status; } __attribute__((packed)) msd_csw_t; typedef struct { - uint8_t header[4]; - uint8_t blocknum[4]; - uint8_t blocklen[4]; + uint8_t header[4]; + uint8_t blocknum[4]; + uint8_t blocklen[4]; } scsi_read_format_capacities_response_t; typedef struct { - uint32_t last_block_addr; - uint32_t block_size; + uint32_t last_block_addr; + uint32_t block_size; } scsi_read_capacity10_response_t; typedef struct { - uint8_t byte[18]; + uint8_t byte[18]; } scsi_sense_response_t; typedef struct { - uint8_t byte[4]; + uint8_t byte[4]; } scsi_mode_sense6_response_t; typedef struct { - uint32_t first_lba; - uint16_t blk_cnt; + uint32_t first_lba; + uint16_t blk_cnt; } data_request_t; typedef struct { - uint8_t peripheral; - uint8_t page_code; - uint8_t reserved; - uint8_t page_length; - uint8_t serialNumber[8]; + uint8_t peripheral; + uint8_t page_code; + uint8_t reserved; + uint8_t page_length; + uint8_t serialNumber[8]; } scsi_unit_serial_number_inquiry_response_t; static inline uint16_t bswap_16(const uint16_t x) - __attribute__ ((warn_unused_result)) - __attribute__ ((const)) - __attribute__ ((always_inline)); + __attribute__((warn_unused_result)) + __attribute__((const)) + __attribute__((always_inline)); static inline uint16_t bswap_16(const uint16_t x) { - uint8_t tmp; - union { uint16_t x; uint8_t b[2]; } data; - - data.x = x; - tmp = data.b[0]; + uint8_t tmp; + union { + uint16_t x; + uint8_t b[2]; + } data; + + data.x = x; + tmp = data.b[0]; data.b[0] = data.b[1]; data.b[1] = tmp; - + return data.x; } static inline uint32_t bswap_32(const uint32_t x) - __attribute__ ((warn_unused_result)) - __attribute__ ((const)) - __attribute__ ((always_inline)); + __attribute__((warn_unused_result)) + __attribute__((const)) + __attribute__((always_inline)); static inline uint32_t bswap_32(const uint32_t x) { - uint8_t tmp; - union { uint32_t x; uint8_t b[4]; } data; - - data.x = x; - tmp = data.b[0]; + uint8_t tmp; + union { + uint32_t x; + uint8_t b[4]; + } data; + + data.x = x; + tmp = data.b[0]; data.b[0] = data.b[3]; data.b[3] = tmp; - tmp = data.b[1]; + tmp = data.b[1]; data.b[1] = data.b[2]; data.b[2] = tmp; - + return data.x; } -#define be16_to_cpu(x) bswap_16(x) -#define be32_to_cpu(x) bswap_32(x) +#define be16_to_cpu(x) bswap_16(x) +#define be32_to_cpu(x) bswap_32(x) -#define cpu_to_be16(x) bswap_16(x) -#define cpu_to_be32(x) bswap_32(x) +#define cpu_to_be16(x) bswap_16(x) +#define cpu_to_be32(x) bswap_32(x) -void scsi_command(msd_cbw_t *msd_cbw_data); +void scsi_command(msd_cbw_t* msd_cbw_data); #endif /* __SCSI_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.c b/firmware/baseband/sd_over_usb/sd_over_usb.c index e2863da5a..4b84ca733 100644 --- a/firmware/baseband/sd_over_usb/sd_over_usb.c +++ b/firmware/baseband/sd_over_usb/sd_over_usb.c @@ -26,107 +26,105 @@ bool scsi_running = false; usb_request_status_t report_max_lun( - usb_endpoint_t* const endpoint, - const usb_transfer_stage_t stage) -{ - if (stage == USB_TRANSFER_STAGE_SETUP) { - endpoint->buffer[0] = 0; - usb_transfer_schedule_block( - endpoint->in, - &endpoint->buffer, - 1, - NULL, - NULL); - - usb_transfer_schedule_ack(endpoint->out); - - scsi_running = true; - } - - return USB_REQUEST_STATUS_OK; + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + endpoint->buffer[0] = 0; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + + usb_transfer_schedule_ack(endpoint->out); + + scsi_running = true; + } + + return USB_REQUEST_STATUS_OK; } usb_request_status_t usb_class_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { - usb_request_status_t status = USB_REQUEST_STATUS_STALL; + usb_request_status_t status = USB_REQUEST_STATUS_STALL; - volatile uint8_t request = endpoint->setup.request; + volatile uint8_t request = endpoint->setup.request; - if (request == 0xFE) - return report_max_lun(endpoint, stage); + if (request == 0xFE) + return report_max_lun(endpoint, stage); - return status; + return status; } const usb_request_handlers_t usb_request_handlers = { - .standard = usb_standard_request, - .class = usb_class_request, - .vendor = 0, - .reserved = 0 -}; + .standard = usb_standard_request, + .class = usb_class_request, + .vendor = 0, + .reserved = 0}; void usb_configuration_changed(usb_device_t* const device) { - (void)device; + (void)device; - usb_endpoint_init(&usb_endpoint_bulk_in); - usb_endpoint_init(&usb_endpoint_bulk_out); + usb_endpoint_init(&usb_endpoint_bulk_in); + usb_endpoint_init(&usb_endpoint_bulk_out); } void start_usb(void) { - detect_hardware_platform(); - pin_setup(); - cpu_clock_init(); + detect_hardware_platform(); + pin_setup(); + cpu_clock_init(); - usb_set_configuration_changed_cb(usb_configuration_changed); - usb_peripheral_reset(); + usb_set_configuration_changed_cb(usb_configuration_changed); + usb_peripheral_reset(); - usb_device_init(0, &usb_device); + usb_device_init(0, &usb_device); - usb_queue_init(&usb_endpoint_control_out_queue); - usb_queue_init(&usb_endpoint_control_in_queue); - usb_queue_init(&usb_endpoint_bulk_out_queue); - usb_queue_init(&usb_endpoint_bulk_in_queue); + usb_queue_init(&usb_endpoint_control_out_queue); + usb_queue_init(&usb_endpoint_control_in_queue); + usb_queue_init(&usb_endpoint_bulk_out_queue); + usb_queue_init(&usb_endpoint_bulk_in_queue); - usb_endpoint_init(&usb_endpoint_control_out); - usb_endpoint_init(&usb_endpoint_control_in); + usb_endpoint_init(&usb_endpoint_control_out); + usb_endpoint_init(&usb_endpoint_control_in); - nvic_set_priority(NVIC_USB0_IRQ, 255); + nvic_set_priority(NVIC_USB0_IRQ, 255); - usb_run(&usb_device); + usb_run(&usb_device); } void stop_usb(void) { - usb_peripheral_reset(); + usb_peripheral_reset(); } void irq_usb(void) { - usb0_isr(); + usb0_isr(); } volatile bool transfer_complete = false; -void scsi_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) -{ - (void)user_data; - (void)bytes_transferred; +void scsi_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) { + (void)user_data; + (void)bytes_transferred; - transfer_complete = true; + transfer_complete = true; } void usb_transfer(void) { - if (scsi_running) { - transfer_complete = false; - usb_transfer_schedule_block( - &usb_endpoint_bulk_out, - &usb_bulk_buffer[0x4000], - USB_TRANSFER_SIZE, - scsi_bulk_transfer_complete, - NULL); - - while (!transfer_complete); - - msd_cbw_t *msd_cbw_data = (msd_cbw_t *) &usb_bulk_buffer[0x4000]; - - if (msd_cbw_data->signature == MSD_CBW_SIGNATURE){ - scsi_command(msd_cbw_data); - } - } + if (scsi_running) { + transfer_complete = false; + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_bulk_buffer[0x4000], + USB_TRANSFER_SIZE, + scsi_bulk_transfer_complete, + NULL); + + while (!transfer_complete) + ; + + msd_cbw_t* msd_cbw_data = (msd_cbw_t*)&usb_bulk_buffer[0x4000]; + + if (msd_cbw_data->signature == MSD_CBW_SIGNATURE) { + scsi_command(msd_cbw_data); + } + } } \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.c b/firmware/baseband/sd_over_usb/usb_descriptor.c index fdba7b1dd..4374502e8 100644 --- a/firmware/baseband/sd_over_usb/usb_descriptor.c +++ b/firmware/baseband/sd_over_usb/usb_descriptor.c @@ -28,135 +28,135 @@ #define USB_VENDOR_ID (0x1D50) #ifdef HACKRF_ONE - #define USB_PRODUCT_ID (0x6089) +#define USB_PRODUCT_ID (0x6089) #elif JAWBREAKER - #define USB_PRODUCT_ID (0x604B) +#define USB_PRODUCT_ID (0x604B) #elif RAD1O - #define USB_PRODUCT_ID (0xCC15) +#define USB_PRODUCT_ID (0xCC15) #else - #define USB_PRODUCT_ID (0xFFFF) +#define USB_PRODUCT_ID (0xFFFF) #endif #define USB_API_VERSION (0x0107) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) -#define USB_MAX_PACKET0 (64) +#define USB_MAX_PACKET0 (64) #define USB_MAX_PACKET_BULK_FS (64) #define USB_MAX_PACKET_BULK_HS (512) -#define USB_BULK_IN_EP_ADDR (0x81) +#define USB_BULK_IN_EP_ADDR (0x81) #define USB_BULK_OUT_EP_ADDR (0x02) #define USB_STRING_LANGID (0x0409) uint8_t usb_descriptor_device[] = { - 18, // bLength - USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType - USB_WORD(0x0200), // bcdUSB - 0x00, // bDeviceClass - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - USB_MAX_PACKET0, // bMaxPacketSize0 - USB_WORD(USB_VENDOR_ID), // idVendor - USB_WORD(USB_PRODUCT_ID), // idProduct - USB_WORD(USB_API_VERSION), // bcdDevice - 0x01, // iManufacturer - 0x02, // iProduct - 0x04, // iSerialNumber - 0x01 // bNumConfigurations + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(USB_PRODUCT_ID), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x04, // iSerialNumber + 0x01 // bNumConfigurations }; uint8_t usb_descriptor_device_qualifier[] = { - 10, // bLength - USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType - USB_WORD(0x0200), // bcdUSB - 0x00, // bDeviceClass - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 64, // bMaxPacketSize0 - 0x01, // bNumOtherSpeedConfigurations - 0x00 // bReserved + 10, // bLength + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 64, // bMaxPacketSize0 + 0x01, // bNumOtherSpeedConfigurations + 0x00 // bReserved }; uint8_t usb_descriptor_configuration_full_speed[] = { - 9, // bLength - USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType - USB_WORD(32), // wTotalLength - 0x01, // bNumInterfaces - 0x01, // bConfigurationValue - 0x00, // iConfiguration - 0x80, // bmAttributes: USB-powered - 250, // bMaxPower: 500mA + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA - 9, // bLength - USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - 0x08, // bInterfaceClass: vendor-specific - 0x06, // bInterfaceSubClass - 0x50, // bInterfaceProtocol: vendor-specific - 0x00, // iInterface + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface - 7, // bLength - USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType - USB_BULK_IN_EP_ADDR, // bEndpointAddress - 0x02, // bmAttributes: BULK - USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize - 0x00, // bInterval: no NAK + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK - 7, // bLength - USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType - USB_BULK_OUT_EP_ADDR, // bEndpointAddress - 0x02, // bmAttributes: BULK - USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize - 0x00, // bInterval: no NAK + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK - 0, // TERMINATOR + 0, // TERMINATOR }; uint8_t usb_descriptor_configuration_high_speed[] = { - 9, // bLength - USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType - USB_WORD(32), // wTotalLength - 0x01, // bNumInterfaces - 0x01, // bConfigurationValue - 0x00, // iConfiguration - 0x80, // bmAttributes: USB-powered - 250, // bMaxPower: 500mA + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA - 9, // bLength - USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - 0x08, // bInterfaceClass: vendor-specific - 0x06, // bInterfaceSubClass - 0x50, // bInterfaceProtocol: vendor-specific - 0x00, // iInterface + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface - 7, // bLength - USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType - USB_BULK_IN_EP_ADDR, // bEndpointAddress - 0x02, // bmAttributes: BULK - USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize - 0x00, // bInterval: no NAK + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no NAK - 7, // bLength - USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType - USB_BULK_OUT_EP_ADDR, // bEndpointAddress - 0x02, // bmAttributes: BULK - USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize - 0x00, // bInterval: no NAK + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no NAK - 0, // TERMINATOR + 0, // TERMINATOR }; uint8_t usb_descriptor_string_languages[] = { - 0x04, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - USB_WORD(USB_STRING_LANGID), // wLANGID + 0x04, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + USB_WORD(USB_STRING_LANGID), // wLANGID }; // clang-format off diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.h b/firmware/baseband/sd_over_usb/usb_descriptor.h index 2c2990e01..a4f19418b 100644 --- a/firmware/baseband/sd_over_usb/usb_descriptor.h +++ b/firmware/baseband/sd_over_usb/usb_descriptor.h @@ -32,7 +32,7 @@ extern uint8_t usb_descriptor_string_product[]; #define USB_DESCRIPTOR_STRING_SERIAL_LEN 32 #define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \ - (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ + (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ extern uint8_t usb_descriptor_string_serial_number[]; extern uint8_t* usb_descriptor_strings[]; diff --git a/firmware/baseband/spectrum_collector.cpp b/firmware/baseband/spectrum_collector.cpp index 165b0077a..e8bb5311a 100644 --- a/firmware/baseband/spectrum_collector.cpp +++ b/firmware/baseband/spectrum_collector.cpp @@ -30,43 +30,42 @@ #include void SpectrumCollector::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - update(); - break; - - case Message::ID::SpectrumStreamingConfig: - set_state(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + update(); + break; + + case Message::ID::SpectrumStreamingConfig: + set_state(*reinterpret_cast(message)); + break; + + default: + break; + } } void SpectrumCollector::set_state(const SpectrumStreamingConfigMessage& message) { - if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) { - start(); - } else { - stop(); - } + if (message.mode == SpectrumStreamingConfigMessage::Mode::Running) { + start(); + } else { + stop(); + } } void SpectrumCollector::start() { - streaming = true; - ChannelSpectrumConfigMessage message { &fifo }; - shared_memory.application_queue.push(message); + streaming = true; + ChannelSpectrumConfigMessage message{&fifo}; + shared_memory.application_queue.push(message); } void SpectrumCollector::stop() { - streaming = false; - fifo.reset_in(); + streaming = false; + fifo.reset_in(); } void SpectrumCollector::set_decimation_factor( - const size_t decimation_factor -) { - channel_spectrum_decimator.set_factor(decimation_factor); + const size_t decimation_factor) { + channel_spectrum_decimator.set_factor(decimation_factor); } /* TODO: Refactor to register task with idle thread? @@ -75,83 +74,81 @@ void SpectrumCollector::set_decimation_factor( */ void SpectrumCollector::feed( - const buffer_c16_t& channel, - const int32_t filter_low_frequency, - const int32_t filter_high_frequency, - const int32_t filter_transition -) { - // Called from baseband processing thread. - channel_filter_low_frequency = filter_low_frequency; - channel_filter_high_frequency = filter_high_frequency; - channel_filter_transition = filter_transition; - - channel_spectrum_decimator.feed( - channel, - [this](const buffer_c16_t& data) { - this->post_message(data); - } - ); + const buffer_c16_t& channel, + const int32_t filter_low_frequency, + const int32_t filter_high_frequency, + const int32_t filter_transition) { + // Called from baseband processing thread. + channel_filter_low_frequency = filter_low_frequency; + channel_filter_high_frequency = filter_high_frequency; + channel_filter_transition = filter_transition; + + channel_spectrum_decimator.feed( + channel, + [this](const buffer_c16_t& data) { + this->post_message(data); + }); } void SpectrumCollector::post_message(const buffer_c16_t& data) { - // Called from baseband processing thread. - if( streaming && !channel_spectrum_request_update ) { - fft_swap(data, channel_spectrum); - channel_spectrum_sampling_rate = data.sampling_rate; - channel_spectrum_request_update = true; - EventDispatcher::events_flag(EVT_MASK_SPECTRUM); - } + // Called from baseband processing thread. + if (streaming && !channel_spectrum_request_update) { + fft_swap(data, channel_spectrum); + channel_spectrum_sampling_rate = data.sampling_rate; + channel_spectrum_request_update = true; + EventDispatcher::events_flag(EVT_MASK_SPECTRUM); + } } -template +template static typename T::value_type spectrum_window_none(const T& s, const size_t i) { - constexpr size_t length = sizeof(s)/sizeof(s[0]); - static_assert(power_of_two(length), "Array length must be power of 2"); - return s[i]; + constexpr size_t length = sizeof(s) / sizeof(s[0]); + static_assert(power_of_two(length), "Array length must be power of 2"); + return s[i]; }; -template +template static typename T::value_type spectrum_window_hamming_3(const T& s, const size_t i) { - constexpr size_t length = sizeof(s)/sizeof(s[0]); - static_assert((length), "Array length must be power of 2"); - constexpr size_t mask = length - 1; - // Three point Hamming window. - return s[i] * 0.54f + (s[(i-1) & mask] + s[(i+1) & mask]) * -0.23f; + constexpr size_t length = sizeof(s) / sizeof(s[0]); + static_assert((length), "Array length must be power of 2"); + constexpr size_t mask = length - 1; + // Three point Hamming window. + return s[i] * 0.54f + (s[(i - 1) & mask] + s[(i + 1) & mask]) * -0.23f; }; -template +template static typename T::value_type spectrum_window_blackman_3(const T& s, const size_t i) { - constexpr size_t length = sizeof(s)/sizeof(s[0]); - static_assert(power_of_two(length), "Array length must be power of 2"); - constexpr size_t mask = length - 1; - // Three term Blackman window. - constexpr float alpha = 0.42f; - constexpr float beta = 0.5f * 0.5f; - constexpr float gamma = 0.08f * 0.05f; - return s[i] * alpha - (s[(i-1) & mask] + s[(i+1) & mask]) * beta + (s[(i-2) & mask] + s[(i+2) & mask]) * gamma; + constexpr size_t length = sizeof(s) / sizeof(s[0]); + static_assert(power_of_two(length), "Array length must be power of 2"); + constexpr size_t mask = length - 1; + // Three term Blackman window. + constexpr float alpha = 0.42f; + constexpr float beta = 0.5f * 0.5f; + constexpr float gamma = 0.08f * 0.05f; + return s[i] * alpha - (s[(i - 1) & mask] + s[(i + 1) & mask]) * beta + (s[(i - 2) & mask] + s[(i + 2) & mask]) * gamma; }; void SpectrumCollector::update() { - // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) - if( streaming && channel_spectrum_request_update ) { - /* Decimated buffer is full. Compute spectrum. */ - fft_c_preswapped(channel_spectrum, 0, 8); - - ChannelSpectrum spectrum; - spectrum.sampling_rate = channel_spectrum_sampling_rate; - spectrum.channel_filter_low_frequency = channel_filter_low_frequency; - spectrum.channel_filter_high_frequency = channel_filter_high_frequency; - spectrum.channel_filter_transition = channel_filter_transition; - for(size_t i=0; i channel_spectrum_decimator { 1 }; - ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k] { }; - ChannelSpectrumFIFO fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k }; + private: + BlockDecimator channel_spectrum_decimator{1}; + ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k]{}; + ChannelSpectrumFIFO fifo{fifo_data, ChannelSpectrumConfigMessage::fifo_k}; - volatile bool channel_spectrum_request_update { false }; - bool streaming { false }; - std::array, 256> channel_spectrum { }; - uint32_t channel_spectrum_sampling_rate { 0 }; - int32_t channel_filter_low_frequency { 0 }; - int32_t channel_filter_high_frequency { 0 }; - int32_t channel_filter_transition { 0 }; + volatile bool channel_spectrum_request_update{false}; + bool streaming{false}; + std::array, 256> channel_spectrum{}; + uint32_t channel_spectrum_sampling_rate{0}; + int32_t channel_filter_low_frequency{0}; + int32_t channel_filter_high_frequency{0}; + int32_t channel_filter_transition{0}; - void post_message(const buffer_c16_t& data); + void post_message(const buffer_c16_t& data); - void set_state(const SpectrumStreamingConfigMessage& message); - void start(); - void stop(); + void set_state(const SpectrumStreamingConfigMessage& message); + void start(); + void stop(); - void update(); + void update(); }; -#endif/*__SPECTRUM_COLLECTOR_H__*/ +#endif /*__SPECTRUM_COLLECTOR_H__*/ diff --git a/firmware/baseband/stream_input.cpp b/firmware/baseband/stream_input.cpp index 2e7bf65de..e101c6cbd 100644 --- a/firmware/baseband/stream_input.cpp +++ b/firmware/baseband/stream_input.cpp @@ -24,53 +24,52 @@ #include "lpc43xx_cpp.hpp" using namespace lpc43xx; -StreamInput::StreamInput(CaptureConfig* const config) : - fifo_buffers_empty { buffers_empty.data(), buffer_count_max_log2 }, - fifo_buffers_full { buffers_full.data(), buffer_count_max_log2 }, - config { config }, - data { std::make_unique(config->write_size * config->buffer_count) } -{ - config->fifo_buffers_empty = &fifo_buffers_empty; - config->fifo_buffers_full = &fifo_buffers_full; +StreamInput::StreamInput(CaptureConfig* const config) + : fifo_buffers_empty{buffers_empty.data(), buffer_count_max_log2}, + fifo_buffers_full{buffers_full.data(), buffer_count_max_log2}, + config{config}, + data{std::make_unique(config->write_size * config->buffer_count)} { + config->fifo_buffers_empty = &fifo_buffers_empty; + config->fifo_buffers_full = &fifo_buffers_full; - for(size_t i=0; ibuffer_count; i++) { - buffers[i] = { &(data.get()[i * config->write_size]), config->write_size }; - fifo_buffers_empty.in(&buffers[i]); - } + for (size_t i = 0; i < config->buffer_count; i++) { + buffers[i] = {&(data.get()[i * config->write_size]), config->write_size}; + fifo_buffers_empty.in(&buffers[i]); + } } size_t StreamInput::write(const void* const data, const size_t length) { - const uint8_t* p = static_cast(data); - size_t written = 0; + const uint8_t* p = static_cast(data); + size_t written = 0; - while( written < length ) { - if( !active_buffer ) { - // We need an empty buffer... - if( !fifo_buffers_empty.out(active_buffer) ) { - // ...but none are available. Samples were dropped. - break; - } - } - - const auto remaining = length - written; - written += active_buffer->write(&p[written], remaining); + while (written < length) { + if (!active_buffer) { + // We need an empty buffer... + if (!fifo_buffers_empty.out(active_buffer)) { + // ...but none are available. Samples were dropped. + break; + } + } - if( active_buffer->is_full() ) { - if( !fifo_buffers_full.in(active_buffer) ) { - // FIFO is full of buffers, there's no place for this one. - // Bail out of the loop, and try submitting the buffer in the - // next pass. - // This should never happen if the number of buffers is less - // than the capacity of the FIFO. - break; - } - active_buffer = nullptr; - creg::m4txevent::assert_event(); - } - } + const auto remaining = length - written; + written += active_buffer->write(&p[written], remaining); - config->baseband_bytes_received += length; - config->baseband_bytes_dropped += (length - written); + if (active_buffer->is_full()) { + if (!fifo_buffers_full.in(active_buffer)) { + // FIFO is full of buffers, there's no place for this one. + // Bail out of the loop, and try submitting the buffer in the + // next pass. + // This should never happen if the number of buffers is less + // than the capacity of the FIFO. + break; + } + active_buffer = nullptr; + creg::m4txevent::assert_event(); + } + } - return written; + config->baseband_bytes_received += length; + config->baseband_bytes_dropped += (length - written); + + return written; } diff --git a/firmware/baseband/stream_input.hpp b/firmware/baseband/stream_input.hpp index 1500d2e53..2abeacd51 100644 --- a/firmware/baseband/stream_input.hpp +++ b/firmware/baseband/stream_input.hpp @@ -31,28 +31,28 @@ #include class StreamInput { -public: - StreamInput(CaptureConfig* const config); - - StreamInput(const StreamInput&) = delete; - StreamInput(StreamInput&&) = delete; - StreamInput& operator=(const StreamInput&) = delete; - StreamInput& operator=(StreamInput&&) = delete; - - size_t write(const void* const data, const size_t length); - -private: - static constexpr size_t buffer_count_max_log2 = 3; - static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2; - - FIFO fifo_buffers_empty; - FIFO fifo_buffers_full; - std::array buffers { }; - std::array buffers_empty { }; - std::array buffers_full { }; - StreamBuffer* active_buffer { nullptr }; - CaptureConfig* const config { nullptr }; - std::unique_ptr data { }; + public: + StreamInput(CaptureConfig* const config); + + StreamInput(const StreamInput&) = delete; + StreamInput(StreamInput&&) = delete; + StreamInput& operator=(const StreamInput&) = delete; + StreamInput& operator=(StreamInput&&) = delete; + + size_t write(const void* const data, const size_t length); + + private: + static constexpr size_t buffer_count_max_log2 = 3; + static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2; + + FIFO fifo_buffers_empty; + FIFO fifo_buffers_full; + std::array buffers{}; + std::array buffers_empty{}; + std::array buffers_full{}; + StreamBuffer* active_buffer{nullptr}; + CaptureConfig* const config{nullptr}; + std::unique_ptr data{}; }; -#endif/*__STREAM_INPUT_H__*/ +#endif /*__STREAM_INPUT_H__*/ diff --git a/firmware/baseband/stream_output.cpp b/firmware/baseband/stream_output.cpp index c28d8b2fe..ee9407e35 100644 --- a/firmware/baseband/stream_output.cpp +++ b/firmware/baseband/stream_output.cpp @@ -25,53 +25,52 @@ #include "lpc43xx_cpp.hpp" using namespace lpc43xx; -StreamOutput::StreamOutput(ReplayConfig* const config) : - fifo_buffers_empty { buffers_empty.data(), buffer_count_max_log2 }, - fifo_buffers_full { buffers_full.data(), buffer_count_max_log2 }, - config { config }, - data { std::make_unique(config->read_size * config->buffer_count) } -{ - config->fifo_buffers_empty = &fifo_buffers_empty; - config->fifo_buffers_full = &fifo_buffers_full; +StreamOutput::StreamOutput(ReplayConfig* const config) + : fifo_buffers_empty{buffers_empty.data(), buffer_count_max_log2}, + fifo_buffers_full{buffers_full.data(), buffer_count_max_log2}, + config{config}, + data{std::make_unique(config->read_size * config->buffer_count)} { + config->fifo_buffers_empty = &fifo_buffers_empty; + config->fifo_buffers_full = &fifo_buffers_full; - for(size_t i=0; ibuffer_count; i++) { - // Set buffers to point consecutively in previously allocated unique_ptr "data" - buffers[i] = { &(data.get()[i * config->read_size]), config->read_size }; - // Put all buffer pointers in the "empty buffer" FIFO - fifo_buffers_empty.in(&buffers[i]); - } + for (size_t i = 0; i < config->buffer_count; i++) { + // Set buffers to point consecutively in previously allocated unique_ptr "data" + buffers[i] = {&(data.get()[i * config->read_size]), config->read_size}; + // Put all buffer pointers in the "empty buffer" FIFO + fifo_buffers_empty.in(&buffers[i]); + } } size_t StreamOutput::read(void* const data, const size_t length) { - uint8_t* p = static_cast(data); - size_t read = 0; + uint8_t* p = static_cast(data); + size_t read = 0; - while( read < length ) { - if( !active_buffer ) { - // We need a full buffer... - if( !fifo_buffers_full.out(active_buffer) ) { - // ...but none are available. Hole in transmission (inform app and stop ?) - break; - } - } - - const auto remaining = length - read; - read += active_buffer->read(&p[read], remaining); + while (read < length) { + if (!active_buffer) { + // We need a full buffer... + if (!fifo_buffers_full.out(active_buffer)) { + // ...but none are available. Hole in transmission (inform app and stop ?) + break; + } + } - if( active_buffer->is_empty() ) { - if( !fifo_buffers_empty.in(active_buffer) ) { - // Empty buffers FIFO is already full. - // This should never happen if the number of buffers is less - // than the capacity of the FIFO. - break; - } - // Tell M0 (IRQ) that a buffer has been consumed. - active_buffer = nullptr; - creg::m4txevent::assert_event(); - } - } + const auto remaining = length - read; + read += active_buffer->read(&p[read], remaining); - config->baseband_bytes_received += length; + if (active_buffer->is_empty()) { + if (!fifo_buffers_empty.in(active_buffer)) { + // Empty buffers FIFO is already full. + // This should never happen if the number of buffers is less + // than the capacity of the FIFO. + break; + } + // Tell M0 (IRQ) that a buffer has been consumed. + active_buffer = nullptr; + creg::m4txevent::assert_event(); + } + } - return read; + config->baseband_bytes_received += length; + + return read; } diff --git a/firmware/baseband/stream_output.hpp b/firmware/baseband/stream_output.hpp index c4bea9720..efa3b326c 100644 --- a/firmware/baseband/stream_output.hpp +++ b/firmware/baseband/stream_output.hpp @@ -32,28 +32,28 @@ #include class StreamOutput { -public: - StreamOutput(ReplayConfig* const config); - - StreamOutput(const StreamOutput&) = delete; - StreamOutput(StreamOutput&&) = delete; - StreamOutput& operator=(const StreamOutput&) = delete; - StreamOutput& operator=(StreamOutput&&) = delete; - - size_t read(void* const data, const size_t length); - -private: - static constexpr size_t buffer_count_max_log2 = 3; - static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2; - - FIFO fifo_buffers_empty; - FIFO fifo_buffers_full; - std::array buffers { }; - std::array buffers_empty { }; - std::array buffers_full { }; - StreamBuffer* active_buffer { nullptr }; - ReplayConfig* const config { nullptr }; - std::unique_ptr data { }; + public: + StreamOutput(ReplayConfig* const config); + + StreamOutput(const StreamOutput&) = delete; + StreamOutput(StreamOutput&&) = delete; + StreamOutput& operator=(const StreamOutput&) = delete; + StreamOutput& operator=(StreamOutput&&) = delete; + + size_t read(void* const data, const size_t length); + + private: + static constexpr size_t buffer_count_max_log2 = 3; + static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2; + + FIFO fifo_buffers_empty; + FIFO fifo_buffers_full; + std::array buffers{}; + std::array buffers_empty{}; + std::array buffers_full{}; + StreamBuffer* active_buffer{nullptr}; + ReplayConfig* const config{nullptr}; + std::unique_ptr data{}; }; -#endif/*__STREAM_OUTPUT_H__*/ +#endif /*__STREAM_OUTPUT_H__*/ diff --git a/firmware/baseband/symbol_coding.hpp b/firmware/baseband/symbol_coding.hpp index 8842abdf6..f28907ee5 100644 --- a/firmware/baseband/symbol_coding.hpp +++ b/firmware/baseband/symbol_coding.hpp @@ -28,28 +28,28 @@ namespace symbol_coding { class NRZIDecoder { -public: - uint_fast8_t operator()(const uint_fast8_t symbol) { - const auto out = (~(symbol ^ last)) & 1; - last = symbol; - return out; - } - -private: - uint_fast8_t last { 0 }; + public: + uint_fast8_t operator()(const uint_fast8_t symbol) { + const auto out = (~(symbol ^ last)) & 1; + last = symbol; + return out; + } + + private: + uint_fast8_t last{0}; }; class ACARSDecoder { -public: - uint_fast8_t operator()(const uint_fast8_t symbol) { - last ^= (~symbol & 1); - return last; - } - -private: - uint_fast8_t last { 0 }; + public: + uint_fast8_t operator()(const uint_fast8_t symbol) { + last ^= (~symbol & 1); + return last; + } + + private: + uint_fast8_t last{0}; }; } /* namespace symbol_coding */ -#endif/*__SYMBOL_CODING_H__*/ +#endif /*__SYMBOL_CODING_H__*/ diff --git a/firmware/baseband/tone_gen.cpp b/firmware/baseband/tone_gen.cpp index d015f94fa..447376637 100644 --- a/firmware/baseband/tone_gen.cpp +++ b/firmware/baseband/tone_gen.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -23,95 +23,84 @@ #include "tone_gen.hpp" #include "sine_table_int8.hpp" - /* int32_t ToneGen::tone_sine() { - // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde - // Hoepfully we can manage without it , same as previous fw 1.3.1 - int32_t tone_sample = sine_table_i8[tone_phase_] * 0x1000000; - tone_phase_ += delta_; + // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde + // Hoepfully we can manage without it , same as previous fw 1.3.1 + int32_t tone_sample = sine_table_i8[tone_phase_] * 0x1000000; + tone_phase_ += delta_; - return tone_sample; + return tone_sample; } -*/ - +*/ int32_t ToneGen::tone_square() { - // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde - int32_t tone_sample = 0; + // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde + int32_t tone_sample = 0; - if(tone_phase_ < (UINT32_MAX / 2)) { - tone_sample = INT32_MAX; - } - else { - tone_sample = INT32_MIN; - } + if (tone_phase_ < (UINT32_MAX / 2)) { + tone_sample = INT32_MAX; + } else { + tone_sample = INT32_MIN; + } - tone_phase_ += delta_; + tone_phase_ += delta_; - return tone_sample; + return tone_sample; } -/* +/* void ToneGen::configure(const uint32_t delta, const float tone_mix_weight) { - // Confirmed ! It is not working well in the fw 1.4.4 Mic App , CTCSS generation, (but added for Sonde App) - // I Think it should be deleted or modified but not use it as it is in Mic App . - - delta_ = (uint8_t) ((delta & 0xFF000000U) >> 24); - delta_ = delta; - tone_mix_weight_ = tone_mix_weight; - input_mix_weight_ = 1.0 - tone_mix_weight; - current_tone_type_ = sine; + // Confirmed ! It is not working well in the fw 1.4.4 Mic App , CTCSS generation, (but added for Sonde App) + // I Think it should be deleted or modified but not use it as it is in Mic App . + + delta_ = (uint8_t) ((delta & 0xFF000000U) >> 24); + delta_ = delta; + tone_mix_weight_ = tone_mix_weight; + input_mix_weight_ = 1.0 - tone_mix_weight; + current_tone_type_ = sine; } -*/ - - +*/ void ToneGen::configure(const uint32_t freq, const float tone_mix_weight, const tone_type tone_type, const uint32_t sample_rate) { - // TODO : Added for Sonde App. We keep it by now to avoid compile errors, but it needs to be reviewed in Sonde - delta_ = (uint8_t) ((freq * sizeof(sine_table_i8)) / sample_rate); - tone_mix_weight_ = tone_mix_weight; - input_mix_weight_ = 1.0 - tone_mix_weight; - current_tone_type_ = tone_type; -} - - - + // TODO : Added for Sonde App. We keep it by now to avoid compile errors, but it needs to be reviewed in Sonde + delta_ = (uint8_t)((freq * sizeof(sine_table_i8)) / sample_rate); + tone_mix_weight_ = tone_mix_weight; + input_mix_weight_ = 1.0 - tone_mix_weight; + current_tone_type_ = tone_type; +} -// ----Original available core SW code from fw 1.3.1 , Working also well in Mic App CTCSS Gen from fw 1.4.0 onwards +// ----Original available core SW code from fw 1.3.1 , Working also well in Mic App CTCSS Gen from fw 1.4.0 onwards // Original direct-look-up synthesis algorithm with Fractional delta phase. It is OK // Delta and Accumulator fase are stored in 32 bits (4 bytes), 1st top byte used as Modulo-256 Sine look-up table [index] // the lower 3 bytes (24 bits) are used as a Fractional Detla and Accumulator phase, to have very finer Fstep control. void ToneGen::configure(const uint32_t delta, const float tone_mix_weight) { - delta_ = delta; - tone_mix_weight_ = tone_mix_weight; - input_mix_weight_ = 1.0 - tone_mix_weight; + delta_ = delta; + tone_mix_weight_ = tone_mix_weight; + input_mix_weight_ = 1.0 - tone_mix_weight; } int32_t ToneGen::process(const int32_t sample_in) { - if (!delta_) - return sample_in; - - int32_t tone_sample = sine_table_i8[(tone_phase_ & 0xFF000000U) >> 24]; - tone_phase_ += delta_; - - return (sample_in * input_mix_weight_) + (tone_sample * tone_mix_weight_); + if (!delta_) + return sample_in; + + int32_t tone_sample = sine_table_i8[(tone_phase_ & 0xFF000000U) >> 24]; + tone_phase_ += delta_; + + return (sample_in * input_mix_weight_) + (tone_sample * tone_mix_weight_); } // ------------------------------------------------------------- +int32_t ToneGen::process_square(const int32_t sample_in) { + // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde + if (!delta_) + return sample_in; + int32_t tone_sample = 0; -int32_t ToneGen::process_square(const int32_t sample_in) { - // TODO : Added for Sonde App. We keep it by now , but it needs to be reviewed in Sonde - if (!delta_) - return sample_in; - - int32_t tone_sample = 0; - - tone_sample = tone_square(); - - - return (sample_in * input_mix_weight_) + (tone_sample * tone_mix_weight_); + tone_sample = tone_square(); + + return (sample_in * input_mix_weight_) + (tone_sample * tone_mix_weight_); } diff --git a/firmware/baseband/tone_gen.hpp b/firmware/baseband/tone_gen.hpp index eca69e2f7..2bbb7a5ae 100644 --- a/firmware/baseband/tone_gen.hpp +++ b/firmware/baseband/tone_gen.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2017 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -27,42 +27,41 @@ #include class ToneGen { -public: - enum tone_type { sine, square }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. - - /*ToneGen(const size_t sample_rate - ) : sample_rate_ { sample_rate } - {};*/ - - void configure(const uint32_t delta, const float tone_mix_weight); - void configure(const uint32_t freq, const float tone_mix_weight, const tone_type tone_type, const uint32_t sample_rate); + public: + enum tone_type { sine, + square }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. - int32_t process(const int32_t sample_in); - int32_t process_square(const int32_t sample_in); + /*ToneGen(const size_t sample_rate + ) : sample_rate_ { sample_rate } + {};*/ -private: - tone_type current_tone_type_ { sine }; + void configure(const uint32_t delta, const float tone_mix_weight); + void configure(const uint32_t freq, const float tone_mix_weight, const tone_type tone_type, const uint32_t sample_rate); - float input_mix_weight_ { 1 }; - float tone_mix_weight_ { 0 }; + int32_t process(const int32_t sample_in); + int32_t process_square(const int32_t sample_in); - uint32_t delta_ { 0 }; - uint32_t tone_phase_ { 0 }; + private: + tone_type current_tone_type_{sine}; -// uint8_t delta_ { 0 }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. -// uint8_t tone_phase_ { 0 }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. + float input_mix_weight_{1}; + float tone_mix_weight_{0}; - /** - * Generator function which selects every other sample from the reference sine waveform to the output sample: - */ - int32_t tone_sine();// TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. + uint32_t delta_{0}; + uint32_t tone_phase_{0}; + // uint8_t delta_ { 0 }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. + // uint8_t tone_phase_ { 0 }; // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. - /** - * Generator function for square waves: - */ - int32_t tone_square(); // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. + /** + * Generator function which selects every other sample from the reference sine waveform to the output sample: + */ + int32_t tone_sine(); // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. + /** + * Generator function for square waves: + */ + int32_t tone_square(); // TODO: Added for Radio Sonde.cpp PR 376, 381 , we need to check if keep or not. }; #endif /* __TONE_GEN_H__ */ diff --git a/firmware/baseband/touch_dma.cpp b/firmware/baseband/touch_dma.cpp index ce66617a1..23de9c791 100644 --- a/firmware/baseband/touch_dma.cpp +++ b/firmware/baseband/touch_dma.cpp @@ -36,7 +36,6 @@ using namespace lpc43xx; namespace touch { namespace dma { - /* TODO: SO MUCH REPEATED CODE FROM rssi_dma.cpp!!! */ static constexpr auto& gpdma_channel = gpdma::channels[portapack::adc0_gpdma_channel_number]; @@ -49,80 +48,80 @@ constexpr uint32_t gpdma_src_peripheral = 0xd; constexpr uint32_t gpdma_dest_peripheral = 0xd; constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) { - return { - .lm = gpdma_ahb_master_lli_fetch, - .r = 0, - .lli = reinterpret_cast(lli), - }; + return { + .lm = gpdma_ahb_master_lli_fetch, + .r = 0, + .lli = reinterpret_cast(lli), + }; } constexpr gpdma::channel::Control control(const size_t number_of_transfers) { - return { - .transfersize = number_of_transfers, - .sbsize = 2, /* Burst size: 8 transfers */ - .dbsize = 2, /* Burst size: 8 transfers */ - .swidth = 2, /* Source transfer width: word (32 bits) */ - .dwidth = 2, /* Destination transfer width: word (32 bits) */ - .s = gpdma_ahb_master_peripheral, - .d = gpdma_ahb_master_memory, - .si = 1, - .di = 1, - .prot1 = 0, - .prot2 = 0, - .prot3 = 0, - .i = 0, - }; + return { + .transfersize = number_of_transfers, + .sbsize = 2, /* Burst size: 8 transfers */ + .dbsize = 2, /* Burst size: 8 transfers */ + .swidth = 2, /* Source transfer width: word (32 bits) */ + .dwidth = 2, /* Destination transfer width: word (32 bits) */ + .s = gpdma_ahb_master_peripheral, + .d = gpdma_ahb_master_memory, + .si = 1, + .di = 1, + .prot1 = 0, + .prot2 = 0, + .prot3 = 0, + .i = 0, + }; } constexpr gpdma::channel::Config config() { - return { - .e = 0, - .srcperipheral = gpdma_src_peripheral, - .destperipheral = gpdma_dest_peripheral, - .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, - .ie = 0, - .itc = 0, - .l = 0, - .a = 0, - .h = 0, - }; + return { + .e = 0, + .srcperipheral = gpdma_src_peripheral, + .destperipheral = gpdma_dest_peripheral, + .flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl, + .ie = 0, + .itc = 0, + .l = 0, + .a = 0, + .h = 0, + }; } -static gpdma::channel::LLI lli; +static gpdma::channel::LLI lli; constexpr size_t channels_per_sample = 8; -//constexpr size_t samples_per_frame = 40; -//constexpr size_t channel_samples_per_frame = channels_per_sample * samples_per_frame; +// constexpr size_t samples_per_frame = 40; +// constexpr size_t channel_samples_per_frame = channels_per_sample * samples_per_frame; void init() { } void allocate() { - //samples = new sample_t[channel_samples_per_frame]; - //lli = new gpdma::channel::LLI; - lli.srcaddr = reinterpret_cast(&LPC_ADC0->DR[0]); - lli.destaddr = reinterpret_cast(&shared_memory.touch_adc_frame.dr[0]); - lli.lli = lli_pointer(&lli); - lli.control = control(channels_per_sample); + // samples = new sample_t[channel_samples_per_frame]; + // lli = new gpdma::channel::LLI; + lli.srcaddr = reinterpret_cast(&LPC_ADC0->DR[0]); + lli.destaddr = reinterpret_cast(&shared_memory.touch_adc_frame.dr[0]); + lli.lli = lli_pointer(&lli); + lli.control = control(channels_per_sample); } void free() { - //delete samples; - //delete lli; + // delete samples; + // delete lli; } void enable() { - const auto gpdma_config = config(); - gpdma_channel.configure(lli, gpdma_config); - gpdma_channel.enable(); + const auto gpdma_config = config(); + gpdma_channel.configure(lli, gpdma_config); + gpdma_channel.enable(); } bool is_enabled() { - return gpdma_channel.is_enabled(); + return gpdma_channel.is_enabled(); } void disable() { - gpdma_channel.disable(); + gpdma_channel.disable(); } } /* namespace dma */ diff --git a/firmware/baseband/touch_dma.hpp b/firmware/baseband/touch_dma.hpp index 13c92b216..6ae75118f 100644 --- a/firmware/baseband/touch_dma.hpp +++ b/firmware/baseband/touch_dma.hpp @@ -47,4 +47,4 @@ buffer_t wait_for_buffer(); } /* namespace dma */ } /* namespace touch */ -#endif/*__TOUCH_DMA_H__*/ +#endif /*__TOUCH_DMA_H__*/ diff --git a/firmware/baseband/tv_collector.cpp b/firmware/baseband/tv_collector.cpp index d323d36d9..f1d2c9587 100644 --- a/firmware/baseband/tv_collector.cpp +++ b/firmware/baseband/tv_collector.cpp @@ -33,43 +33,42 @@ #include void TvCollector::on_message(const Message* const message) { - switch(message->id) { - case Message::ID::UpdateSpectrum: - update(); - break; - - case Message::ID::SpectrumStreamingConfig: - set_state(*reinterpret_cast(message)); - break; - - default: - break; - } + switch (message->id) { + case Message::ID::UpdateSpectrum: + update(); + break; + + case Message::ID::SpectrumStreamingConfig: + set_state(*reinterpret_cast(message)); + break; + + default: + break; + } } void TvCollector::set_state(const SpectrumStreamingConfigMessage& message) { - if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) { - start(); - } else { - stop(); - } + if (message.mode == SpectrumStreamingConfigMessage::Mode::Running) { + start(); + } else { + stop(); + } } void TvCollector::start() { - streaming = true; - ChannelSpectrumConfigMessage message { &fifo }; - shared_memory.application_queue.push(message); + streaming = true; + ChannelSpectrumConfigMessage message{&fifo}; + shared_memory.application_queue.push(message); } void TvCollector::stop() { - streaming = false; - fifo.reset_in(); + streaming = false; + fifo.reset_in(); } void TvCollector::set_decimation_factor( - const size_t decimation_factor -) { - channel_spectrum_decimator.set_factor(decimation_factor); + const size_t decimation_factor) { + channel_spectrum_decimator.set_factor(decimation_factor); } /* TODO: Refactor to register task with idle thread? @@ -78,43 +77,40 @@ void TvCollector::set_decimation_factor( */ void TvCollector::feed( - const buffer_c16_t& channel -) { - // Called from baseband processing thread. - channel_spectrum_decimator.feed(channel,[this](const buffer_c16_t& data) {this->post_message(data);}); + const buffer_c16_t& channel) { + // Called from baseband processing thread. + channel_spectrum_decimator.feed(channel, [this](const buffer_c16_t& data) { this->post_message(data); }); } void TvCollector::post_message(const buffer_c16_t& data) { - // Called from baseband processing thread. - float re, im; - float mag; - if( streaming && !channel_spectrum_request_update ) { - for(size_t i=0; i<256; i++) - { - re = (float)(data.p[i].real()); - im = (float)(data.p[i].imag()); - mag = __builtin_sqrtf((re * re) + (im * im)) ; - channel_spectrum[i] = {mag, mag}; - } - channel_spectrum_sampling_rate = data.sampling_rate; - channel_spectrum_request_update = true; - EventDispatcher::events_flag(EVT_MASK_SPECTRUM); - } + // Called from baseband processing thread. + float re, im; + float mag; + if (streaming && !channel_spectrum_request_update) { + for (size_t i = 0; i < 256; i++) { + re = (float)(data.p[i].real()); + im = (float)(data.p[i].imag()); + mag = __builtin_sqrtf((re * re) + (im * im)); + channel_spectrum[i] = {mag, mag}; + } + channel_spectrum_sampling_rate = data.sampling_rate; + channel_spectrum_request_update = true; + EventDispatcher::events_flag(EVT_MASK_SPECTRUM); + } } - void TvCollector::update() { - // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) - if( streaming && channel_spectrum_request_update ) { - ChannelSpectrum spectrum; - spectrum.sampling_rate = channel_spectrum_sampling_rate; - for(size_t i=0; i channel_spectrum_decimator { 1 }; - ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k] { }; - ChannelSpectrumFIFO fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k }; + private: + BlockDecimator channel_spectrum_decimator{1}; + ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k]{}; + ChannelSpectrumFIFO fifo{fifo_data, ChannelSpectrumConfigMessage::fifo_k}; - volatile bool channel_spectrum_request_update { false }; - bool streaming { false }; - std::array, 256> channel_spectrum { }; - uint32_t channel_spectrum_sampling_rate { 0 }; + volatile bool channel_spectrum_request_update{false}; + bool streaming{false}; + std::array, 256> channel_spectrum{}; + uint32_t channel_spectrum_sampling_rate{0}; - void post_message(const buffer_c16_t& data); + void post_message(const buffer_c16_t& data); - void set_state(const SpectrumStreamingConfigMessage& message); - void start(); - void stop(); - - void update(); + void set_state(const SpectrumStreamingConfigMessage& message); + void start(); + void stop(); + void update(); }; -#endif/*__TV_COLLECTOR_H__*/ +#endif /*__TV_COLLECTOR_H__*/ diff --git a/firmware/baseband/w25q80bv.cpp b/firmware/baseband/w25q80bv.cpp index d7af6b30b..f83bfff1e 100644 --- a/firmware/baseband/w25q80bv.cpp +++ b/firmware/baseband/w25q80bv.cpp @@ -25,141 +25,150 @@ namespace w25q80bv { -void disable_spifi(){ - RESET_CTRL1 = RESET_CTRL1_SPIFI_RST; +void disable_spifi() { + RESET_CTRL1 = RESET_CTRL1_SPIFI_RST; } -void initialite_spi(){ - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - palOutputPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); +void initialite_spi() { + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + palOutputPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - const uint32_t clock_prescale_rate = 2; - const uint32_t serial_clock_rate = 2; + const uint32_t clock_prescale_rate = 2; + const uint32_t serial_clock_rate = 2; - SSP_CR1(SSP0_BASE) = 0; - SSP_CPSR(SSP0_BASE) = clock_prescale_rate; - SSP_CR0(SSP0_BASE) = (serial_clock_rate << 8) | SSP_DATA_8BITS; - SSP_CR1(SSP0_BASE) = SSP_ENABLE; + SSP_CR1(SSP0_BASE) = 0; + SSP_CPSR(SSP0_BASE) = clock_prescale_rate; + SSP_CR0(SSP0_BASE) = (serial_clock_rate << 8) | SSP_DATA_8BITS; + SSP_CR1(SSP0_BASE) = SSP_ENABLE; } void setup() { - /* Init SPIFI GPIO to Normal GPIO */ - - MMIO32(PIN_GROUP3 + PIN3) = (SCU_SSP_IO | SCU_CONF_FUNCTION2); - MMIO32(PIN_GROUP3 + PIN4) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - MMIO32(PIN_GROUP3 + PIN5) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - MMIO32(PIN_GROUP3 + PIN6) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - MMIO32(PIN_GROUP3 + PIN7) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - MMIO32(PIN_GROUP3 + PIN8) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - - /* configure SSP pins */ - MMIO32(SCU_SSP0_CIPO) = (SCU_SSP_IO | SCU_CONF_FUNCTION5); - MMIO32(SCU_SSP0_COPI) = (SCU_SSP_IO | SCU_CONF_FUNCTION5); - MMIO32(SCU_SSP0_SCK) = (SCU_SSP_IO | SCU_CONF_FUNCTION2); - - /* configure GPIO pins */ - MMIO32(SCU_FLASH_HOLD) = SCU_GPIO_FAST; - MMIO32(SCU_FLASH_WP) = SCU_GPIO_FAST; - MMIO32(SCU_SSP0_CS) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); - - //remove hardware write protection - /* drive CS, HOLD, and WP pins high */ - palSetPad(W25Q80BV_HOLD_PORT, W25Q80BV_HOLD_PAD); - palSetPad(W25Q80BV_WP_PORT, W25Q80BV_WP_PAD); - - /* Set GPIO pins as outputs. */ - palOutputPad(W25Q80BV_HOLD_PORT, W25Q80BV_HOLD_PAD); - palOutputPad(W25Q80BV_WP_PORT, W25Q80BV_WP_PAD); + /* Init SPIFI GPIO to Normal GPIO */ + + MMIO32(PIN_GROUP3 + PIN3) = (SCU_SSP_IO | SCU_CONF_FUNCTION2); + MMIO32(PIN_GROUP3 + PIN4) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + MMIO32(PIN_GROUP3 + PIN5) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + MMIO32(PIN_GROUP3 + PIN6) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + MMIO32(PIN_GROUP3 + PIN7) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + MMIO32(PIN_GROUP3 + PIN8) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + + /* configure SSP pins */ + MMIO32(SCU_SSP0_CIPO) = (SCU_SSP_IO | SCU_CONF_FUNCTION5); + MMIO32(SCU_SSP0_COPI) = (SCU_SSP_IO | SCU_CONF_FUNCTION5); + MMIO32(SCU_SSP0_SCK) = (SCU_SSP_IO | SCU_CONF_FUNCTION2); + + /* configure GPIO pins */ + MMIO32(SCU_FLASH_HOLD) = SCU_GPIO_FAST; + MMIO32(SCU_FLASH_WP) = SCU_GPIO_FAST; + MMIO32(SCU_SSP0_CS) = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + + // remove hardware write protection + /* drive CS, HOLD, and WP pins high */ + palSetPad(W25Q80BV_HOLD_PORT, W25Q80BV_HOLD_PAD); + palSetPad(W25Q80BV_WP_PORT, W25Q80BV_WP_PAD); + + /* Set GPIO pins as outputs. */ + palOutputPad(W25Q80BV_HOLD_PORT, W25Q80BV_HOLD_PAD); + palOutputPad(W25Q80BV_WP_PORT, W25Q80BV_WP_PAD); } void wait_for_device() { - uint8_t device_id; - do { - device_id = get_device_id(); - } while (device_id != W25Q80BV_DEVICE_ID_RES && - device_id != W25Q16DV_DEVICE_ID_RES); + uint8_t device_id; + do { + device_id = get_device_id(); + } while (device_id != W25Q80BV_DEVICE_ID_RES && + device_id != W25Q16DV_DEVICE_ID_RES); } void wait_not_busy() { - while (get_status() & W25Q80BV_STATUS_BUSY) {HALT_IF_DEBUGGING();} + while (get_status() & W25Q80BV_STATUS_BUSY) { + HALT_IF_DEBUGGING(); + } } void remove_write_protection() { - uint8_t data[] = {W25Q80BV_WRITE_ENABLE}; - palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + uint8_t data[] = {W25Q80BV_WRITE_ENABLE}; + palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - for (size_t j = 0; j < 1; j++) { - data[j] = spi_ssp_transfer_word(data[j]); - } + for (size_t j = 0; j < 1; j++) { + data[j] = spi_ssp_transfer_word(data[j]); + } - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - while (!(get_status() & W25Q80BV_STATUS_WEL)) {HALT_IF_DEBUGGING();} + while (!(get_status() & W25Q80BV_STATUS_WEL)) { + HALT_IF_DEBUGGING(); + } } uint32_t spi_ssp_transfer_word(const uint32_t data) { - while ((SSP_SR(SSP0_BASE) & SSP_SR_TNF) == 0) {HALT_IF_DEBUGGING();} - SSP_DR(SSP0_BASE) = data; - while (SSP_SR(SSP0_BASE) & SSP_SR_BSY) {HALT_IF_DEBUGGING();} - while ((SSP_SR(SSP0_BASE) & SSP_SR_RNE) == 0) {HALT_IF_DEBUGGING();} - return SSP_DR(SSP0_BASE); + while ((SSP_SR(SSP0_BASE) & SSP_SR_TNF) == 0) { + HALT_IF_DEBUGGING(); + } + SSP_DR(SSP0_BASE) = data; + while (SSP_SR(SSP0_BASE) & SSP_SR_BSY) { + HALT_IF_DEBUGGING(); + } + while ((SSP_SR(SSP0_BASE) & SSP_SR_RNE) == 0) { + HALT_IF_DEBUGGING(); + } + return SSP_DR(SSP0_BASE); } uint8_t get_status() { - uint8_t data[] = {W25Q80BV_READ_STATUS1, 0xFF}; - palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + uint8_t data[] = {W25Q80BV_READ_STATUS1, 0xFF}; + palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - for (size_t j = 0; j < 2; j++) { - data[j] = spi_ssp_transfer_word(data[j]); - } + for (size_t j = 0; j < 2; j++) { + data[j] = spi_ssp_transfer_word(data[j]); + } - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - return data[1]; + return data[1]; } uint8_t get_device_id() { - uint8_t data[] = {W25Q80BV_DEVICE_ID, 0xFF, 0xFF, 0xFF, 0xFF}; - palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + uint8_t data[] = {W25Q80BV_DEVICE_ID, 0xFF, 0xFF, 0xFF, 0xFF}; + palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - for (size_t j = 0; j < 5; j++) { - data[j] = spi_ssp_transfer_word(data[j]); - } + for (size_t j = 0; j < 5; j++) { + data[j] = spi_ssp_transfer_word(data[j]); + } - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - return data[4]; + return data[4]; } void erase_chip() { - uint8_t data[] = {W25Q80BV_CHIP_ERASE}; - palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + uint8_t data[] = {W25Q80BV_CHIP_ERASE}; + palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - for (size_t j = 0; j < 1; j++) { - data[j] = spi_ssp_transfer_word(data[j]); - } + for (size_t j = 0; j < 1; j++) { + data[j] = spi_ssp_transfer_word(data[j]); + } - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); } -void write(size_t page_index, uint8_t *data_buffer, size_t length) { - size_t page_len = 256U; - size_t addr = page_index * page_len; - uint8_t header[] = { - W25Q80BV_PAGE_PROGRAM, - (uint8_t)((addr & 0xFF0000) >> 16), - (uint8_t)((addr & 0xFF00) >> 8), - (uint8_t)(addr & 0xFF) - }; - - palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); - for (size_t j = 0; j < 4; j++) { - header[j] = spi_ssp_transfer_word(header[j]); - } - - for (size_t j = 0; j < length; j++) { - data_buffer[j] = spi_ssp_transfer_word(data_buffer[j]); - } - palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); -} +void write(size_t page_index, uint8_t* data_buffer, size_t length) { + size_t page_len = 256U; + size_t addr = page_index * page_len; + uint8_t header[] = { + W25Q80BV_PAGE_PROGRAM, + (uint8_t)((addr & 0xFF0000) >> 16), + (uint8_t)((addr & 0xFF00) >> 8), + (uint8_t)(addr & 0xFF)}; + + palClearPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); + for (size_t j = 0; j < 4; j++) { + header[j] = spi_ssp_transfer_word(header[j]); + } + + for (size_t j = 0; j < length; j++) { + data_buffer[j] = spi_ssp_transfer_word(data_buffer[j]); + } + palSetPad(W25Q80BV_SELECT_PORT, W25Q80BV_SELECT_PAD); } +} // namespace w25q80bv diff --git a/firmware/baseband/w25q80bv.hpp b/firmware/baseband/w25q80bv.hpp index 1d29a653e..b1705e2bf 100644 --- a/firmware/baseband/w25q80bv.hpp +++ b/firmware/baseband/w25q80bv.hpp @@ -32,13 +32,13 @@ #define RESET_CTRL1_SPIFI_RST (1 << RESET_CTRL1_SPIFI_RST_SHIFT) #define W25Q80BV_WRITE_ENABLE 0x06 -#define W25Q80BV_CHIP_ERASE 0xC7 -#define W25Q80BV_DEVICE_ID 0xAB +#define W25Q80BV_CHIP_ERASE 0xC7 +#define W25Q80BV_DEVICE_ID 0xAB #define W25Q80BV_PAGE_PROGRAM 0x02 #define PERIPH_BASE_APB0 0x40080000 #define SCU_BASE (PERIPH_BASE_APB0 + 0x06000) -#define MMIO32(addr) (*(volatile uint32_t *)(addr)) +#define MMIO32(addr) (*(volatile uint32_t*)(addr)) #define PIN_GROUP3 (SCU_BASE + 0x180) #define PIN3 0x00C @@ -48,25 +48,25 @@ #define PIN7 0x01C #define PIN8 0x020 -#define BIT0 (1<<0) -#define BIT1 (1<<1) -#define BIT2 (1<<2) -#define BIT3 (1<<3) -#define BIT4 (1<<4) -#define BIT5 (1<<5) -#define BIT6 (1<<6) -#define BIT7 (1<<7) -#define BIT8 (1<<8) +#define BIT0 (1 << 0) +#define BIT1 (1 << 1) +#define BIT2 (1 << 2) +#define BIT3 (1 << 3) +#define BIT4 (1 << 4) +#define BIT5 (1 << 5) +#define BIT6 (1 << 6) +#define BIT7 (1 << 7) +#define BIT8 (1 << 8) #define SCU_CONF_EPUN_DIS_PULLUP (BIT4) #define SCU_CONF_EHS_FAST (BIT5) #define SCU_CONF_EZI_EN_IN_BUFFER (BIT6) #define SCU_CONF_ZIF_DIS_IN_GLITCH_FILT (BIT7) -#define SCU_GPIO_FAST (SCU_CONF_EPUN_DIS_PULLUP | \ - SCU_CONF_EHS_FAST | \ - SCU_CONF_EZI_EN_IN_BUFFER | \ - SCU_CONF_ZIF_DIS_IN_GLITCH_FILT) +#define SCU_GPIO_FAST (SCU_CONF_EPUN_DIS_PULLUP | \ + SCU_CONF_EHS_FAST | \ + SCU_CONF_EZI_EN_IN_BUFFER | \ + SCU_CONF_ZIF_DIS_IN_GLITCH_FILT) #define SCU_SSP_IO SCU_GPIO_FAST #define SCU_CONF_FUNCTION0 (0x0) @@ -89,8 +89,8 @@ #define SSP0_BASE (PERIPH_BASE_APB0 + 0x03000) #define SSP_CPSR(port) MMIO32(port + 0x010) #define SSP_CR0(port) MMIO32(port + 0x000) -#define SSP_ENABLE BIT1 -#define SSP_DATA_8BITS 0x7 +#define SSP_ENABLE BIT1 +#define SSP_DATA_8BITS 0x7 #define palSetPad(port, pad) (LPC_GPIO->SET[(port)] = 1 << (pad)) #define palClearPad(port, pad) (LPC_GPIO->CLR[(port)] = 1 << (pad)) @@ -108,33 +108,32 @@ #define LEDRX_PAD 2 #define LEDTX_PAD 8 - #define W25Q80BV_STATUS_BUSY 0x01 -#define W25Q80BV_STATUS_WEL 0x02 +#define W25Q80BV_STATUS_WEL 0x02 #define W25Q80BV_READ_STATUS1 0x05 #define W25Q80BV_READ_STATUS2 0x35 -#define PERIPH_BASE_APB0 0x40080000 -#define SSP0_BASE (PERIPH_BASE_APB0 + 0x03000) +#define PERIPH_BASE_APB0 0x40080000 +#define SSP0_BASE (PERIPH_BASE_APB0 + 0x03000) #define SSP_DR(port) MMIO32(port + 0x008) #define SSP_SR(port) MMIO32(port + 0x00C) -#define SSP_SR_TNF BIT1 -#define SSP_SR_RNE BIT2 -#define SSP_SR_BSY BIT4 +#define SSP_SR_TNF BIT1 +#define SSP_SR_RNE BIT2 +#define SSP_SR_BSY BIT4 namespace w25q80bv { - void disable_spifi(); - uint8_t get_status(); - void wait_for_device(); - void wait_not_busy(); - uint32_t spi_ssp_transfer_word(const uint32_t data); - void initialite_spi(); - void setup(); - void remove_write_protection(); - uint8_t get_device_id(); - void erase_chip(); - void write(size_t page_index, uint8_t *data_buffer, size_t length); -} - -#endif/*__W25Q80BV_H__*/ +void disable_spifi(); +uint8_t get_status(); +void wait_for_device(); +void wait_not_busy(); +uint32_t spi_ssp_transfer_word(const uint32_t data); +void initialite_spi(); +void setup(); +void remove_write_protection(); +uint8_t get_device_id(); +void erase_chip(); +void write(size_t page_index, uint8_t* data_buffer, size_t length); +} // namespace w25q80bv + +#endif /*__W25Q80BV_H__*/ diff --git a/firmware/common/acars_packet.cpp b/firmware/common/acars_packet.cpp index 4a0e46bd9..653b1b627 100644 --- a/firmware/common/acars_packet.cpp +++ b/firmware/common/acars_packet.cpp @@ -29,79 +29,79 @@ namespace acars { size_t Packet::length() const { - return packet_.size(); + return packet_.size(); } bool Packet::is_valid() const { - return true; //length_valid() && crc_ok(); + return true; // length_valid() && crc_ok(); } Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } uint8_t Packet::block_id() const { - return field_.read(96, 8); + return field_.read(96, 8); } std::string Packet::registration_number() const { - std::string result; - result.reserve(7); - - const size_t character_length = 8; - for(size_t i=16; i<(16+7*character_length); i+=character_length) { - result += (field_.read(i, character_length) & 0x7F); - } - - return result; + std::string result; + result.reserve(7); + + const size_t character_length = 8; + for (size_t i = 16; i < (16 + 7 * character_length); i += character_length) { + result += (field_.read(i, character_length) & 0x7F); + } + + return result; } uint32_t Packet::read(const size_t start_bit, const size_t length) const { - return field_.read(start_bit, length); + return field_.read(start_bit, length); } /*std::string Packet::text( - const size_t start_bit, - const size_t character_count + const size_t start_bit, + const size_t character_count ) const { - std::string result; - result.reserve(character_count); - - const size_t character_length = 6; - const size_t end_bit = start_bit + character_count * character_length; - for(size_t i=start_bit; i acars_fcs { 0x1021, 0x0000, 0x0000 }; - - for(size_t i=0; i acars_fcs{0x1021, 0x0000, 0x0000}; + + for (size_t i = 0; i < data_length(); i += 8) { + acars_fcs.process_byte(field_crc.read(i, 8)); + } + + return (acars_fcs.checksum() == (unsigned)field_crc.read(data_length(), fcs_length)); } size_t Packet::data_and_fcs_length() const { - return length() - 8; + return length() - 8; } size_t Packet::data_length() const { - return data_and_fcs_length() - fcs_length; + return data_and_fcs_length() - fcs_length; } bool Packet::length_valid() const { - const size_t extra_bits = data_and_fcs_length() & 7; - if( extra_bits != 0 ) { - return false; - } + const size_t extra_bits = data_and_fcs_length() & 7; + if (extra_bits != 0) { + return false; + } - return true; + return true; } -} /* namespace ais */ +} // namespace acars diff --git a/firmware/common/acars_packet.hpp b/firmware/common/acars_packet.hpp index c8081b303..322e64c83 100644 --- a/firmware/common/acars_packet.hpp +++ b/firmware/common/acars_packet.hpp @@ -33,43 +33,42 @@ namespace acars { class Packet { -public: - constexpr Packet( - const baseband::Packet& packet - ) : packet_ { packet }, - field_ { packet_ } - { - } + public: + constexpr Packet( + const baseband::Packet& packet) + : packet_{packet}, + field_{packet_} { + } - size_t length() const; - - bool is_valid() const; + size_t length() const; - Timestamp received_at() const; + bool is_valid() const; - uint8_t block_id() const; - std::string registration_number() const; + Timestamp received_at() const; - uint32_t read(const size_t start_bit, const size_t length) const; - //std::string text(const size_t start_bit, const size_t character_count) const; + uint8_t block_id() const; + std::string registration_number() const; - bool crc_ok() const; + uint32_t read(const size_t start_bit, const size_t length) const; + // std::string text(const size_t start_bit, const size_t character_count) const; -private: - using Reader = FieldReader; - using CRCReader = FieldReader; - - const baseband::Packet packet_; - const Reader field_; + bool crc_ok() const; - const size_t fcs_length = 16; + private: + using Reader = FieldReader; + using CRCReader = FieldReader; - size_t data_and_fcs_length() const; - size_t data_length() const; + const baseband::Packet packet_; + const Reader field_; - bool length_valid() const; + const size_t fcs_length = 16; + + size_t data_and_fcs_length() const; + size_t data_length() const; + + bool length_valid() const; }; } /* namespace acars */ -#endif/*__ACARS_PACKET_H__*/ +#endif /*__ACARS_PACKET_H__*/ diff --git a/firmware/common/adc.hpp b/firmware/common/adc.hpp index 71414666a..c530db2dd 100644 --- a/firmware/common/adc.hpp +++ b/firmware/common/adc.hpp @@ -30,106 +30,98 @@ namespace adc { constexpr size_t clock_rate_max = 4500000U; struct CR { - uint32_t sel; - uint32_t clkdiv; - uint32_t resolution; - uint32_t edge; - - constexpr operator uint32_t() const { - return - ((sel & 0xff) << 0) - | ((clkdiv & 0xff) << 8) - | ((0 & 1) << 16) - | (((10 - resolution) & 7) << 17) - | ((1 & 1) << 21) - | ((0 & 7) << 24) - | ((edge & 1) << 27) - ; - } + uint32_t sel; + uint32_t clkdiv; + uint32_t resolution; + uint32_t edge; + + constexpr operator uint32_t() const { + return ((sel & 0xff) << 0) | ((clkdiv & 0xff) << 8) | ((0 & 1) << 16) | (((10 - resolution) & 7) << 17) | ((1 & 1) << 21) | ((0 & 7) << 24) | ((edge & 1) << 27); + } }; struct Config { - uint32_t cr; + uint32_t cr; }; -template +template class ADC { -public: - static void power_up(const Config config) { - adcp().CR = config.cr; - } - - static void clock_enable() { - if( &adcp() == LPC_ADC0 ) { - LPC_CCU1->CLK_APB3_ADC0_CFG.AUTO = 1; - LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 1; - } - if( &adcp() == LPC_ADC1 ) { - LPC_CCU1->CLK_APB3_ADC1_CFG.AUTO = 1; - LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 1; - } - } - - static void clock_disable() { - if( &adcp() == LPC_ADC0 ) { - LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 0; - } - if( &adcp() == LPC_ADC1 ) { - LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 0; - } - } - - static void disable() { - adcp().INTEN = 0; - adcp().CR = 0; - - clock_disable(); - } - - static void interrupts_disable() { - adcp().INTEN = 0; - } - - static void interrupts_enable(const uint32_t mask) { - adcp().INTEN = mask; - } - - static void start_burst() { - adcp().CR |= (1U << 16); - } - - static void start_once() { - adcp().CR |= (1U << 24); - } - - static void start_once(size_t n) { - uint32_t cr = adcp().CR; - cr &= ~(0xffU); - cr |= (1 << 24) | (1 << n); - adcp().CR = cr; - } - - static void stop_burst() { - adcp().CR &= ~(1U << 16); - } - - static uint32_t convert(size_t n) { - start_once(n); - while(true) { - const uint32_t data = adcp().DR[n]; - if( (data >> 31) & 1 ) { - return (data >> 6) & 0x3ff; - } - } - } - -private: - static LPC_ADCx_Type& adcp() { - return *reinterpret_cast(BaseAddress); - } + public: + static void power_up(const Config config) { + adcp().CR = config.cr; + } + + static void clock_enable() { + if (&adcp() == LPC_ADC0) { + LPC_CCU1->CLK_APB3_ADC0_CFG.AUTO = 1; + LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 1; + } + if (&adcp() == LPC_ADC1) { + LPC_CCU1->CLK_APB3_ADC1_CFG.AUTO = 1; + LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 1; + } + } + + static void clock_disable() { + if (&adcp() == LPC_ADC0) { + LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 0; + } + if (&adcp() == LPC_ADC1) { + LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 0; + } + } + + static void disable() { + adcp().INTEN = 0; + adcp().CR = 0; + + clock_disable(); + } + + static void interrupts_disable() { + adcp().INTEN = 0; + } + + static void interrupts_enable(const uint32_t mask) { + adcp().INTEN = mask; + } + + static void start_burst() { + adcp().CR |= (1U << 16); + } + + static void start_once() { + adcp().CR |= (1U << 24); + } + + static void start_once(size_t n) { + uint32_t cr = adcp().CR; + cr &= ~(0xffU); + cr |= (1 << 24) | (1 << n); + adcp().CR = cr; + } + + static void stop_burst() { + adcp().CR &= ~(1U << 16); + } + + static uint32_t convert(size_t n) { + start_once(n); + while (true) { + const uint32_t data = adcp().DR[n]; + if ((data >> 31) & 1) { + return (data >> 6) & 0x3ff; + } + } + } + + private: + static LPC_ADCx_Type& adcp() { + return *reinterpret_cast(BaseAddress); + } }; } /* namespace adc */ } /* namespace lpc43xx */ -#endif/*__ADC_H__*/ +#endif /*__ADC_H__*/ diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index 712155048..1b3628369 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -29,83 +29,83 @@ namespace adsb { void make_frame_adsb(ADSBFrame& frame, const uint32_t ICAO_address) { - frame.clear(); - frame.push_byte((DF_ADSB << 3) | 5); // DF=17 and CA - frame.push_byte(ICAO_address >> 16); - frame.push_byte(ICAO_address >> 8); - frame.push_byte(ICAO_address & 0xFF); + frame.clear(); + frame.push_byte((DF_ADSB << 3) | 5); // DF=17 and CA + frame.push_byte(ICAO_address >> 16); + frame.push_byte(ICAO_address >> 8); + frame.push_byte(ICAO_address & 0xFF); } -// Civil aircraft ADS-B message type starts with Dowlink Format (DF=17) and frame is 112 bits long. +// Civil aircraft ADS-B message type starts with Dowlink Format (DF=17) and frame is 112 bits long. // All known DF's >=16 are long (112 bits). All known DF's <=15 are short (56 bits).(In this case 112 bits) -// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) +// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) // Aircraft identification and category message structure, the ME (56 bits) = TC,5 bits | CA,3 bits | C1,6 bits | C2,6 bits | C3,6 | C4,6 | C5,6 | C6,6 | C7,6 | C8,6 -// TC : (1..4) : Aircraft identification Type Code . // TC : 9 to 18: Airbone postion // TC : 19 Airbone velocity . +// TC : (1..4) : Aircraft identification Type Code . // TC : 9 to 18: Airbone postion // TC : 19 Airbone velocity . // In this encode_frame_identification function we are using DF = 17 (112 bits long) and TC=4) void encode_frame_id(ADSBFrame& frame, const uint32_t ICAO_address, const std::string& callsign) { - std::string callsign_formatted(8, '_'); - uint64_t callsign_coded = 0; - uint32_t c, s; - char ch; - - make_frame_adsb(frame, ICAO_address); // Header DF=17 Downlink Format = ADS-B message (frame 112 bits) - - frame.push_byte(TC_IDENT << 3); // 5 top bits ME = TC = we use fix 4 , # Type aircraft Identification Category = TC_IDENT = 4, - - // Translate and encode callsign - for (c = 0; c < 8; c++) { - ch = callsign[c]; - - for (s = 0; s < 64; s++) - if (ch == icao_id_lut[s]) break; - - if (s == 64) { - ch = ' '; // Invalid character - s = 32; - } - - callsign_coded <<= 6; - callsign_coded |= s; - - //callsign[c] = ch; - } - - // Insert callsign in frame - for (c = 0; c < 6; c++) - frame.push_byte((callsign_coded >> ((5 - c) * 8)) & 0xFF); - - frame.make_CRC(); + std::string callsign_formatted(8, '_'); + uint64_t callsign_coded = 0; + uint32_t c, s; + char ch; + + make_frame_adsb(frame, ICAO_address); // Header DF=17 Downlink Format = ADS-B message (frame 112 bits) + + frame.push_byte(TC_IDENT << 3); // 5 top bits ME = TC = we use fix 4 , # Type aircraft Identification Category = TC_IDENT = 4, + + // Translate and encode callsign + for (c = 0; c < 8; c++) { + ch = callsign[c]; + + for (s = 0; s < 64; s++) + if (ch == icao_id_lut[s]) break; + + if (s == 64) { + ch = ' '; // Invalid character + s = 32; + } + + callsign_coded <<= 6; + callsign_coded |= s; + + // callsign[c] = ch; + } + + // Insert callsign in frame + for (c = 0; c < 6; c++) + frame.push_byte((callsign_coded >> ((5 - c) * 8)) & 0xFF); + + frame.make_CRC(); } std::string decode_frame_id(ADSBFrame& frame) { - std::string callsign = ""; - uint8_t * raw_data = frame.get_raw_data(); - uint64_t callsign_coded = 0; - uint32_t c; - - // Frame bytes to long - for (c = 5; c < 11; c++) { - callsign_coded <<= 8; - callsign_coded |= raw_data[c]; - } - - // Long to 6-bit characters - for (c = 0; c < 8; c++) { - callsign.append(1, icao_id_lut[(callsign_coded >> 42) & 0x3F]); - callsign_coded <<= 6; - } - - return callsign; + std::string callsign = ""; + uint8_t* raw_data = frame.get_raw_data(); + uint64_t callsign_coded = 0; + uint32_t c; + + // Frame bytes to long + for (c = 5; c < 11; c++) { + callsign_coded <<= 8; + callsign_coded |= raw_data[c]; + } + + // Long to 6-bit characters + for (c = 0; c < 8; c++) { + callsign.append(1, icao_id_lut[(callsign_coded >> 42) & 0x3F]); + callsign_coded <<= 6; + } + + return callsign; } /*void generate_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code) { - make_frame_mode_s(frame, ICAO_address); - - frame.push_byte((28 << 3) + 1); // TC = 28 (Emergency), subtype = 1 (Emergency) - frame.push_byte(code << 5); - - frame.make_CRC(); + make_frame_mode_s(frame, ICAO_address); + + frame.push_byte((28 << 3) + 1); // TC = 28 (Emergency), subtype = 1 (Emergency) + frame.push_byte(code << 5); + + frame.make_CRC(); }*/ // Mode S services. (Mode Select Beacon System). There are two types of Mode S interrogations: The short (56 bits) . and the long (112 bits ) @@ -113,111 +113,116 @@ std::string decode_frame_id(ADSBFrame& frame) { // Identity squawk replies can be DF=5 (Surveillance Identity reply)(56 bits) / DF 21 (Comm-B with Identity reply) (112 bits) // DF 21: Comm-B with identity reply structure = |DF=21(5 bits)|FS (3 bits)|DR (5 bits)|UM (6 bits) |Identity squawk code (13 bits) |MB (56 bits) |CRC (24 bits) (total 112 bits) // Comm-B messages count for a large portion of the Mode S selective interrogation responses.(means, only transmitted information upon selective request) -// Comm-B messages protocol supports many different types of msg's (up to 255).The three more popular ones are the following ones: +// Comm-B messages protocol supports many different types of msg's (up to 255).The three more popular ones are the following ones: // (a) Mode S ELementary Surveillance (ELS) / (b) Mode S EnHanced Surveillance (EHS) / (c) Meteorological information // Comm-B Data Selector (BDS) is an 8-bit code that determines which information to be included in the MB fields void encode_frame_squawk(ADSBFrame& frame, const uint16_t squawk) { - uint16_t squawk_coded; - uint8_t UM_field=0b111101, FS=0b010, DR=0b00001 ; - - // To be sent those fields, (56 bits). We should store byte by byte into the frame , and It will be transmitted byte to byte same FIFO order. - // DF 5 bits 5 DF=21 (5 top bits) Downlink Format - // FS 3 bits 0b000, FS (3 bottom bits) (Flight status ) = 000 : no alert, no SPI, aircraft is airborne - // DR 5 bits 0b00001 DR (Downlink request) (5 top bits) = 00000 : no downlink request (In surveillance replies, only values 0, 1, 4, and 5 are used.) - // UM 6 bits 0b000010 UM (Utility message)= 000000, Utility message (UM): 6 bits, contains transponder communication status information.(IIS + IDS) - // Identity_code 13 bits squawk id_code in special interleaved format. - // MB 56 bits - // CRC partity 24 bits parity checksum , cyclic redundancy chek. - - frame.clear(); - frame.push_byte( ( DF_EHS_SQUAWK << 3 ) | FS ); // DF=21 (5bits) + FS (3bits, 010 : alert, NO SPI, aircraft is airborne) - frame.push_byte(( DR <<3 ) | ( UM_field>>3) ); // DR (5bits, 00001 : downlink request + 3 top bits of UM , let's use 0b111000 - - // 12 11 10 9 8 7 6 5 4 3 2 1 0 (Original notes) bit weight position---------------------- - // 32 31 30 29 28 27 26 25 24 23 22 21 20 (it was wrong , now corrected) bit order inside frame msg - // D4 B4 D2 B2 D1 B1 __ A4 C4 A2 C2 A1 C1 standard spec order of the 13 bits, to be sent , each octal digit = 3 bits , (example A=7 binary A4 A2 A0 = 111 - // ABCD = code (octal, 0000~7777) - - // FEDCBA9876543210 - // xAAAxBBBxCCCxDDD 4 x 3 bits (each octal digit) - // x421x421x421x421 binary weight of each binary position, example AAA = 7 = 111 ------------------------- - - // Additional , expanded notes ------------------------------- - // Identity squawk code ABCD = code (octal, 0000~7777) , input concatenated squawk : 4 octal digits ,A4 A2 A1-B4 B2 B1-C4 C2 C1-D4 D2 D1. - // 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 bit position of the frame msg, (Squawk id is bit 20-32, from C1..D4). - // UM4 UM2 UM1 C1 A1 C2 A2 C4 A4 X B1 D1 B2 D2 B4 D4 3 lower bit UM4,UM2,UM1 of the UM (6bits), and we should re-order the 13 bits ABCD changing 12 bit poistion based on std. - // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Two bytes , bit position to be send. - - squawk_coded = ( (( UM_field & (0b111)) <<13) | ((squawk << 9) & 0x1000) ) | // C1 It also leaves in the top 3 lower bottom bitd part of UM field. - ((squawk << 2) & 0x0800) | // A1 - ((squawk << 6) & 0x0400) | // C2 - ((squawk >> 1) & 0x0200) | // A2 - ((squawk << 3) & 0x0100) | // C4 - ((squawk >> 4) & 0x0080) | // A4 - - ((squawk >> 1) & 0x0020) | // B1 - ((squawk << 4) & 0x0010) | // D1 - ((squawk >> 4) & 0x0008) | // B2 - ((squawk << 1) & 0x0004) | // D2 - ((squawk >> 7) & 0x0002) | // B4 - ((squawk >> 2) & 0x0001); // D4 - - frame.push_byte(squawk_coded>>8); // UM4 UM2 UM1 C1 A1 C2 A2 C4 that is the correct order, confirmed with dump1090 - frame.push_byte(squawk_coded); // A4 X(1) B1 D1 B2 D2 B4 D4 that is the correct order, confirmed with dupm1090 - - // DF 21 messages , has 56 bits more after 13 bits of squawk, we should add MB (56 bits) - // In this example, we are adding fixed MB = Track and turn report (BDS 5,0) decoding MB example = "F9363D3BBF9CE9" (56 bits) - // # -9.7, roll angle (deg) - // # 140.273, track angle (deg) - // # -0.406, track angle rate (deg/s) - // # 476, ground speed (kt) - // # 466, TAS (kt) - - frame.push_byte(0xF9);frame.push_byte(0x36);frame.push_byte(0x3D);frame.push_byte(0x3B); // If we deltele those two lines, to send this fixed MB (56 bits), - frame.push_byte(0xBF);frame.push_byte(0x9C);frame.push_byte(0xE9); // current fw is padding with 56 x 0's to complete 112 bits msg. - - frame.make_CRC(); - } + uint16_t squawk_coded; + uint8_t UM_field = 0b111101, FS = 0b010, DR = 0b00001; + + // To be sent those fields, (56 bits). We should store byte by byte into the frame , and It will be transmitted byte to byte same FIFO order. + // DF 5 bits 5 DF=21 (5 top bits) Downlink Format + // FS 3 bits 0b000, FS (3 bottom bits) (Flight status ) = 000 : no alert, no SPI, aircraft is airborne + // DR 5 bits 0b00001 DR (Downlink request) (5 top bits) = 00000 : no downlink request (In surveillance replies, only values 0, 1, 4, and 5 are used.) + // UM 6 bits 0b000010 UM (Utility message)= 000000, Utility message (UM): 6 bits, contains transponder communication status information.(IIS + IDS) + // Identity_code 13 bits squawk id_code in special interleaved format. + // MB 56 bits + // CRC partity 24 bits parity checksum , cyclic redundancy chek. + + frame.clear(); + frame.push_byte((DF_EHS_SQUAWK << 3) | FS); // DF=21 (5bits) + FS (3bits, 010 : alert, NO SPI, aircraft is airborne) + frame.push_byte((DR << 3) | (UM_field >> 3)); // DR (5bits, 00001 : downlink request + 3 top bits of UM , let's use 0b111000 + + // 12 11 10 9 8 7 6 5 4 3 2 1 0 (Original notes) bit weight position---------------------- + // 32 31 30 29 28 27 26 25 24 23 22 21 20 (it was wrong , now corrected) bit order inside frame msg + // D4 B4 D2 B2 D1 B1 __ A4 C4 A2 C2 A1 C1 standard spec order of the 13 bits, to be sent , each octal digit = 3 bits , (example A=7 binary A4 A2 A0 = 111 + // ABCD = code (octal, 0000~7777) + + // FEDCBA9876543210 + // xAAAxBBBxCCCxDDD 4 x 3 bits (each octal digit) + // x421x421x421x421 binary weight of each binary position, example AAA = 7 = 111 ------------------------- + + // Additional , expanded notes ------------------------------- + // Identity squawk code ABCD = code (octal, 0000~7777) , input concatenated squawk : 4 octal digits ,A4 A2 A1-B4 B2 B1-C4 C2 C1-D4 D2 D1. + // 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 bit position of the frame msg, (Squawk id is bit 20-32, from C1..D4). + // UM4 UM2 UM1 C1 A1 C2 A2 C4 A4 X B1 D1 B2 D2 B4 D4 3 lower bit UM4,UM2,UM1 of the UM (6bits), and we should re-order the 13 bits ABCD changing 12 bit poistion based on std. + // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Two bytes , bit position to be send. + + squawk_coded = (((UM_field & (0b111)) << 13) | ((squawk << 9) & 0x1000)) | // C1 It also leaves in the top 3 lower bottom bitd part of UM field. + ((squawk << 2) & 0x0800) | // A1 + ((squawk << 6) & 0x0400) | // C2 + ((squawk >> 1) & 0x0200) | // A2 + ((squawk << 3) & 0x0100) | // C4 + ((squawk >> 4) & 0x0080) | // A4 + + ((squawk >> 1) & 0x0020) | // B1 + ((squawk << 4) & 0x0010) | // D1 + ((squawk >> 4) & 0x0008) | // B2 + ((squawk << 1) & 0x0004) | // D2 + ((squawk >> 7) & 0x0002) | // B4 + ((squawk >> 2) & 0x0001); // D4 + + frame.push_byte(squawk_coded >> 8); // UM4 UM2 UM1 C1 A1 C2 A2 C4 that is the correct order, confirmed with dump1090 + frame.push_byte(squawk_coded); // A4 X(1) B1 D1 B2 D2 B4 D4 that is the correct order, confirmed with dupm1090 + + // DF 21 messages , has 56 bits more after 13 bits of squawk, we should add MB (56 bits) + // In this example, we are adding fixed MB = Track and turn report (BDS 5,0) decoding MB example = "F9363D3BBF9CE9" (56 bits) + // # -9.7, roll angle (deg) + // # 140.273, track angle (deg) + // # -0.406, track angle rate (deg/s) + // # 476, ground speed (kt) + // # 466, TAS (kt) + + frame.push_byte(0xF9); + frame.push_byte(0x36); + frame.push_byte(0x3D); + frame.push_byte(0x3B); // If we deltele those two lines, to send this fixed MB (56 bits), + frame.push_byte(0xBF); + frame.push_byte(0x9C); + frame.push_byte(0xE9); // current fw is padding with 56 x 0's to complete 112 bits msg. + + frame.make_CRC(); +} float cpr_mod(float a, float b) { - return a - (b * floor(a / b)); + return a - (b * floor(a / b)); } int cpr_NL_precise(float lat) { - return (int) floor(2 * PI / acos(1 - ((1 - cos(PI / (2 * NZ))) / pow(cos(PI * lat / 180), 2)))); + return (int)floor(2 * PI / acos(1 - ((1 - cos(PI / (2 * NZ))) / pow(cos(PI * lat / 180), 2)))); } int cpr_NL_approx(float lat) { - if (lat < 0) - lat = -lat; // Symmetry - - for (size_t c = 0; c < 58; c++) { - if (lat < adsb_lat_lut[c]) - return 59 - c; - } - - return 1; + if (lat < 0) + lat = -lat; // Symmetry + + for (size_t c = 0; c < 58; c++) { + if (lat < adsb_lat_lut[c]) + return 59 - c; + } + + return 1; } int cpr_NL(float lat) { - // TODO prove that the approximate function is good - // enough for the precision we need. Uncomment if - // that is true. No performance penalty was noticed - // from testing, but if you find it might be an issue, - // switch to cpr_NL_approx() instead: + // TODO prove that the approximate function is good + // enough for the precision we need. Uncomment if + // that is true. No performance penalty was noticed + // from testing, but if you find it might be an issue, + // switch to cpr_NL_approx() instead: - //return cpr_NL_approx(lat); + // return cpr_NL_approx(lat); - return cpr_NL_precise(lat); + return cpr_NL_precise(lat); } int cpr_N(float lat, int is_odd) { int nl = cpr_NL(lat) - is_odd; - + if (nl < 1) - nl = 1; - + nl = 1; + return nl; } @@ -225,220 +230,215 @@ float cpr_Dlon(float lat, int is_odd) { return 360.0 / cpr_N(lat, is_odd); } -// An ADS-B frame Civil aircraft message type starts with Dowlink Format (DF=17) and frame is 112 bits long. +// An ADS-B frame Civil aircraft message type starts with Dowlink Format (DF=17) and frame is 112 bits long. // All known DF's >=16 are long (112 bits). All known DF's <=15 are short (56 bits). (In this case 112 bits) -// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) +// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) // Airborne position msg struct, the ME (56 bits) = |TC,5 bits| SS, 2 bits | SAF, 1 | ALT, 12 | T, 1 | F, 1 | LAT-CPR, 17 | LON-CPR, 17 // TC : (1..4) : Aircraft identification Type Code. // TC : 9 to 18: Airbone postion and altitude // TC : 19 Airbone velocity . // Airborne position message is used to broadcast the position and altitude of the aircraft. It has the Type Code 9–18 and 20–22. (here , we use TC=11) -void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, - const float latitude, const float longitude, const uint32_t time_parity) { - - uint32_t altitude_coded; - uint32_t lat, lon; - float delta_lat, yz, rlat, delta_lon, xz; - - make_frame_adsb(frame, ICAO_address); // Header DF=17 (long frame 112 bits) - - frame.push_byte(TC_AIRBORNE_POS << 3); // Bits 2~1: Surveillance Status, bit 0: NICsb - - altitude_coded = (altitude + 1000) / 25; // 25ft precision, insert Q-bit (1) - altitude_coded = ((altitude_coded & 0x7F0) << 1) | 0x10 | (altitude_coded & 0x0F); - - frame.push_byte(altitude_coded >> 4); // Top-most altitude bits - - // CPR encoding - // Info from: http://antena.fe.uni-lj.si/literatura/Razno/Avionika/modes/CPRencoding.pdf - - delta_lat = 360.0 / ((4.0 * NZ) - time_parity); // NZ = 15 - yz = floor(CPR_MAX_VALUE * (cpr_mod(latitude, delta_lat) / delta_lat) + 0.5); - rlat = delta_lat * ((yz / CPR_MAX_VALUE) + floor(latitude / delta_lat)); - - if ((cpr_NL(rlat) - time_parity) > 0) - delta_lon = 360.0 / cpr_N(rlat, time_parity); - else - delta_lon = 360.0; - xz = floor(CPR_MAX_VALUE * (cpr_mod(longitude, delta_lon) / delta_lon) + 0.5); - - lat = cpr_mod(yz, CPR_MAX_VALUE); - lon = cpr_mod(xz, CPR_MAX_VALUE); - - frame.push_byte((altitude_coded << 4) | ((uint32_t)time_parity << 2) | (lat >> 15)); // T = 0 - frame.push_byte(lat >> 7); - frame.push_byte((lat << 1) | (lon >> 16)); - frame.push_byte(lon >> 8); - frame.push_byte(lon); - - frame.make_CRC(); +void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, const float latitude, const float longitude, const uint32_t time_parity) { + uint32_t altitude_coded; + uint32_t lat, lon; + float delta_lat, yz, rlat, delta_lon, xz; + + make_frame_adsb(frame, ICAO_address); // Header DF=17 (long frame 112 bits) + + frame.push_byte(TC_AIRBORNE_POS << 3); // Bits 2~1: Surveillance Status, bit 0: NICsb + + altitude_coded = (altitude + 1000) / 25; // 25ft precision, insert Q-bit (1) + altitude_coded = ((altitude_coded & 0x7F0) << 1) | 0x10 | (altitude_coded & 0x0F); + + frame.push_byte(altitude_coded >> 4); // Top-most altitude bits + + // CPR encoding + // Info from: http://antena.fe.uni-lj.si/literatura/Razno/Avionika/modes/CPRencoding.pdf + + delta_lat = 360.0 / ((4.0 * NZ) - time_parity); // NZ = 15 + yz = floor(CPR_MAX_VALUE * (cpr_mod(latitude, delta_lat) / delta_lat) + 0.5); + rlat = delta_lat * ((yz / CPR_MAX_VALUE) + floor(latitude / delta_lat)); + + if ((cpr_NL(rlat) - time_parity) > 0) + delta_lon = 360.0 / cpr_N(rlat, time_parity); + else + delta_lon = 360.0; + xz = floor(CPR_MAX_VALUE * (cpr_mod(longitude, delta_lon) / delta_lon) + 0.5); + + lat = cpr_mod(yz, CPR_MAX_VALUE); + lon = cpr_mod(xz, CPR_MAX_VALUE); + + frame.push_byte((altitude_coded << 4) | ((uint32_t)time_parity << 2) | (lat >> 15)); // T = 0 + frame.push_byte(lat >> 7); + frame.push_byte((lat << 1) | (lon >> 16)); + frame.push_byte(lon >> 8); + frame.push_byte(lon); + + frame.make_CRC(); } // Decoding method from dump1090 adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd) { - uint8_t * raw_data; - uint32_t latcprE, latcprO, loncprE, loncprO; - float latE, latO, m, Dlon, cpr_lon_odd, cpr_lon_even, cpr_lat_odd, cpr_lat_even; - int ni; - adsb_pos position { false, 0, 0, 0 }; - - uint32_t time_even = frame_even.get_rx_timestamp(); - uint32_t time_odd = frame_odd.get_rx_timestamp(); - uint8_t * frame_data_even = frame_even.get_raw_data(); - uint8_t * frame_data_odd = frame_odd.get_raw_data(); - - // Return most recent altitude - if (time_even > time_odd) - raw_data = frame_data_even; - else - raw_data = frame_data_odd; - - // Q-bit must be present - if (raw_data[5] & 1) - position.altitude = ((((raw_data[5] & 0xFE) << 3) | ((raw_data[6] & 0xF0) >> 4)) * 25) - 1000; - - // Position - latcprE = ((frame_data_even[6] & 3) << 15) | (frame_data_even[7] << 7) | (frame_data_even[8] >> 1); - loncprE = ((frame_data_even[8] & 1) << 16) | (frame_data_even[9] << 8) | frame_data_even[10]; - - latcprO = ((frame_data_odd[6] & 3) << 15) | (frame_data_odd[7] << 7) | (frame_data_odd[8] >> 1); - loncprO = ((frame_data_odd[8] & 1) << 16) | (frame_data_odd[9] << 8) | frame_data_odd[10]; - - // Calculate the coefficients - cpr_lon_even = loncprE / CPR_MAX_VALUE; - cpr_lon_odd = loncprO / CPR_MAX_VALUE; - - cpr_lat_odd = latcprO / CPR_MAX_VALUE; - cpr_lat_even = latcprE / CPR_MAX_VALUE; - - // Compute latitude index - float j = floor(((59.0 * cpr_lat_even) - (60.0 * cpr_lat_odd)) + 0.5); - latE = (360.0 / 60.0) * (cpr_mod(j, 60) + cpr_lat_even); - latO = (360.0 / 59.0) * (cpr_mod(j, 59) + cpr_lat_odd); - - if (latE >= 270) latE -= 360; - if (latO >= 270) latO -= 360; - - // Both frames must be in the same latitude zone - if (cpr_NL(latE) != cpr_NL(latO)) - return position; - - // Compute longitude - if (time_even > time_odd) { - // Use even frame2 - ni = cpr_N(latE, 0); - Dlon = 360.0 / ni; - - m = floor((cpr_lon_even * (cpr_NL(latE) - 1)) - (cpr_lon_odd * cpr_NL(latE)) + 0.5); - - position.longitude = Dlon * (cpr_mod(m, ni) + cpr_lon_even); - - position.latitude = latE; - } else { - // Use odd frame - ni = cpr_N(latO, 1); - Dlon = 360.0 / ni; - - m = floor((cpr_lon_even * (cpr_NL(latO) - 1)) - (cpr_lon_odd * cpr_NL(latO)) + 0.5); - - position.longitude = Dlon * (cpr_mod(m, ni) + cpr_lon_odd); - - position.latitude = latO; - } - - if (position.longitude >= 180) position.longitude -= 360; - - position.valid = true; - - return position; + uint8_t* raw_data; + uint32_t latcprE, latcprO, loncprE, loncprO; + float latE, latO, m, Dlon, cpr_lon_odd, cpr_lon_even, cpr_lat_odd, cpr_lat_even; + int ni; + adsb_pos position{false, 0, 0, 0}; + + uint32_t time_even = frame_even.get_rx_timestamp(); + uint32_t time_odd = frame_odd.get_rx_timestamp(); + uint8_t* frame_data_even = frame_even.get_raw_data(); + uint8_t* frame_data_odd = frame_odd.get_raw_data(); + + // Return most recent altitude + if (time_even > time_odd) + raw_data = frame_data_even; + else + raw_data = frame_data_odd; + + // Q-bit must be present + if (raw_data[5] & 1) + position.altitude = ((((raw_data[5] & 0xFE) << 3) | ((raw_data[6] & 0xF0) >> 4)) * 25) - 1000; + + // Position + latcprE = ((frame_data_even[6] & 3) << 15) | (frame_data_even[7] << 7) | (frame_data_even[8] >> 1); + loncprE = ((frame_data_even[8] & 1) << 16) | (frame_data_even[9] << 8) | frame_data_even[10]; + + latcprO = ((frame_data_odd[6] & 3) << 15) | (frame_data_odd[7] << 7) | (frame_data_odd[8] >> 1); + loncprO = ((frame_data_odd[8] & 1) << 16) | (frame_data_odd[9] << 8) | frame_data_odd[10]; + + // Calculate the coefficients + cpr_lon_even = loncprE / CPR_MAX_VALUE; + cpr_lon_odd = loncprO / CPR_MAX_VALUE; + + cpr_lat_odd = latcprO / CPR_MAX_VALUE; + cpr_lat_even = latcprE / CPR_MAX_VALUE; + + // Compute latitude index + float j = floor(((59.0 * cpr_lat_even) - (60.0 * cpr_lat_odd)) + 0.5); + latE = (360.0 / 60.0) * (cpr_mod(j, 60) + cpr_lat_even); + latO = (360.0 / 59.0) * (cpr_mod(j, 59) + cpr_lat_odd); + + if (latE >= 270) latE -= 360; + if (latO >= 270) latO -= 360; + + // Both frames must be in the same latitude zone + if (cpr_NL(latE) != cpr_NL(latO)) + return position; + + // Compute longitude + if (time_even > time_odd) { + // Use even frame2 + ni = cpr_N(latE, 0); + Dlon = 360.0 / ni; + + m = floor((cpr_lon_even * (cpr_NL(latE) - 1)) - (cpr_lon_odd * cpr_NL(latE)) + 0.5); + + position.longitude = Dlon * (cpr_mod(m, ni) + cpr_lon_even); + + position.latitude = latE; + } else { + // Use odd frame + ni = cpr_N(latO, 1); + Dlon = 360.0 / ni; + + m = floor((cpr_lon_even * (cpr_NL(latO) - 1)) - (cpr_lon_odd * cpr_NL(latO)) + 0.5); + + position.longitude = Dlon * (cpr_mod(m, ni) + cpr_lon_odd); + + position.latitude = latO; + } + + if (position.longitude >= 180) position.longitude -= 360; + + position.valid = true; + + return position; } // An ADS-B frame is 112 bits long. Civil aircraft ADS-B message starts with the Downlink Format ,DF=17. -// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) +// Msg structure consists of five main parts :|DF=17 (5 bits)|CA (3 bits)|ICAO (24 bits)|ME (56 bits)|CRC (24 bits) // Airborne velocities are all transmitted with Type Code 19 ( TC=19 ) inside ME (56 bits) // [units] : speed is in knots, vertical rate climb / descend is in ft/min -void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, - const float angle, const int32_t v_rate) { - - int32_t velo_ew, velo_ns; - uint32_t velo_ew_abs, velo_ns_abs, v_rate_coded_abs; - - // To get NS and EW speeds from speed and bearing, a polar to cartesian conversion is enough - velo_ew = static_cast(sin_f32(DEG_TO_RAD(angle) ) * speed); // East direction, is the projection from West -> East is directly sin(angle=Compas Bearing) , (90º is the max +1, EAST) max velo_EW - velo_ns = static_cast(sin_f32( (pi/2 - DEG_TO_RAD(angle) ) ) * speed); // North direction,is the projection of North = cos(angle=Compas Bearing), cos(angle)= sen(90-angle) (0º is the max +1 NORTH) max velo_NS - - v_rate_coded_abs = (abs(v_rate) / 64) + 1; //encoding vertical rate source. (Decoding, VR ft/min = (Decimal v_rate_value - 1)* 64) - - velo_ew_abs = abs(velo_ew) + 1; // encoding Velo speed EW , when sign Direction is 0 (+): West->East, (-) 1: East->West - velo_ns_abs = abs(velo_ns) + 1; // encoding Velo speed NS , when sign Direction is 0 (+): South->North , (-) 1: North->South - - make_frame_adsb(frame, ICAO_address); // Header DF=17 (long frame 112 bits) - - // Airborne velocities are all transmitted with Type Code 19 ( TC=19, using 5 bits ,TC=19 [Binary: 10011]), the following 3 bits are Subt-type Code ,SC= 1,2,3,4 - // SC Subtypes code 1 and 2 are used to report ground speeds of aircraft. (SC 3,4 to used to report true airspeed. SC 2,4 are for supersonic aircraft (not used in commercial airline). - frame.push_byte((TC_AIRBORNE_VELO << 3) | 1); // 1st byte , top 5 bits Type Code TC=19, and lower 3 bits (38-40 bits), SC=001 Subtype Code SC: 1 (subsonic) , - - // Message A, (ME bits from 14-35) , 22 bits = Sign ew(1 bit) + V_ew (10 bits) + Sign_ns (1 bit) + V_ns (10 bits) - // Vertical rate source bit VrSrc (ME bit 36) indicates source of the altitude measurements. GNSS altitude(0) / , barometric altitude(1). - // Vertical rate source direction,(ME bit 37) movement can be read from Svr bit , with 0 and 1 referring to climb and descent, respectively (ft/min) +void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, const float angle, const int32_t v_rate) { + int32_t velo_ew, velo_ns; + uint32_t velo_ew_abs, velo_ns_abs, v_rate_coded_abs; + + // To get NS and EW speeds from speed and bearing, a polar to cartesian conversion is enough + velo_ew = static_cast(sin_f32(DEG_TO_RAD(angle)) * speed); // East direction, is the projection from West -> East is directly sin(angle=Compas Bearing) , (90º is the max +1, EAST) max velo_EW + velo_ns = static_cast(sin_f32((pi / 2 - DEG_TO_RAD(angle))) * speed); // North direction,is the projection of North = cos(angle=Compas Bearing), cos(angle)= sen(90-angle) (0º is the max +1 NORTH) max velo_NS + + v_rate_coded_abs = (abs(v_rate) / 64) + 1; // encoding vertical rate source. (Decoding, VR ft/min = (Decimal v_rate_value - 1)* 64) + + velo_ew_abs = abs(velo_ew) + 1; // encoding Velo speed EW , when sign Direction is 0 (+): West->East, (-) 1: East->West + velo_ns_abs = abs(velo_ns) + 1; // encoding Velo speed NS , when sign Direction is 0 (+): South->North , (-) 1: North->South + + make_frame_adsb(frame, ICAO_address); // Header DF=17 (long frame 112 bits) + + // Airborne velocities are all transmitted with Type Code 19 ( TC=19, using 5 bits ,TC=19 [Binary: 10011]), the following 3 bits are Subt-type Code ,SC= 1,2,3,4 + // SC Subtypes code 1 and 2 are used to report ground speeds of aircraft. (SC 3,4 to used to report true airspeed. SC 2,4 are for supersonic aircraft (not used in commercial airline). + frame.push_byte((TC_AIRBORNE_VELO << 3) | 1); // 1st byte , top 5 bits Type Code TC=19, and lower 3 bits (38-40 bits), SC=001 Subtype Code SC: 1 (subsonic) , + + // Message A, (ME bits from 14-35) , 22 bits = Sign ew(1 bit) + V_ew (10 bits) + Sign_ns (1 bit) + V_ns (10 bits) + // Vertical rate source bit VrSrc (ME bit 36) indicates source of the altitude measurements. GNSS altitude(0) / , barometric altitude(1). + // Vertical rate source direction,(ME bit 37) movement can be read from Svr bit , with 0 and 1 referring to climb and descent, respectively (ft/min) // The encoded vertical rate value VR can be computed using message (ME bits 38 to 46). If the 9-bit block contains all zeros, the vertical rate information is not available. - // + Sign VrSrc (vert rate src) (1 bit)+ VrSrc (9 bits). - frame.push_byte(((velo_ew < 0 ? 1 : 0) << 2) | (velo_ew_abs >> 8)); - frame.push_byte(velo_ew_abs); - frame.push_byte(((velo_ns < 0 ? 1 : 0) << 7) | (velo_ns_abs >> 3)); - frame.push_byte((velo_ns_abs << 5) | ((v_rate < 0 ? 1 : 0) << 3) | (v_rate_coded_abs >> 6)); // VrSrc = 0 - frame.push_byte(v_rate_coded_abs << 2); - frame.push_byte(0); - - frame.make_CRC(); + // + Sign VrSrc (vert rate src) (1 bit)+ VrSrc (9 bits). + frame.push_byte(((velo_ew < 0 ? 1 : 0) << 2) | (velo_ew_abs >> 8)); + frame.push_byte(velo_ew_abs); + frame.push_byte(((velo_ns < 0 ? 1 : 0) << 7) | (velo_ns_abs >> 3)); + frame.push_byte((velo_ns_abs << 5) | ((v_rate < 0 ? 1 : 0) << 3) | (v_rate_coded_abs >> 6)); // VrSrc = 0 + frame.push_byte(v_rate_coded_abs << 2); + frame.push_byte(0); + + frame.make_CRC(); } // Decoding method from dump1090 -adsb_vel decode_frame_velo(ADSBFrame& frame){ - adsb_vel velo {false, 0, 0, 0}; +adsb_vel decode_frame_velo(ADSBFrame& frame) { + adsb_vel velo{false, 0, 0, 0}; + + uint8_t* frame_data = frame.get_raw_data(); + uint8_t velo_type = frame.get_msg_sub(); - uint8_t * frame_data = frame.get_raw_data(); - uint8_t velo_type = frame.get_msg_sub(); + if (velo_type >= 1 && velo_type <= 4) { // vertical rate is always present - if(velo_type >= 1 && velo_type <= 4){ //vertical rate is always present + velo.v_rate = (((frame_data[8] & 0x07) << 6) | ((frame_data[9] >> 2) - 1)) * 64; - velo.v_rate = (((frame_data[8] & 0x07 ) << 6) | ((frame_data[9] >> 2) - 1)) * 64; + if ((frame_data[8] & 0x8) >> 3) velo.v_rate *= -1; // check v_rate sign + } - if((frame_data[8] & 0x8) >> 3) velo.v_rate *= -1; //check v_rate sign - } + if (velo_type == 1 || velo_type == 2) { // Ground Speed + int32_t raw_ew = ((frame_data[5] & 0x03) << 8) | frame_data[6]; + int32_t velo_ew = raw_ew - 1; // velocities are all offset by one (this is part of the spec) - if(velo_type == 1 || velo_type == 2){ //Ground Speed - int32_t raw_ew = ((frame_data[5] & 0x03) << 8) | frame_data[6]; - int32_t velo_ew = raw_ew - 1; //velocities are all offset by one (this is part of the spec) + int32_t raw_ns = ((frame_data[7] & 0x7f) << 3) | (frame_data[8] >> 5); + int32_t velo_ns = raw_ns - 1; - int32_t raw_ns = ((frame_data[7] & 0x7f) << 3) | (frame_data[8] >> 5); - int32_t velo_ns = raw_ns - 1; + if (velo_type == 2) { // supersonic indicator so multiply by 4 + velo_ew = velo_ew << 2; + velo_ns = velo_ns << 2; + } - if (velo_type == 2){ // supersonic indicator so multiply by 4 - velo_ew = velo_ew << 2; - velo_ns = velo_ns << 2; - } + if (frame_data[5] & 0x04) velo_ew *= -1; // check ew direction sign + if (frame_data[7] & 0x80) velo_ns *= -1; // check ns direction sign - if(frame_data[5]&0x04) velo_ew *= -1; //check ew direction sign - if(frame_data[7]&0x80) velo_ns *= -1; //check ns direction sign + velo.speed = fast_int_magnitude(velo_ns, velo_ew); - velo.speed = fast_int_magnitude(velo_ns,velo_ew); - - if(velo.speed){ - //calculate heading in degrees from ew/ns velocities - int16_t heading_temp = (int16_t)(int_atan2(velo_ew,velo_ns)); // Nearest degree - // We don't want negative values but a 0-360 scale. - if (heading_temp < 0) heading_temp += 360.0; - velo.heading = (uint16_t)heading_temp; - } - - }else if(velo_type == 3 || velo_type == 4){ //Airspeed - velo.valid = frame_data[5] & (1<<2); - velo.heading = ((((frame_data[5] & 0x03)<<8) | frame_data[6]) * 45) << 7; - } + if (velo.speed) { + // calculate heading in degrees from ew/ns velocities + int16_t heading_temp = (int16_t)(int_atan2(velo_ew, velo_ns)); // Nearest degree + // We don't want negative values but a 0-360 scale. + if (heading_temp < 0) heading_temp += 360.0; + velo.heading = (uint16_t)heading_temp; + } - return velo; + } else if (velo_type == 3 || velo_type == 4) { // Airspeed + velo.valid = frame_data[5] & (1 << 2); + velo.heading = ((((frame_data[5] & 0x03) << 8) | frame_data[6]) * 45) << 7; + } + return velo; } } /* namespace adsb */ diff --git a/firmware/common/adsb.hpp b/firmware/common/adsb.hpp index 1501e993f..dc98d417f 100644 --- a/firmware/common/adsb.hpp +++ b/firmware/common/adsb.hpp @@ -32,57 +32,56 @@ namespace adsb { enum downlink_format { - DF_ADSB = 17, - DF_EHS_SQUAWK = 21, // DF 21: Comm-B with identity reply . Mode S enhanced surveillance of squawk + (MB_field = Track and turn report (BDS 5,0)). - // Confirmed that it is Detected correctly by dump1090. and sdrangel. + DF_ADSB = 17, + DF_EHS_SQUAWK = 21, // DF 21: Comm-B with identity reply . Mode S enhanced surveillance of squawk + (MB_field = Track and turn report (BDS 5,0)). + // Confirmed that it is Detected correctly by dump1090. and sdrangel. }; enum type_code { - TC_IDENT = 4, - TC_AIRBORNE_POS = 11, - TC_AIRBORNE_VELO = 19 + TC_IDENT = 4, + TC_AIRBORNE_POS = 11, + TC_AIRBORNE_VELO = 19 }; enum data_selector { - BDS_ID = 0x20, - BDS_ID_MARKS = 0x21, - BDS_INTENT = 0x40, - BDS_HEADING = 0x60 + BDS_ID = 0x20, + BDS_ID_MARKS = 0x21, + BDS_INTENT = 0x40, + BDS_HEADING = 0x60 }; struct adsb_pos { - bool valid; - float latitude; - float longitude; - int32_t altitude; + bool valid; + float latitude; + float longitude; + int32_t altitude; }; struct adsb_vel { - bool valid; - int32_t speed; //knot - uint16_t heading; //degree - int32_t v_rate; //ft/min + bool valid; + int32_t speed; // knot + uint16_t heading; // degree + int32_t v_rate; // ft/min }; const float CPR_MAX_VALUE = 131072.0; const float adsb_lat_lut[58] = { - 10.47047130, 14.82817437, 18.18626357, 21.02939493, - 23.54504487, 25.82924707, 27.93898710, 29.91135686, - 31.77209708, 33.53993436, 35.22899598, 36.85025108, - 38.41241892, 39.92256684, 41.38651832, 42.80914012, - 44.19454951, 45.54626723, 46.86733252, 48.16039128, - 49.42776439, 50.67150166, 51.89342469, 53.09516153, - 54.27817472, 55.44378444, 56.59318756, 57.72747354, - 58.84763776, 59.95459277, 61.04917774, 62.13216659, - 63.20427479, 64.26616523, 65.31845310, 66.36171008, - 67.39646774, 68.42322022, 69.44242631, 70.45451075, - 71.45986473, 72.45884545, 73.45177442, 74.43893416, - 75.42056257, 76.39684391, 77.36789461, 78.33374083, - 79.29428225, 80.24923213, 81.19801349, 82.13956981, - 83.07199445, 83.99173563, 84.89166191, 85.75541621, - 86.53536998, 87.00000000 -}; + 10.47047130, 14.82817437, 18.18626357, 21.02939493, + 23.54504487, 25.82924707, 27.93898710, 29.91135686, + 31.77209708, 33.53993436, 35.22899598, 36.85025108, + 38.41241892, 39.92256684, 41.38651832, 42.80914012, + 44.19454951, 45.54626723, 46.86733252, 48.16039128, + 49.42776439, 50.67150166, 51.89342469, 53.09516153, + 54.27817472, 55.44378444, 56.59318756, 57.72747354, + 58.84763776, 59.95459277, 61.04917774, 62.13216659, + 63.20427479, 64.26616523, 65.31845310, 66.36171008, + 67.39646774, 68.42322022, 69.44242631, 70.45451075, + 71.45986473, 72.45884545, 73.45177442, 74.43893416, + 75.42056257, 76.39684391, 77.36789461, 78.33374083, + 79.29428225, 80.24923213, 81.19801349, 82.13956981, + 83.07199445, 83.99173563, 84.89166191, 85.75541621, + 86.53536998, 87.00000000}; const float PI = 3.14159265358979323846; @@ -93,20 +92,18 @@ void make_frame_adsb(ADSBFrame& frame, const uint32_t ICAO_address); void encode_frame_id(ADSBFrame& frame, const uint32_t ICAO_address, const std::string& callsign); std::string decode_frame_id(ADSBFrame& frame); -void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, - const float latitude, const float longitude, const uint32_t time_parity); +void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, const float latitude, const float longitude, const uint32_t time_parity); adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd); -void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, - const float angle, const int32_t v_rate); +void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, const float angle, const int32_t v_rate); adsb_vel decode_frame_velo(ADSBFrame& frame); -//void encode_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code); +// void encode_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code); void encode_frame_squawk(ADSBFrame& frame, const uint16_t squawk); } /* namespace adsb */ -#endif/*__ADSB_H__*/ +#endif /*__ADSB_H__*/ diff --git a/firmware/common/adsb_frame.hpp b/firmware/common/adsb_frame.hpp index 6e5e70964..7bdfb1fb2 100644 --- a/firmware/common/adsb_frame.hpp +++ b/firmware/common/adsb_frame.hpp @@ -19,7 +19,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ - + #ifndef __ADSB_FRAME_H__ #define __ADSB_FRAME_H__ @@ -28,104 +28,104 @@ namespace adsb { -alignas(4) const uint8_t adsb_preamble[16] = { 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0 }; +alignas(4) const uint8_t adsb_preamble[16] = {1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0}; alignas(4) const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"; class ADSBFrame { -public: - uint8_t get_DF() { - return (raw_data[0] >> 3); - } - - uint8_t get_msg_type() { - return (raw_data[4] >> 3); - } - - uint8_t get_msg_sub() { - return (raw_data[4] & 7); - } - - uint32_t get_ICAO_address() { - return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3]; - } - - void set_rx_timestamp(uint32_t timestamp) { - rx_timestamp = timestamp; - } - uint32_t get_rx_timestamp() { - return rx_timestamp; - } - - void clear() { - index = 0; - memset(raw_data, 0, 14); - } - - void push_byte(uint8_t byte) { - if (index >= 14) - return; - - raw_data[index++] = byte; - } - - uint8_t * get_raw_data() const { - return (uint8_t* )raw_data; - } - - void make_CRC() { - uint32_t computed_CRC = compute_CRC(); - - // Insert CRC in frame - raw_data[11] = (computed_CRC >> 16) & 0xFF; - raw_data[12] = (computed_CRC >> 8) & 0xFF; - raw_data[13] = computed_CRC & 0xFF; - } - - bool check_CRC() { - uint32_t computed_CRC = compute_CRC(); - - if ((raw_data[11] != ((computed_CRC >> 16) & 0xFF)) || - (raw_data[12] != ((computed_CRC >> 8) & 0xFF)) || - (raw_data[13] != (computed_CRC & 0xFF))) return false; - - return true; - } - - bool empty() { - return (index == 0); - } - -private: - static const uint8_t adsb_preamble[16]; - static const char icao_id_lut[65]; - alignas(4) uint8_t index { 0 }; - alignas(4) uint8_t raw_data[14] { }; // 112 bits at most - uint32_t rx_timestamp { }; - - uint32_t compute_CRC() { - uint8_t adsb_crc[14] = { 0 }; // Temp buffer - uint8_t b, c, s, bitn; - const uint32_t crc_poly = 0x1205FFF; - - // Copy frame data - memcpy(adsb_crc, raw_data, 11); - - // Compute CRC - for (c = 0; c < 11; c++) { - for (b = 0; b < 8; b++) { - if ((adsb_crc[c] << b) & 0x80) { - for (s = 0; s < 25; s++) { - bitn = (c * 8) + b + s; - if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7)); - } - } - } - } - - return (adsb_crc[11] << 16) + (adsb_crc[12] << 8) + adsb_crc[13]; - } + public: + uint8_t get_DF() { + return (raw_data[0] >> 3); + } + + uint8_t get_msg_type() { + return (raw_data[4] >> 3); + } + + uint8_t get_msg_sub() { + return (raw_data[4] & 7); + } + + uint32_t get_ICAO_address() { + return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3]; + } + + void set_rx_timestamp(uint32_t timestamp) { + rx_timestamp = timestamp; + } + uint32_t get_rx_timestamp() { + return rx_timestamp; + } + + void clear() { + index = 0; + memset(raw_data, 0, 14); + } + + void push_byte(uint8_t byte) { + if (index >= 14) + return; + + raw_data[index++] = byte; + } + + uint8_t* get_raw_data() const { + return (uint8_t*)raw_data; + } + + void make_CRC() { + uint32_t computed_CRC = compute_CRC(); + + // Insert CRC in frame + raw_data[11] = (computed_CRC >> 16) & 0xFF; + raw_data[12] = (computed_CRC >> 8) & 0xFF; + raw_data[13] = computed_CRC & 0xFF; + } + + bool check_CRC() { + uint32_t computed_CRC = compute_CRC(); + + if ((raw_data[11] != ((computed_CRC >> 16) & 0xFF)) || + (raw_data[12] != ((computed_CRC >> 8) & 0xFF)) || + (raw_data[13] != (computed_CRC & 0xFF))) return false; + + return true; + } + + bool empty() { + return (index == 0); + } + + private: + static const uint8_t adsb_preamble[16]; + static const char icao_id_lut[65]; + alignas(4) uint8_t index{0}; + alignas(4) uint8_t raw_data[14]{}; // 112 bits at most + uint32_t rx_timestamp{}; + + uint32_t compute_CRC() { + uint8_t adsb_crc[14] = {0}; // Temp buffer + uint8_t b, c, s, bitn; + const uint32_t crc_poly = 0x1205FFF; + + // Copy frame data + memcpy(adsb_crc, raw_data, 11); + + // Compute CRC + for (c = 0; c < 11; c++) { + for (b = 0; b < 8; b++) { + if ((adsb_crc[c] << b) & 0x80) { + for (s = 0; s < 25; s++) { + bitn = (c * 8) + b + s; + if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7)); + } + } + } + } + + return (adsb_crc[11] << 16) + (adsb_crc[12] << 8) + adsb_crc[13]; + } }; } /* namespace adsb */ -#endif/*__ADSB_FRAME_H__*/ +#endif /*__ADSB_FRAME_H__*/ diff --git a/firmware/common/ais_baseband.hpp b/firmware/common/ais_baseband.hpp index f8bbc0d89..85e052400 100644 --- a/firmware/common/ais_baseband.hpp +++ b/firmware/common/ais_baseband.hpp @@ -36,12 +36,14 @@ namespace ais { // sample=38.4k, deviation=2400, symbol=9600 // Length: 4 taps, 1 symbol, 1/4 cycle of sinusoid // Gain: 1.0 (sinusoid / len(taps)) -constexpr std::array, 4> square_taps_38k4_1t_p { { - { 0.25000000f, 0.00000000f }, { 0.23096988f, 0.09567086f }, - { 0.17677670f, 0.17677670f }, { 0.09567086f, 0.23096988f }, -} }; +constexpr std::array, 4> square_taps_38k4_1t_p{{ + {0.25000000f, 0.00000000f}, + {0.23096988f, 0.09567086f}, + {0.17677670f, 0.17677670f}, + {0.09567086f, 0.23096988f}, +}}; } /* namespace ais */ } /* namespace baseband */ -#endif/*__AIS_BASEBAND_H__*/ +#endif /*__AIS_BASEBAND_H__*/ diff --git a/firmware/common/ais_packet.cpp b/firmware/common/ais_packet.cpp index c6e7561f5..0446596aa 100644 --- a/firmware/common/ais_packet.cpp +++ b/firmware/common/ais_packet.cpp @@ -28,193 +28,190 @@ namespace ais { struct PacketLengthRange { - constexpr PacketLengthRange( - ) : min_bytes { 0 }, - max_bytes { 0 } - { - } - - constexpr PacketLengthRange( - const uint16_t min_bits, - const uint16_t max_bits - ) : min_bytes { static_cast(min_bits / 8U) }, - max_bytes { static_cast(max_bits / 8U) } - { - // static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8"); - // static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8"); - } - - constexpr bool contains(const size_t bit_count) const { - return !is_above(bit_count) && !is_below(bit_count); - } - - constexpr bool is_above(const size_t bit_count) const { - return (min() > bit_count); - } - - constexpr bool is_below(const size_t bit_count) const { - return (max() < bit_count); - } - - constexpr size_t min() const { - return min_bytes * 8; - } - - constexpr size_t max() const { - return max_bytes * 8; - } - -private: - const uint8_t min_bytes; - const uint8_t max_bytes; + constexpr PacketLengthRange() + : min_bytes{0}, + max_bytes{0} { + } + + constexpr PacketLengthRange( + const uint16_t min_bits, + const uint16_t max_bits) + : min_bytes{static_cast(min_bits / 8U)}, + max_bytes{static_cast(max_bits / 8U)} { + // static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8"); + // static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8"); + } + + constexpr bool contains(const size_t bit_count) const { + return !is_above(bit_count) && !is_below(bit_count); + } + + constexpr bool is_above(const size_t bit_count) const { + return (min() > bit_count); + } + + constexpr bool is_below(const size_t bit_count) const { + return (max() < bit_count); + } + + constexpr size_t min() const { + return min_bytes * 8; + } + + constexpr size_t max() const { + return max_bytes * 8; + } + + private: + const uint8_t min_bytes; + const uint8_t max_bytes; }; -static constexpr std::array packet_length_range { { - { 0, 0 }, // 0 - { 168, 168 }, // 1 - { 168, 168 }, // 2 - { 168, 168 }, // 3 - { 168, 168 }, // 4 - { 424, 424 }, // 5 - { 0, 0 }, // 6 - { 0, 0 }, // 7 - { 0, 1008 }, // 8 - { 0, 0 }, // 9 - { 0, 0 }, // 10 - { 0, 0 }, // 11 - { 0, 0 }, // 12 - { 0, 0 }, // 13 - { 0, 0 }, // 14 - { 0, 0 }, // 15 - { 0, 0 }, // 16 - { 0, 0 }, // 17 - { 168, 168 }, // 18 - { 0, 0 }, // 19 - { 72, 160 }, // 20 - { 272, 360 }, // 21 - { 168, 168 }, // 22 - { 160, 160 }, // 23 - { 160, 168 }, // 24 - { 0, 168 }, // 25 - { 0, 0 }, // 26 - { 0, 0 }, // 27 - { 0, 0 }, // 28 - { 0, 0 }, // 29 - { 0, 0 }, // 30 - { 0, 0 }, // 31 -} }; +static constexpr std::array packet_length_range{{ + {0, 0}, // 0 + {168, 168}, // 1 + {168, 168}, // 2 + {168, 168}, // 3 + {168, 168}, // 4 + {424, 424}, // 5 + {0, 0}, // 6 + {0, 0}, // 7 + {0, 1008}, // 8 + {0, 0}, // 9 + {0, 0}, // 10 + {0, 0}, // 11 + {0, 0}, // 12 + {0, 0}, // 13 + {0, 0}, // 14 + {0, 0}, // 15 + {0, 0}, // 16 + {0, 0}, // 17 + {168, 168}, // 18 + {0, 0}, // 19 + {72, 160}, // 20 + {272, 360}, // 21 + {168, 168}, // 22 + {160, 160}, // 23 + {160, 168}, // 24 + {0, 168}, // 25 + {0, 0}, // 26 + {0, 0}, // 27 + {0, 0}, // 28 + {0, 0}, // 29 + {0, 0}, // 30 + {0, 0}, // 31 +}}; struct PacketLengthValidator { - constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { - return packet_length_range[message_id].contains(length); - } + constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { + return packet_length_range[message_id].contains(length); + } }; struct PacketTooLong { - constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { - return packet_length_range[message_id].is_below(length); - } + constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { + return packet_length_range[message_id].is_below(length); + } }; static constexpr char char_to_ascii(const uint8_t c) { - return (c ^ 32) + 32; + return (c ^ 32) + 32; } size_t Packet::length() const { - return packet_.size(); + return packet_.size(); } bool Packet::is_valid() const { - return length_valid() && crc_ok(); + return length_valid() && crc_ok(); } Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } uint32_t Packet::message_id() const { - return field_.read(0, 6); + return field_.read(0, 6); } MMSI Packet::user_id() const { - return field_.read(8, 30); + return field_.read(8, 30); } MMSI Packet::source_id() const { - return field_.read(8, 30); + return field_.read(8, 30); } uint32_t Packet::read(const size_t start_bit, const size_t length) const { - return field_.read(start_bit, length); + return field_.read(start_bit, length); } std::string Packet::text( - const size_t start_bit, - const size_t character_count -) const { - std::string result; - result.reserve(character_count); - - const size_t character_length = 6; - const size_t end_bit = start_bit + character_count * character_length; - for(size_t i=start_bit; i(field_.read(start_bit + 0, 14)), - static_cast(field_.read(start_bit + 14, 4)), - static_cast(field_.read(start_bit + 18, 5)), - static_cast(field_.read(start_bit + 23, 5)), - static_cast(field_.read(start_bit + 28, 6)), - static_cast(field_.read(start_bit + 34, 6)), - }; + return { + static_cast(field_.read(start_bit + 0, 14)), + static_cast(field_.read(start_bit + 14, 4)), + static_cast(field_.read(start_bit + 18, 5)), + static_cast(field_.read(start_bit + 23, 5)), + static_cast(field_.read(start_bit + 28, 6)), + static_cast(field_.read(start_bit + 34, 6)), + }; } Latitude Packet::latitude(const size_t start_bit) const { - return field_.read(start_bit, 27); + return field_.read(start_bit, 27); } Longitude Packet::longitude(const size_t start_bit) const { - return field_.read(start_bit, 28); + return field_.read(start_bit, 28); } bool Packet::crc_ok() const { - CRCReader field_crc { packet_ }; - CRC<16> ais_fcs { 0x1021, 0xffff, 0xffff }; - - for(size_t i=0; i ais_fcs{0x1021, 0xffff, 0xffff}; + + for (size_t i = 0; i < data_length(); i += 8) { + ais_fcs.process_byte(field_crc.read(i, 8)); + } - return (ais_fcs.checksum() == (unsigned)field_crc.read(data_length(), fcs_length)); + return (ais_fcs.checksum() == (unsigned)field_crc.read(data_length(), fcs_length)); } size_t Packet::data_and_fcs_length() const { - // Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag). - return length() - 7; + // Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag). + return length() - 7; } size_t Packet::data_length() const { - return data_and_fcs_length() - fcs_length; + return data_and_fcs_length() - fcs_length; } bool Packet::length_valid() const { - const size_t extra_bits = data_and_fcs_length() & 7; - if( extra_bits != 0 ) { - return false; - } + const size_t extra_bits = data_and_fcs_length() & 7; + if (extra_bits != 0) { + return false; + } - const PacketLengthValidator packet_length_valid; - if( !packet_length_valid(message_id(), data_length()) ) { - return false; - } + const PacketLengthValidator packet_length_valid; + if (!packet_length_valid(message_id(), data_length())) { + return false; + } - return true; + return true; } } /* namespace ais */ diff --git a/firmware/common/ais_packet.hpp b/firmware/common/ais_packet.hpp index eabd88d2d..8d2630516 100644 --- a/firmware/common/ais_packet.hpp +++ b/firmware/common/ais_packet.hpp @@ -32,60 +32,56 @@ namespace ais { struct DateTime { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; }; -template +template struct LatLonBase { - - constexpr LatLonBase( - ) : LatLonBase { raw_not_available } - { - } - - constexpr LatLonBase( - const int32_t raw - ) : raw_ { raw } - { - } - - constexpr LatLonBase( - const LatLonBase& other - ) : raw_ { other.raw_ } - { - } - - LatLonBase& operator=( const LatLonBase &)=default; - - int32_t normalized() const { - return static_cast(raw() << sign_extend_shift) / (1 << sign_extend_shift); - } - - int32_t raw() const { - return raw_; - } - - bool is_not_available() const { - return raw() == raw_not_available; - } - - bool is_valid() const { - return (normalized() >= raw_valid_min) && (normalized() <= raw_valid_max); - } - -private: - int32_t raw_; - - static constexpr size_t sign_extend_shift = 32 - FieldSize; - - static constexpr int32_t raw_not_available = NAValue; - static constexpr int32_t raw_valid_min = -DegMax * 60 * 10000; - static constexpr int32_t raw_valid_max = DegMax * 60 * 10000; + constexpr LatLonBase() + : LatLonBase{raw_not_available} { + } + + constexpr LatLonBase( + const int32_t raw) + : raw_{raw} { + } + + constexpr LatLonBase( + const LatLonBase& other) + : raw_{other.raw_} { + } + + LatLonBase& operator=(const LatLonBase&) = default; + + int32_t normalized() const { + return static_cast(raw() << sign_extend_shift) / (1 << sign_extend_shift); + } + + int32_t raw() const { + return raw_; + } + + bool is_not_available() const { + return raw() == raw_not_available; + } + + bool is_valid() const { + return (normalized() >= raw_valid_min) && (normalized() <= raw_valid_max); + } + + private: + int32_t raw_; + + static constexpr size_t sign_extend_shift = 32 - FieldSize; + + static constexpr int32_t raw_not_available = NAValue; + static constexpr int32_t raw_valid_min = -DegMax * 60 * 10000; + static constexpr int32_t raw_valid_max = DegMax * 60 * 10000; }; using Latitude = LatLonBase<27, 90, 0x3412140>; @@ -99,50 +95,49 @@ using TrueHeading = uint16_t; using MMSI = uint32_t; class Packet { -public: - constexpr Packet( - const baseband::Packet& packet - ) : packet_ { packet }, - field_ { packet_ } - { - } + public: + constexpr Packet( + const baseband::Packet& packet) + : packet_{packet}, + field_{packet_} { + } + + size_t length() const; + + bool is_valid() const; - size_t length() const; - - bool is_valid() const; + Timestamp received_at() const; - Timestamp received_at() const; + uint32_t message_id() const; + MMSI user_id() const; + MMSI source_id() const; - uint32_t message_id() const; - MMSI user_id() const; - MMSI source_id() const; + uint32_t read(const size_t start_bit, const size_t length) const; - uint32_t read(const size_t start_bit, const size_t length) const; + std::string text(const size_t start_bit, const size_t character_count) const; - std::string text(const size_t start_bit, const size_t character_count) const; + DateTime datetime(const size_t start_bit) const; - DateTime datetime(const size_t start_bit) const; + Latitude latitude(const size_t start_bit) const; + Longitude longitude(const size_t start_bit) const; - Latitude latitude(const size_t start_bit) const; - Longitude longitude(const size_t start_bit) const; + bool crc_ok() const; - bool crc_ok() const; + private: + using Reader = FieldReader; + using CRCReader = FieldReader; -private: - using Reader = FieldReader; - using CRCReader = FieldReader; - - const baseband::Packet packet_; - const Reader field_; + const baseband::Packet packet_; + const Reader field_; - const size_t fcs_length = 16; + const size_t fcs_length = 16; - size_t data_and_fcs_length() const; - size_t data_length() const; + size_t data_and_fcs_length() const; + size_t data_length() const; - bool length_valid() const; + bool length_valid() const; }; } /* namespace ais */ -#endif/*__AIS_PACKET_H__*/ +#endif /*__AIS_PACKET_H__*/ diff --git a/firmware/common/ak4951.cpp b/firmware/common/ak4951.cpp index 1f88a3d6e..f96ae8755 100644 --- a/firmware/common/ak4951.cpp +++ b/firmware/common/ak4951.cpp @@ -30,550 +30,543 @@ namespace asahi_kasei { namespace ak4951 { void AK4951::configure_digital_interface_i2s() { - // Configure for external slave mode. - map.r.mode_control_1.DIF = 0b11; // I2S compatible - map.r.mode_control_1.BCKO = 0; // BICK = 32fs - update(Register::ModeControl1); - - map.r.mode_control_2.CM = 0b00; // MCKI = 256fs - map.r.mode_control_2.FS = 0b1011; // fs = 48kHz - update(Register::ModeControl2); + // Configure for external slave mode. + map.r.mode_control_1.DIF = 0b11; // I2S compatible + map.r.mode_control_1.BCKO = 0; // BICK = 32fs + update(Register::ModeControl1); + + map.r.mode_control_2.CM = 0b00; // MCKI = 256fs + map.r.mode_control_2.FS = 0b1011; // fs = 48kHz + update(Register::ModeControl2); } void AK4951::configure_digital_interface_external_slave() { - map.r.power_management_2.MS = 0; // Slave mode - map.r.power_management_2.PMPLL = 0; // EXT mode - update(Register::PowerManagement2); + map.r.power_management_2.MS = 0; // Slave mode + map.r.power_management_2.PMPLL = 0; // EXT mode + update(Register::PowerManagement2); } void AK4951::configure_digital_interface_external_master() { - map.r.power_management_2.MS = 1; // Master mode - map.r.power_management_2.PMPLL = 0; // EXT mode - update(Register::PowerManagement2); + map.r.power_management_2.MS = 1; // Master mode + map.r.power_management_2.PMPLL = 0; // EXT mode + update(Register::PowerManagement2); } void AK4951::init() { - reset(); + reset(); - // Write dummy address to "release" the reset. - write(0x00, 0x00); + // Write dummy address to "release" the reset. + write(0x00, 0x00); - configure_digital_interface_i2s(); - configure_digital_interface_external_slave(); + configure_digital_interface_i2s(); + configure_digital_interface_external_slave(); - map.r.power_management_1.PMVCM = 1; - update(Register::PowerManagement1); + map.r.power_management_1.PMVCM = 1; + update(Register::PowerManagement1); - // Headphone output is hi-Z when not active, reduces crosstalk from speaker output. - map.r.beep_control.HPZ = 1; - update(Register::BeepControl); + // Headphone output is hi-Z when not active, reduces crosstalk from speaker output. + map.r.beep_control.HPZ = 1; + update(Register::BeepControl); - // Pause for VCOM and REGFIL pins to stabilize. - chThdSleepMilliseconds(2); + // Pause for VCOM and REGFIL pins to stabilize. + chThdSleepMilliseconds(2); - headphone_mute(); + headphone_mute(); - // SPK-Amp gain setting: SPKG1-0 bits = “00†→ “01†- map.r.signal_select_2.SPKG = 0b01; - update(Register::SignalSelect2); + // SPK-Amp gain setting: SPKG1-0 bits = “00†→ “01†+ map.r.signal_select_2.SPKG = 0b01; + update(Register::SignalSelect2); - map.r.signal_select_3.MONO = 0b00; - update(Register::SignalSelect3); + map.r.signal_select_3.MONO = 0b00; + update(Register::SignalSelect3); - map.r.digital_filter_mode.PFSDO = 0; // ADC bypass digital filter block. - map.r.digital_filter_mode.ADCPF = 1; // ADC output - map.r.digital_filter_mode.PFDAC = 0b00; // SDTI - update(Register::DigitalFilterMode); + map.r.digital_filter_mode.PFSDO = 0; // ADC bypass digital filter block. + map.r.digital_filter_mode.ADCPF = 1; // ADC output + map.r.digital_filter_mode.PFDAC = 0b00; // SDTI + update(Register::DigitalFilterMode); - // Set up FRN, FRATT and ADRST1-0 bits (Addr = 09H) - // map.r.timer_select.FRN = 0; - // map.r.timer_select.FRATT = 0; - // map.r.timer_select.ADRST = 0b00; - // update(Register::TimerSelect); + // Set up FRN, FRATT and ADRST1-0 bits (Addr = 09H) + // map.r.timer_select.FRN = 0; + // map.r.timer_select.FRATT = 0; + // map.r.timer_select.ADRST = 0b00; + // update(Register::TimerSelect); - // Set up ALC mode (Addr = 0AH, 0BH) - // map.r.alc_timer_select. = ; - // update(Register::ALCTimerSelect); - // map.r.alc_mode_control_1. = ; - // update(Register::ALCModeControl1); + // Set up ALC mode (Addr = 0AH, 0BH) + // map.r.alc_timer_select. = ; + // update(Register::ALCTimerSelect); + // map.r.alc_mode_control_1. = ; + // update(Register::ALCModeControl1); - // Set up REF value of ALC (Addr = 0CH) - // map.r.alc_mode_control_2. = ; - // update(Register::ALCModeControl2); + // Set up REF value of ALC (Addr = 0CH) + // map.r.alc_mode_control_2. = ; + // update(Register::ALCModeControl2); - // Set up IVOL value of ALC operation start (Addr = 0DH) - // map.r.l_ch_input_volume_control. = ; - // update(Register::LchInputVolumeControl); - // map.r.r_ch_input_volume_control. = ; - // update(Register::RchInputVolumeControl); + // Set up IVOL value of ALC operation start (Addr = 0DH) + // map.r.l_ch_input_volume_control. = ; + // update(Register::LchInputVolumeControl); + // map.r.r_ch_input_volume_control. = ; + // update(Register::RchInputVolumeControl); - // Set up the output digital volume. (Addr = 13H) - // set_headphone_volume(...); + // Set up the output digital volume. (Addr = 13H) + // set_headphone_volume(...); - // Set up Programmable Filter Path: PFDAC1-0 bits=“01â€, PFSDO=ADCPF bits=“0†(Addr = 1DH) - // map.r.digital_filter_mode.PFDAC = 0b01; - // update(Register::DigitalFilterMode); + // Set up Programmable Filter Path: PFDAC1-0 bits=“01â€, PFSDO=ADCPF bits=“0†(Addr = 1DH) + // map.r.digital_filter_mode.PFDAC = 0b01; + // update(Register::DigitalFilterMode); } bool AK4951::detected() { - return reset(); + return reset(); } bool AK4951::reset() { - io.audio_reset_state(true); + io.audio_reset_state(true); - // PDN# pulse must be >200ns - chThdSleepMicroseconds(10); + // PDN# pulse must be >200ns + chThdSleepMicroseconds(10); - io.audio_reset_state(false); + io.audio_reset_state(false); - return true; + return true; } void AK4951::set_digtal_volume_control(const reg_t value) { - map.r.l_ch_digital_volume_control.DV = value; - update(Register::LchDigitalVolumeControl); + map.r.l_ch_digital_volume_control.DV = value; + update(Register::LchDigitalVolumeControl); } void AK4951::set_headphone_volume(const volume_t volume) { - const auto normalized = headphone_gain_range().normalize(volume); - auto n = normalized.centibel() / 5; - set_digtal_volume_control(0xcb - n); + const auto normalized = headphone_gain_range().normalize(volume); + auto n = normalized.centibel() / 5; + set_digtal_volume_control(0xcb - n); } void AK4951::headphone_mute() { - set_digtal_volume_control(0xff); + set_digtal_volume_control(0xff); } void AK4951::set_dac_power(const bool enable) { - map.r.power_management_1.PMDAC = enable; - update(Register::PowerManagement1); + map.r.power_management_1.PMDAC = enable; + update(Register::PowerManagement1); } void AK4951::set_headphone_power(const bool enable) { - map.r.power_management_2.PMHPL = map.r.power_management_2.PMHPR = enable; - update(Register::PowerManagement2); + map.r.power_management_2.PMHPL = map.r.power_management_2.PMHPR = enable; + update(Register::PowerManagement2); } void AK4951::set_speaker_power(const bool enable) { - map.r.power_management_2.PMSL = enable; - update(Register::PowerManagement2); + map.r.power_management_2.PMSL = enable; + update(Register::PowerManagement2); } void AK4951::select_line_out(const LineOutSelect value) { - map.r.power_management_2.LOSEL = (value == LineOutSelect::Line) ? 1 : 0; - update(Register::PowerManagement2); + map.r.power_management_2.LOSEL = (value == LineOutSelect::Line) ? 1 : 0; + update(Register::PowerManagement2); } void AK4951::headphone_enable() { - set_dac_power(true); - set_headphone_power(true); + set_dac_power(true); + set_headphone_power(true); - // Wait for headphone amplifier charge pump power-up. - chThdSleepMilliseconds(35); + // Wait for headphone amplifier charge pump power-up. + chThdSleepMilliseconds(35); } void AK4951::headphone_disable() { - set_headphone_power(false); - set_dac_power(false); + set_headphone_power(false); + set_dac_power(false); } void AK4951::speaker_enable() { - // Set up the path of DAC → SPK-Amp: DACS bit = “0†→ “1†- map.r.signal_select_1.DACS = 1; - update(Register::SignalSelect1); + // Set up the path of DAC → SPK-Amp: DACS bit = “0†→ “1†+ map.r.signal_select_1.DACS = 1; + update(Register::SignalSelect1); - // Enter Speaker-Amp Output Mode: LOSEL bit = “0†- select_line_out(LineOutSelect::Speaker); + // Enter Speaker-Amp Output Mode: LOSEL bit = “0†+ select_line_out(LineOutSelect::Speaker); - // Power up DAC, Programmable Filter and Speaker-Amp: PMDAC=PMPFIL=PMSL bits=“0â€â†’“1†- set_dac_power(true); - // map.r.power_management_1.PMPFIL = 1; - // update(Register::PowerManagement1); - set_speaker_power(true); + // Power up DAC, Programmable Filter and Speaker-Amp: PMDAC=PMPFIL=PMSL bits=“0â€â†’“1†+ set_dac_power(true); + // map.r.power_management_1.PMPFIL = 1; + // update(Register::PowerManagement1); + set_speaker_power(true); - // Time from PMSL=1 to SLPSN=1. - chThdSleepMilliseconds(1); + // Time from PMSL=1 to SLPSN=1. + chThdSleepMilliseconds(1); - // Exit the power-save mode of Speaker-Amp: SLPSN bit = “0†→ “1†- map.r.signal_select_1.SLPSN = 1; - update(Register::SignalSelect1); + // Exit the power-save mode of Speaker-Amp: SLPSN bit = “0†→ “1†+ map.r.signal_select_1.SLPSN = 1; + update(Register::SignalSelect1); } void AK4951::speaker_disable() { - // Enter Speaker-Amp Power Save Mode: SLPSN bit = “1†→ “0†- map.r.signal_select_1.SLPSN = 0; - update(Register::SignalSelect1); - - // Disable the path of DAC → SPK-Amp: DACS bit = “1†→ “0†- map.r.signal_select_1.DACS = 0; - update(Register::SignalSelect1); - - // Power down DAC, Programmable Filter and speaker: PMDAC=PMPFIL=PMSL bits= “1â€â†’“0†- set_dac_power(false); - // map.r.power_management_1.PMPFIL = 0; - // update(Register::PowerManagement1); - set_speaker_power(false); + // Enter Speaker-Amp Power Save Mode: SLPSN bit = “1†→ “0†+ map.r.signal_select_1.SLPSN = 0; + update(Register::SignalSelect1); + + // Disable the path of DAC → SPK-Amp: DACS bit = “1†→ “0†+ map.r.signal_select_1.DACS = 0; + update(Register::SignalSelect1); + + // Power down DAC, Programmable Filter and speaker: PMDAC=PMPFIL=PMSL bits= “1â€â†’“0†+ set_dac_power(false); + // map.r.power_management_1.PMPFIL = 0; + // update(Register::PowerManagement1); + set_speaker_power(false); } void AK4951::microphone_enable(int8_t alc_mode) { -// alc_mode =0 = (OFF =same as original code = NOT using AK4951 Programmable digital filter block), -// alc_mode >1 (with DIGITAL FILTER BLOCK , example : 1:(+12dB) , 2:(+9dB)", 3:(+6dB), ...) - -// map.r.digital_mic.DMIC = 0; // originally commented code -// update(Register::DigitalMic); // originally commented code - -uint_fast8_t mgain =0b0111; // Pre-amp mic (Original code, =0b0111 (+21dB's=7x3dBs),(Max is NOT 0b1111!, it is 0b1010=+30dBs=10x3dBs) - -map.r.signal_select_2.INL = 0b01; // Lch input signal = LIN2 , our ext. MONO MIC is connected here LIN2 in Portapack. -map.r.signal_select_2.INR = 0b01; // Rch input signal = RIN2 , Not used ,not connected ,but no problem. -map.r.signal_select_2.MICL = 0; // MPWR = 2.4V (it has two possible settings , 2.4V or 2.0V) , (majority smarthphones around 2V , range 1V-5V) -update(Register::SignalSelect2); - -// ------Common code part, = original setting conditions, it is fine for all user-GUI alc_modes: OFF , and ALC modes .*/ -map.r.digital_filter_select_1.HPFAD = 1; // HPF1 ON (after ADC);page 40 datasheet, HPFAD bit controls the ON/OFF of the HPF1 (HPF ON is recommended). -map.r.digital_filter_select_1.HPFC = 0b11; // HPF Cut off frequency of high pass filter from 236.8 Hz @fs=48k ("00":3.7Hz, "01":14,8Hz, "10":118,4Hz) -update(Register::DigitalFilterSelect1); - -// map.r.r_ch_mic_gain_setting.MGR = 0x80; // Microphone sensitivity correction = 0dB., (not used by now , original code cond.) -// update(Register::RchMicGainSetting); // (those two lines , not activated, same as original) - -// pre-load 4 byes LPF coefficicients (.lpf_coefficient_0,1,2,3), FSA 14..0, FSB 14..0 , (fcut initial 6kHz, fs 48Khz). -// it will be default pre-loading coeff. for al ALC modes, LPF bit is activated down, for all ALC digital modes. -map.r.lpf_coefficient_0.l = 0x5F; // Pre-loading here LPF 6kHz, 1st Order from digital Block , Fc=6000 Hz, fs = 48khz -map.r.lpf_coefficient_1.h = 0x09; // LPF bit is activated down, for all ALC digital modes. -map.r.lpf_coefficient_2.l = 0xBF; // Writting reg to AK4951, with "update", following instructions. -map.r.lpf_coefficient_3.h = 0x32; - -update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 -update(Register::LPFCoefficient1); // In this case , LPF 6KHz , when we activate the LPF block. -update(Register::LPFCoefficient2); -update(Register::LPFCoefficient3); - -// Reset , setting OFF all 5 x Digital Equalizer filters -map.r.digital_filter_select_3.EQ1 = 0; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled -map.r.digital_filter_select_3.EQ2 = 0; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled -map.r.digital_filter_select_3.EQ3 = 0; // EQ3 Coeffic Setting , (0: Disable-default, audio data passes EQ3 block by 0dB gain). When EQ3="1â€, the settings of E3A15-0, E3B15-0 and E3C15-0 bits are enabled -map.r.digital_filter_select_3.EQ4 = 0; // EQ4 Coeffic Setting , (0: Disable-default, audio data passes EQ4 block by 0dB gain). When EQ4="1â€, the settings of E4A15-0, E4B15-0 and E4C15-0 bits are enabled -map.r.digital_filter_select_3.EQ5 = 0; // EQ5 Coeffic Setting , (0: Disable-default, audio data passes EQ5 block by 0dB gain). When EQ5="1â€, the settings of E5A15-0, E5B15-0 and E5C15-0 bits are enabled -update(Register::DigitalFilterSelect3); // A,B,C EQ1 Coefficients are already pre-loaded in ak4951.hpp - - - if (alc_mode==0) { // Programmable Digital Filter OFF, same as original condition., no Digital ALC, nor Wind Noise Filter, LPF , EQ - - map.r.digital_filter_select_2.LPF = 0; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. - update(Register::DigitalFilterSelect2); - - // Pre-loading AUDIO PATH with all DIGITAL BLOCK by pased, see, audio path block diagramm AK4951 datasheet + Table Playback mode -Recording mode. - // Digital filter block PATH is BY PASSED (we can swith off DIG. BLOCK power , PMPFIL=0) .The Path in Recording Mode 2 & Playback Mode 2 (NO DIG FILTER BLOCK AT ALL, not for MIC recording, nor for Playback) - map.r.digital_filter_mode.ADCPF = 1; // ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) - map.r.digital_filter_mode.PFSDO = 0; // ADC bit switch ("0" : 1st order HPF) connectedto the Output. By bass DIGITAL block . - map.r.digital_filter_mode.PFDAC = 0b00; // (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) - update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. - - map.r.power_management_1.PMADL = 1; // ADC Lch = Lch input signal. Mic Amp Lch and ADC Lch Power Management - map.r.power_management_1.PMADR = 1; // ADC Rch = Rch input signal. Mic Amp Rch and ADC Rch Power Management - map.r.power_management_1.PMPFIL = 0; // Pre-loading , Programmable Dig. filter OFF ,filter unused, routed around.(original value = 0 ) - update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) - - // 1059/fs, 22ms @ 48kHz - chThdSleepMilliseconds(22); - - } else { // ( alc_mode !=0) - - switch(alc_mode) { // Pre-loading register values depending on user-GUI selection (they will be sended below, with "update(Register_name::xxx )". - - case 1: // ALC-> on, (+12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0xC0; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, C0H=+12dBs) - map.r.l_ch_input_volume_control.IV = 0xC0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xC0; // Right Input Dig Vol Setting, same comment as above , The value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 2: // ALC-> on, (+09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0xB8; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, B8H= +9dBs) - map.r.l_ch_input_volume_control.IV = 0xB8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xB8; // Right Input Dig Vol Setting, same comment as above , The value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 3: // ALC-> on, (+06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0xB0; // 0xB8 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, B0H= +6dBs) - map.r.l_ch_input_volume_control.IV = 0xB0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xB0; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 4: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + Pre-amp Mic (+21dB=original) - // + EQ boosting ~<2kHz (f0:1,1k, fb:1,7K, k=1,8) && + LPF 3,5k - map.r.alc_mode_control_2.REF = 0xA8; // 0xA8 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A8H= +3dBs) - map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - //The EQn (n=1, 2, 3, 4 or 5) coefficient must be set when EQn bit = “0†or PMPFIL bit = “0â€. - map.r.digital_filter_select_3.EQ1 = 1; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled - update(Register::DigitalFilterSelect3); // A,B,C EQ1 Coefficients are already pre-loaded in ak4951.hpp - - map.r.lpf_coefficient_0.l = 0x0D; // Pre-loading here LPF 3,5k , 1st Order from digital Block , Fc=3.500 Hz, fs = 48khz - map.r.lpf_coefficient_1.h = 0x06; // LPF bit is activated down, for all ALC digital modes. - map.r.lpf_coefficient_2.l = 0x1A; // Writting reg to AK4951 , down with update.... - map.r.lpf_coefficient_3.h = 0x2C; - // LPF bit is activated down, for all ALC digital modes. - break; - - case 5: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + Pre-amp Mic (+21dB=original) - // + EQ boosting ~<3kHz (f0~1k4,fb~2,4k,k=1,8) && LPF 4kHz - map.r.alc_mode_control_2.REF = 0xA8; // 0xA0 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A8H= +3dBs) - map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - map.r.digital_filter_select_3.EQ2 = 1; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled - update(Register::DigitalFilterSelect3); - - map.r.lpf_coefficient_0.l = 0xC3; // Pre-loading here LPF 4k , 1st Order from digital Block , Fc=4000 Hz, fs = 48khz - map.r.lpf_coefficient_1.h = 0x06; // LPF bit is activated down, for all ALC digital modes. - map.r.lpf_coefficient_2.l = 0x86; // Writting reg to AK4951 , down with update.... - map.r.lpf_coefficient_3.h = 0x2D; - // LPF bit is activated down, for all ALC digital modes. - break; - - case 6: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0xA8; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A0H= 0dBs) - map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 7: // ALC-> on, (+00dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0xA0; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A0H= 0dBs) - map.r.l_ch_input_volume_control.IV = 0xA0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0xA0; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 8: // ALC-> on, (-03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0x98; //REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 98H=-03dBs) - map.r.l_ch_input_volume_control.IV = 0x98; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0x98; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 9: // ALC-> on, (-06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) - map.r.alc_mode_control_2.REF = 0x90; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 90H=-06dBs) - map.r.l_ch_input_volume_control.IV = 0x90; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0x90; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 10: // ALC-> on, (-09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -3dB (18dB's) - // Reduce also Pre-amp Mic -3dB's (+18dB's) - mgain = 0b0110; // Pre-amp mic Mic Gain Pre-amp (+18dB), Original=0b0111 (+21dB's =7x3dBs), - - map.r.alc_mode_control_2.REF = 0x88; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 88H=-09dBs) - map.r.l_ch_input_volume_control.IV = 0x88; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0x88; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - - case 11: // ALC-> on, (-12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -6dB (15dB's) - // Reduce also Pre-amp Mic -6dB's (+15dB's) - mgain = 0b0101; // Pre-amp mic Mic Gain Pre-amp (+15dB), (Original=0b0111 (+21dB's= 7x3dBs), - - map.r.alc_mode_control_2.REF = 0x80; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 80H=-12dBs) - map.r.l_ch_input_volume_control.IV = 0x80; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - map.r.r_ch_input_volume_control.IV = 0x80; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s - - // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz - // LPF bit is activated down, for all ALC digital modes. - break; - } - - //-------------------------------DIGITAL ALC (Automatic Level Control ) --- -------- - map.r.alc_mode_control_1.ALC = 0; // LMTH2-0, WTM1-0, RGAIN2-0, REF7-0, RFST1-0, EQFC1-0, FRATT, FRN and ALCEQN bits (needs to be set up with ALC disable = 0) - update(Register::ALCModeControl1); - - map.r.timer_select.FRN = 0; // (FRN= 0 Fast Recovery mode , enable ) - map.r.timer_select.FRATT = 0; // Fast Recovery Ref. Volume Atten. Amount -0,00106dB's, timing 4/fs (default) - map.r.timer_select.ADRST = 0b00; // initial offset ADC cycles , 22ms @fs=48Khz. - update(Register::TimerSelect); - - map.r.alc_timer_select.RFST = 0b00; // RFST1-0: ALC Fast Recovery Speed Default: “00†(0.0032dB) - map.r.alc_timer_select.WTM = 0b00; // ALC Recovery Operation Waiting Period 128/fs = 2,7 mseg (min=default) - map.r.alc_timer_select.EQFC = 0b10; // Selecting default, fs 48Khz , ALCEQ: First order zero pole high pass filter fc2=100Hz, fc1=150Hz - map.r.alc_timer_select.IVTM = 0; // IVTM bit set the vol transition time ,236/fs = 4,9msecs (min) (default was 19,7msegs.) - update(Register::ALCTimerSelect); - - map.r.alc_mode_control_1.LMTH10 = 0b11; // ALC Limiter Detec Level/ Recovery Counter Reset; lower 2 bits (Ob111=-8,4dbs), (default 0b000=-2,5dBs) - map.r.alc_mode_control_1.RGAIN = 0b000; // ALC Recovery Gain Step, max step , max speed. Default: “000†(0.00424dB) - map.r.alc_mode_control_1.ALC = 1; // ALC Enable . (we are now, NOT in MANUAL volume mode, only becomes manual when (ALC=“0†while ADCPF=“1â€. ) - map.r.alc_mode_control_1.LMTH2 = 1; // ALC Limiter Detection Level/ Recovery Counter Reset Level,Upper bit,default 0b000 - map.r.alc_mode_control_1.ALCEQN = 1; // ALC EQ Off =1 not used by now, 0: ALC EQ On (default) - update(Register::ALCModeControl1); - - // map.r.alc_mode_control_2.REF = 0x??; // Pre-loaded in top part. Maximum gain at ALC recovery operation,.(FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - update(Register::ALCModeControl2); - - // map.r.l_ch_input_volume_control.IV = 0x??; // Pre-loaded in top part. Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - update(Register::LchInputVolumeControl); - - // map.r.r_ch_input_volume_control.IV = 0x??; // Pre-loaded in top part. Right,Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) - update(Register::RchInputVolumeControl); - - - //---------------Switch ON, Digital Automatic Wind Noise Filter reduction ------------------- - // Difficult to realise that Dynamic HPF Wind noise filter benefit, maybe because we have another fixed HPF 236.8 Hz . - // Anyway , we propose to activate it , with default setting conditions. - map.r.power_management_1.PMPFIL = 0; // (*1) To programm SENC, STG , we need PMPFIL = 0 . (but this disconnect Digital block power supply. - update(Register::PowerManagement1); // Updated PMPFIL to 0 . (*1) - - map.r.auto_hpf_control.STG = 0b00; // (00=LOW ATTENUATION Level), lets put 11 (HIGH ATTENUATION Level) (default 00) - map.r.auto_hpf_control.SENC = 0b011; // (000=LOW sensitivity detection)… 111((MAX sensitivity detection) (default 011) - map.r.auto_hpf_control.AHPF = 1; // Autom. Wind noise filter ON (AHPF bit=“1â€).It atten. wind noise when detecting ,and adjusts the atten. level dynamically. - update(Register::AutoHPFControl); - - // We are in Digital Block ON , (Wind Noise Filter+ALC+LPF+EQ),==> needs at the end , PMPFIL=1 , Program. Dig.filter ON - // map.r.power_management_1.PMPFIL = 1; // that instruction is at the end , we can skp pre-loading Programmable Dig. filter ON (*1) - //--------------------------------------------------------------------- - - // Writing AUDIO PATH diagramm, Changing Audio mode path : Playback mode1 /-Recording mode2. (Figure 37 AK4951 datasheet, Table 27. Recording Playback Mode) - // When changing those modes, PMPFIL bit must be “0â€, it is OK (*1) - map.r.digital_filter_mode.ADCPF = 1; // ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) - map.r.digital_filter_mode.PFSDO = 1; // ADC (+ 1st order HPF) Output - map.r.digital_filter_mode.PFDAC = 0b00; // (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) - update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. - - // The EQn (n=1, 2, 3, 4 or 5) coefficient must be set when EQn bit = “0†or PMPFIL bit = “0â€., but we are already (*1) - // map.r.power_management_1.PMPFIL = 0; // In the previous Wind Noise Filter , we already set up PPFIL = 0 - // update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) - - // ... Set EQ & LPF coefficients --------------------------------- - - // writting to the IC ak4951 reg. settings defined in Ak4951.hpp , the 30 bytes , EQ coefficient = 5 (EQ1,2,3,4,5) x 3 (A,B,C coefficients) x 2 bytes (16 bits) - update(Register::E1Coefficient0); // we could pre-load here,ex ,"map.r.e1_coefficient_0.l = 0x50;" , EQ1 Coefficient A : A7...A0, but already done in ak4951.hpp - update(Register::E1Coefficient1); // we could pre-load here,ex ,"map.r.e1_coefficient_1.h = 0xFE;" , EQ1 Coefficient A : A15..A8, " " - update(Register::E1Coefficient2); // we could pre-load here,ex ,"map.r.e1_coefficient_2.l = 0x29;" , EQ1 Coefficient B : B7...B0, " " - update(Register::E1Coefficient3); // we could pre-load here,ex ,"map.r.e1_coefficient_3.h = 0xC5;" , EQ1 Coefficient B : B15..B8, " " - update(Register::E1Coefficient4); // we could pre-load here,ex ,"map.r.e1_coefficient_4.l = 0xA0;" , EQ1 Coefficient C : C7...C0, " " - update(Register::E1Coefficient5); // we could pre-load here,ex ,"map.r.e1_coefficient_5.h = 0x1C;" , EQ1 Coefficient C : C15..C8, " " - - update(Register::E2Coefficient0); // writing pre-loaded EQ2 coefficcients - update(Register::E2Coefficient1); - update(Register::E2Coefficient2); - update(Register::E2Coefficient3); - update(Register::E2Coefficient4); - update(Register::E2Coefficient5); - - // Already pre-loaded LPF coefficients to 6k, 3,5k or 4k ,(LPF 6Khz all digital alc modes top , except when 3k5 , 4k) - update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 - update(Register::LPFCoefficient1); - update(Register::LPFCoefficient2); - update(Register::LPFCoefficient3); - - // Activating LPF block , (and re-configuring the rest of bits of the same register) - map.r.digital_filter_select_2.HPF = 0; // HPF2-Block, Coeffic Setting Enable (OFF-Default), When HPF bit is “0â€, audio data passes the HPF2 block by is 0dB gain. - map.r.digital_filter_select_2.LPF = 1; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. - map.r.digital_filter_select_2.FIL3 = 0; // Stereo_Emphasis_Filter-Block,(OFF-Default) Coefficient Setting Enable , OFF , Disable. - map.r.digital_filter_select_2.EQ0 = 0; // Gain Compensation-Block, (OFF-Default) Coeffic Setting Enable, When EQ0 bit = “0†audio data passes the EQ0 block by 0dB gain. - map.r.digital_filter_select_2.GN = 0b00; // Gain Setting of the Gain Compensation Block Default: “00â€-Default (0dB) - update(Register::DigitalFilterSelect2); - - // Acitivating digital block , power supply - map.r.power_management_1.PMADL = 1; // ADC Lch = Lch input signal. Mic Amp Lch and ADC Lch Power Management - map.r.power_management_1.PMADR = 1; // ADC Rch = Rch input signal. Mic Amp Rch and ADC Rch Power Management - map.r.power_management_1.PMPFIL = 1; // Pre-loaded in top part. Orig value=0, Programmable Digital filter unused (not power up), routed around. - update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) - - // 1059/fs, 22ms @ 48kHz - chThdSleepMilliseconds(22); - - } - - // Common part for all alc_mode , -------------------------- - // const uint_fast8_t mgain = 0b0111; // Already pre-loaded , in above switch case . - map.r.signal_select_1.MGAIN20 = mgain & 7; // writing 3 lower bits of mgain , (pre-amp mic gain). - map.r.signal_select_1.PMMP = 1; // Activating DC Mic Power supply through 2kohms res., similar majority smartphones headphone+mic jack, "plug-in-power" - map.r.signal_select_1.MPSEL = 1; // MPWR2 pin ,selecting output voltage to MPWR2 pin, that we are using in portapack ext. MIC) - map.r.signal_select_1.MGAIN3 = (mgain >> 3) & 1; // writing 4th upper bit of mgain (pre-amp mic gain). - update(Register::SignalSelect1); - + // alc_mode =0 = (OFF =same as original code = NOT using AK4951 Programmable digital filter block), + // alc_mode >1 (with DIGITAL FILTER BLOCK , example : 1:(+12dB) , 2:(+9dB)", 3:(+6dB), ...) + + // map.r.digital_mic.DMIC = 0; // originally commented code + // update(Register::DigitalMic); // originally commented code + + uint_fast8_t mgain = 0b0111; // Pre-amp mic (Original code, =0b0111 (+21dB's=7x3dBs),(Max is NOT 0b1111!, it is 0b1010=+30dBs=10x3dBs) + + map.r.signal_select_2.INL = 0b01; // Lch input signal = LIN2 , our ext. MONO MIC is connected here LIN2 in Portapack. + map.r.signal_select_2.INR = 0b01; // Rch input signal = RIN2 , Not used ,not connected ,but no problem. + map.r.signal_select_2.MICL = 0; // MPWR = 2.4V (it has two possible settings , 2.4V or 2.0V) , (majority smarthphones around 2V , range 1V-5V) + update(Register::SignalSelect2); + + // ------Common code part, = original setting conditions, it is fine for all user-GUI alc_modes: OFF , and ALC modes .*/ + map.r.digital_filter_select_1.HPFAD = 1; // HPF1 ON (after ADC);page 40 datasheet, HPFAD bit controls the ON/OFF of the HPF1 (HPF ON is recommended). + map.r.digital_filter_select_1.HPFC = 0b11; // HPF Cut off frequency of high pass filter from 236.8 Hz @fs=48k ("00":3.7Hz, "01":14,8Hz, "10":118,4Hz) + update(Register::DigitalFilterSelect1); + + // map.r.r_ch_mic_gain_setting.MGR = 0x80; // Microphone sensitivity correction = 0dB., (not used by now , original code cond.) + // update(Register::RchMicGainSetting); // (those two lines , not activated, same as original) + + // pre-load 4 byes LPF coefficicients (.lpf_coefficient_0,1,2,3), FSA 14..0, FSB 14..0 , (fcut initial 6kHz, fs 48Khz). + // it will be default pre-loading coeff. for al ALC modes, LPF bit is activated down, for all ALC digital modes. + map.r.lpf_coefficient_0.l = 0x5F; // Pre-loading here LPF 6kHz, 1st Order from digital Block , Fc=6000 Hz, fs = 48khz + map.r.lpf_coefficient_1.h = 0x09; // LPF bit is activated down, for all ALC digital modes. + map.r.lpf_coefficient_2.l = 0xBF; // Writting reg to AK4951, with "update", following instructions. + map.r.lpf_coefficient_3.h = 0x32; + + update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 + update(Register::LPFCoefficient1); // In this case , LPF 6KHz , when we activate the LPF block. + update(Register::LPFCoefficient2); + update(Register::LPFCoefficient3); + + // Reset , setting OFF all 5 x Digital Equalizer filters + map.r.digital_filter_select_3.EQ1 = 0; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled + map.r.digital_filter_select_3.EQ2 = 0; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled + map.r.digital_filter_select_3.EQ3 = 0; // EQ3 Coeffic Setting , (0: Disable-default, audio data passes EQ3 block by 0dB gain). When EQ3="1â€, the settings of E3A15-0, E3B15-0 and E3C15-0 bits are enabled + map.r.digital_filter_select_3.EQ4 = 0; // EQ4 Coeffic Setting , (0: Disable-default, audio data passes EQ4 block by 0dB gain). When EQ4="1â€, the settings of E4A15-0, E4B15-0 and E4C15-0 bits are enabled + map.r.digital_filter_select_3.EQ5 = 0; // EQ5 Coeffic Setting , (0: Disable-default, audio data passes EQ5 block by 0dB gain). When EQ5="1â€, the settings of E5A15-0, E5B15-0 and E5C15-0 bits are enabled + update(Register::DigitalFilterSelect3); // A,B,C EQ1 Coefficients are already pre-loaded in ak4951.hpp + + if (alc_mode == 0) { // Programmable Digital Filter OFF, same as original condition., no Digital ALC, nor Wind Noise Filter, LPF , EQ + + map.r.digital_filter_select_2.LPF = 0; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. + update(Register::DigitalFilterSelect2); + + // Pre-loading AUDIO PATH with all DIGITAL BLOCK by pased, see, audio path block diagramm AK4951 datasheet + Table Playback mode -Recording mode. + // Digital filter block PATH is BY PASSED (we can swith off DIG. BLOCK power , PMPFIL=0) .The Path in Recording Mode 2 & Playback Mode 2 (NO DIG FILTER BLOCK AT ALL, not for MIC recording, nor for Playback) + map.r.digital_filter_mode.ADCPF = 1; // ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) + map.r.digital_filter_mode.PFSDO = 0; // ADC bit switch ("0" : 1st order HPF) connectedto the Output. By bass DIGITAL block . + map.r.digital_filter_mode.PFDAC = 0b00; // (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) + update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. + + map.r.power_management_1.PMADL = 1; // ADC Lch = Lch input signal. Mic Amp Lch and ADC Lch Power Management + map.r.power_management_1.PMADR = 1; // ADC Rch = Rch input signal. Mic Amp Rch and ADC Rch Power Management + map.r.power_management_1.PMPFIL = 0; // Pre-loading , Programmable Dig. filter OFF ,filter unused, routed around.(original value = 0 ) + update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) + + // 1059/fs, 22ms @ 48kHz + chThdSleepMilliseconds(22); + + } else { // ( alc_mode !=0) + + switch (alc_mode) { // Pre-loading register values depending on user-GUI selection (they will be sended below, with "update(Register_name::xxx )". + + case 1: // ALC-> on, (+12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0xC0; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, C0H=+12dBs) + map.r.l_ch_input_volume_control.IV = 0xC0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xC0; // Right Input Dig Vol Setting, same comment as above , The value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 2: // ALC-> on, (+09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0xB8; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, B8H= +9dBs) + map.r.l_ch_input_volume_control.IV = 0xB8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xB8; // Right Input Dig Vol Setting, same comment as above , The value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 3: // ALC-> on, (+06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0xB0; // 0xB8 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, B0H= +6dBs) + map.r.l_ch_input_volume_control.IV = 0xB0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xB0; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 4: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + Pre-amp Mic (+21dB=original) + // + EQ boosting ~<2kHz (f0:1,1k, fb:1,7K, k=1,8) && + LPF 3,5k + map.r.alc_mode_control_2.REF = 0xA8; // 0xA8 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A8H= +3dBs) + map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // The EQn (n=1, 2, 3, 4 or 5) coefficient must be set when EQn bit = “0†or PMPFIL bit = “0â€. + map.r.digital_filter_select_3.EQ1 = 1; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled + update(Register::DigitalFilterSelect3); // A,B,C EQ1 Coefficients are already pre-loaded in ak4951.hpp + + map.r.lpf_coefficient_0.l = 0x0D; // Pre-loading here LPF 3,5k , 1st Order from digital Block , Fc=3.500 Hz, fs = 48khz + map.r.lpf_coefficient_1.h = 0x06; // LPF bit is activated down, for all ALC digital modes. + map.r.lpf_coefficient_2.l = 0x1A; // Writting reg to AK4951 , down with update.... + map.r.lpf_coefficient_3.h = 0x2C; + // LPF bit is activated down, for all ALC digital modes. + break; + + case 5: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + Pre-amp Mic (+21dB=original) + // + EQ boosting ~<3kHz (f0~1k4,fb~2,4k,k=1,8) && LPF 4kHz + map.r.alc_mode_control_2.REF = 0xA8; // 0xA0 , REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A8H= +3dBs) + map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + map.r.digital_filter_select_3.EQ2 = 1; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled + update(Register::DigitalFilterSelect3); + + map.r.lpf_coefficient_0.l = 0xC3; // Pre-loading here LPF 4k , 1st Order from digital Block , Fc=4000 Hz, fs = 48khz + map.r.lpf_coefficient_1.h = 0x06; // LPF bit is activated down, for all ALC digital modes. + map.r.lpf_coefficient_2.l = 0x86; // Writting reg to AK4951 , down with update.... + map.r.lpf_coefficient_3.h = 0x2D; + // LPF bit is activated down, for all ALC digital modes. + break; + + case 6: // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0xA8; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A0H= 0dBs) + map.r.l_ch_input_volume_control.IV = 0xA8; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xA8; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 7: // ALC-> on, (+00dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0xA0; // REF7-0 bits,max gain at ALC recoveryoperation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, A0H= 0dBs) + map.r.l_ch_input_volume_control.IV = 0xA0; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0xA0; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 8: // ALC-> on, (-03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0x98; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 98H=-03dBs) + map.r.l_ch_input_volume_control.IV = 0x98; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0x98; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 9: // ALC-> on, (-06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original) + map.r.alc_mode_control_2.REF = 0x90; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 90H=-06dBs) + map.r.l_ch_input_volume_control.IV = 0x90; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0x90; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 10: // ALC-> on, (-09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -3dB (18dB's) + // Reduce also Pre-amp Mic -3dB's (+18dB's) + mgain = 0b0110; // Pre-amp mic Mic Gain Pre-amp (+18dB), Original=0b0111 (+21dB's =7x3dBs), + + map.r.alc_mode_control_2.REF = 0x88; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 88H=-09dBs) + map.r.l_ch_input_volume_control.IV = 0x88; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0x88; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + + case 11: // ALC-> on, (-12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -6dB (15dB's) + // Reduce also Pre-amp Mic -6dB's (+15dB's) + mgain = 0b0101; // Pre-amp mic Mic Gain Pre-amp (+15dB), (Original=0b0111 (+21dB's= 7x3dBs), + + map.r.alc_mode_control_2.REF = 0x80; // REF7-0 bits,max gain at ALC recovery operation,(FFH +36dBs , D0H +18dBs, A0H 0dBs, 80H=-12dBs) + map.r.l_ch_input_volume_control.IV = 0x80; // Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + map.r.r_ch_input_volume_control.IV = 0x80; // Right Input Dig Vol Setting, same comment as above , Then value of IVOL should be <= than REF’s + + // Already Pre-loaded, "map.r.lpf_coefficient", 6Khz - LPF 1st Order from digital Block,Fc=6000Hz,fs = 48khz + // LPF bit is activated down, for all ALC digital modes. + break; + } + + //-------------------------------DIGITAL ALC (Automatic Level Control ) --- -------- + map.r.alc_mode_control_1.ALC = 0; // LMTH2-0, WTM1-0, RGAIN2-0, REF7-0, RFST1-0, EQFC1-0, FRATT, FRN and ALCEQN bits (needs to be set up with ALC disable = 0) + update(Register::ALCModeControl1); + + map.r.timer_select.FRN = 0; // (FRN= 0 Fast Recovery mode , enable ) + map.r.timer_select.FRATT = 0; // Fast Recovery Ref. Volume Atten. Amount -0,00106dB's, timing 4/fs (default) + map.r.timer_select.ADRST = 0b00; // initial offset ADC cycles , 22ms @fs=48Khz. + update(Register::TimerSelect); + + map.r.alc_timer_select.RFST = 0b00; // RFST1-0: ALC Fast Recovery Speed Default: “00†(0.0032dB) + map.r.alc_timer_select.WTM = 0b00; // ALC Recovery Operation Waiting Period 128/fs = 2,7 mseg (min=default) + map.r.alc_timer_select.EQFC = 0b10; // Selecting default, fs 48Khz , ALCEQ: First order zero pole high pass filter fc2=100Hz, fc1=150Hz + map.r.alc_timer_select.IVTM = 0; // IVTM bit set the vol transition time ,236/fs = 4,9msecs (min) (default was 19,7msegs.) + update(Register::ALCTimerSelect); + + map.r.alc_mode_control_1.LMTH10 = 0b11; // ALC Limiter Detec Level/ Recovery Counter Reset; lower 2 bits (Ob111=-8,4dbs), (default 0b000=-2,5dBs) + map.r.alc_mode_control_1.RGAIN = 0b000; // ALC Recovery Gain Step, max step , max speed. Default: “000†(0.00424dB) + map.r.alc_mode_control_1.ALC = 1; // ALC Enable . (we are now, NOT in MANUAL volume mode, only becomes manual when (ALC=“0†while ADCPF=“1â€. ) + map.r.alc_mode_control_1.LMTH2 = 1; // ALC Limiter Detection Level/ Recovery Counter Reset Level,Upper bit,default 0b000 + map.r.alc_mode_control_1.ALCEQN = 1; // ALC EQ Off =1 not used by now, 0: ALC EQ On (default) + update(Register::ALCModeControl1); + + // map.r.alc_mode_control_2.REF = 0x??; // Pre-loaded in top part. Maximum gain at ALC recovery operation,.(FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + update(Register::ALCModeControl2); + + // map.r.l_ch_input_volume_control.IV = 0x??; // Pre-loaded in top part. Left, Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + update(Register::LchInputVolumeControl); + + // map.r.r_ch_input_volume_control.IV = 0x??; // Pre-loaded in top part. Right,Input Digital Volume Setting, (FFH +36dBs , D0H +18dBs, A0H 0dBs, 70H=-18dBs) + update(Register::RchInputVolumeControl); + + //---------------Switch ON, Digital Automatic Wind Noise Filter reduction ------------------- + // Difficult to realise that Dynamic HPF Wind noise filter benefit, maybe because we have another fixed HPF 236.8 Hz . + // Anyway , we propose to activate it , with default setting conditions. + map.r.power_management_1.PMPFIL = 0; // (*1) To programm SENC, STG , we need PMPFIL = 0 . (but this disconnect Digital block power supply. + update(Register::PowerManagement1); // Updated PMPFIL to 0 . (*1) + + map.r.auto_hpf_control.STG = 0b00; // (00=LOW ATTENUATION Level), lets put 11 (HIGH ATTENUATION Level) (default 00) + map.r.auto_hpf_control.SENC = 0b011; // (000=LOW sensitivity detection)… 111((MAX sensitivity detection) (default 011) + map.r.auto_hpf_control.AHPF = 1; // Autom. Wind noise filter ON (AHPF bit=“1â€).It atten. wind noise when detecting ,and adjusts the atten. level dynamically. + update(Register::AutoHPFControl); + + // We are in Digital Block ON , (Wind Noise Filter+ALC+LPF+EQ),==> needs at the end , PMPFIL=1 , Program. Dig.filter ON + // map.r.power_management_1.PMPFIL = 1; // that instruction is at the end , we can skp pre-loading Programmable Dig. filter ON (*1) + //--------------------------------------------------------------------- + + // Writing AUDIO PATH diagramm, Changing Audio mode path : Playback mode1 /-Recording mode2. (Figure 37 AK4951 datasheet, Table 27. Recording Playback Mode) + // When changing those modes, PMPFIL bit must be “0â€, it is OK (*1) + map.r.digital_filter_mode.ADCPF = 1; // ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) + map.r.digital_filter_mode.PFSDO = 1; // ADC (+ 1st order HPF) Output + map.r.digital_filter_mode.PFDAC = 0b00; // (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) + update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. + + // The EQn (n=1, 2, 3, 4 or 5) coefficient must be set when EQn bit = “0†or PMPFIL bit = “0â€., but we are already (*1) + // map.r.power_management_1.PMPFIL = 0; // In the previous Wind Noise Filter , we already set up PPFIL = 0 + // update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) + + // ... Set EQ & LPF coefficients --------------------------------- + + // writting to the IC ak4951 reg. settings defined in Ak4951.hpp , the 30 bytes , EQ coefficient = 5 (EQ1,2,3,4,5) x 3 (A,B,C coefficients) x 2 bytes (16 bits) + update(Register::E1Coefficient0); // we could pre-load here,ex ,"map.r.e1_coefficient_0.l = 0x50;" , EQ1 Coefficient A : A7...A0, but already done in ak4951.hpp + update(Register::E1Coefficient1); // we could pre-load here,ex ,"map.r.e1_coefficient_1.h = 0xFE;" , EQ1 Coefficient A : A15..A8, " " + update(Register::E1Coefficient2); // we could pre-load here,ex ,"map.r.e1_coefficient_2.l = 0x29;" , EQ1 Coefficient B : B7...B0, " " + update(Register::E1Coefficient3); // we could pre-load here,ex ,"map.r.e1_coefficient_3.h = 0xC5;" , EQ1 Coefficient B : B15..B8, " " + update(Register::E1Coefficient4); // we could pre-load here,ex ,"map.r.e1_coefficient_4.l = 0xA0;" , EQ1 Coefficient C : C7...C0, " " + update(Register::E1Coefficient5); // we could pre-load here,ex ,"map.r.e1_coefficient_5.h = 0x1C;" , EQ1 Coefficient C : C15..C8, " " + + update(Register::E2Coefficient0); // writing pre-loaded EQ2 coefficcients + update(Register::E2Coefficient1); + update(Register::E2Coefficient2); + update(Register::E2Coefficient3); + update(Register::E2Coefficient4); + update(Register::E2Coefficient5); + + // Already pre-loaded LPF coefficients to 6k, 3,5k or 4k ,(LPF 6Khz all digital alc modes top , except when 3k5 , 4k) + update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 + update(Register::LPFCoefficient1); + update(Register::LPFCoefficient2); + update(Register::LPFCoefficient3); + + // Activating LPF block , (and re-configuring the rest of bits of the same register) + map.r.digital_filter_select_2.HPF = 0; // HPF2-Block, Coeffic Setting Enable (OFF-Default), When HPF bit is “0â€, audio data passes the HPF2 block by is 0dB gain. + map.r.digital_filter_select_2.LPF = 1; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. + map.r.digital_filter_select_2.FIL3 = 0; // Stereo_Emphasis_Filter-Block,(OFF-Default) Coefficient Setting Enable , OFF , Disable. + map.r.digital_filter_select_2.EQ0 = 0; // Gain Compensation-Block, (OFF-Default) Coeffic Setting Enable, When EQ0 bit = “0†audio data passes the EQ0 block by 0dB gain. + map.r.digital_filter_select_2.GN = 0b00; // Gain Setting of the Gain Compensation Block Default: “00â€-Default (0dB) + update(Register::DigitalFilterSelect2); + + // Acitivating digital block , power supply + map.r.power_management_1.PMADL = 1; // ADC Lch = Lch input signal. Mic Amp Lch and ADC Lch Power Management + map.r.power_management_1.PMADR = 1; // ADC Rch = Rch input signal. Mic Amp Rch and ADC Rch Power Management + map.r.power_management_1.PMPFIL = 1; // Pre-loaded in top part. Orig value=0, Programmable Digital filter unused (not power up), routed around. + update(Register::PowerManagement1); // Activating the Power management of the used blocks . (Mic ADC always + Dig Block filter , when used ) + + // 1059/fs, 22ms @ 48kHz + chThdSleepMilliseconds(22); + } + + // Common part for all alc_mode , -------------------------- + // const uint_fast8_t mgain = 0b0111; // Already pre-loaded , in above switch case . + map.r.signal_select_1.MGAIN20 = mgain & 7; // writing 3 lower bits of mgain , (pre-amp mic gain). + map.r.signal_select_1.PMMP = 1; // Activating DC Mic Power supply through 2kohms res., similar majority smartphones headphone+mic jack, "plug-in-power" + map.r.signal_select_1.MPSEL = 1; // MPWR2 pin ,selecting output voltage to MPWR2 pin, that we are using in portapack ext. MIC) + map.r.signal_select_1.MGAIN3 = (mgain >> 3) & 1; // writing 4th upper bit of mgain (pre-amp mic gain). + update(Register::SignalSelect1); } - - void AK4951::microphone_disable() { - map.r.power_management_1.PMADL = 0; // original code , disable Power managem.Mic ADC L - map.r.power_management_1.PMADR = 0; // original code , disable Power managem.Mic ADC R - map.r.power_management_1.PMPFIL = 0; // original code , disable Power managem. all Programmable Dig. block - update(Register::PowerManagement1); - - map.r.alc_mode_control_1.ALC = 0; // original code , Restore , disable ALC block. - update(Register::ALCModeControl1); - - map.r.auto_hpf_control.AHPF = 0; //----------- new code addition , Restore disable Wind noise filter OFF (AHPF bit=“0â€). - update(Register::AutoHPFControl); - - //Restore original AUDIO PATH , condition, (Digital filter block PATH is BY PASSED) (we can also swith off DIG. BLOCK power , PMPFIL=0) - // The Path in Recording Mode 2 & Playback Mode 2 , (NO DIG FILTER BLOCK AT ALL, not for MIC recording, nor for Playback) - map.r.digital_filter_mode.ADCPF = 1; // new code addition , ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) - map.r.digital_filter_mode.PFSDO = 0; // new code addition , ADC bit switch ("0" : 1st order HPF) connectedto the Output. By bass DIGITAL block . - map.r.digital_filter_mode.PFDAC = 0b00; // new code addition , (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) - update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. - - // Restore original condition , LPF , OFF . same as when not using DIGITAL Programmable block - map.r.digital_filter_select_2.LPF = 0; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. - update(Register::DigitalFilterSelect2); - - map.r.lpf_coefficient_0.l = 0x00; // Pre-loading here LPF 6k , 1st Order from digital Block , Fc=6000 Hz, fs = 48khz - map.r.lpf_coefficient_1.h = 0x00; // LPF bit is activated down, for all ALC digital modes. - map.r.lpf_coefficient_2.l = 0x00; // Writting reg to AK4951 , down with update.... - map.r.lpf_coefficient_3.h = 0x00; - - update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 - update(Register::LPFCoefficient1); - update(Register::LPFCoefficient2); - update(Register::LPFCoefficient3); - - // Switch off all EQ 1,2,3,4,5 - map.r.digital_filter_select_3.EQ1 = 0; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled - map.r.digital_filter_select_3.EQ2 = 0; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled - map.r.digital_filter_select_3.EQ3 = 0; // EQ3 Coeffic Setting , (0: Disable-default, audio data passes EQ3 block by 0dB gain). When EQ3="1â€, the settings of E3A15-0, E3B15-0 and E3C15-0 bits are enabled - map.r.digital_filter_select_3.EQ4 = 0; // EQ4 Coeffic Setting , (0: Disable-default, audio data passes EQ4 block by 0dB gain). When EQ4="1â€, the settings of E4A15-0, E4B15-0 and E4C15-0 bits are enabled - map.r.digital_filter_select_3.EQ5 = 0; // EQ5 Coeffic Setting , (0: Disable-default, audio data passes EQ5 block by 0dB gain). When EQ5="1â€, the settings of E5A15-0, E5B15-0 and E5C15-0 bits are enabled - update(Register::DigitalFilterSelect3); - + map.r.power_management_1.PMADL = 0; // original code , disable Power managem.Mic ADC L + map.r.power_management_1.PMADR = 0; // original code , disable Power managem.Mic ADC R + map.r.power_management_1.PMPFIL = 0; // original code , disable Power managem. all Programmable Dig. block + update(Register::PowerManagement1); + + map.r.alc_mode_control_1.ALC = 0; // original code , Restore , disable ALC block. + update(Register::ALCModeControl1); + + map.r.auto_hpf_control.AHPF = 0; //----------- new code addition , Restore disable Wind noise filter OFF (AHPF bit=“0â€). + update(Register::AutoHPFControl); + + // Restore original AUDIO PATH , condition, (Digital filter block PATH is BY PASSED) (we can also swith off DIG. BLOCK power , PMPFIL=0) + // The Path in Recording Mode 2 & Playback Mode 2 , (NO DIG FILTER BLOCK AT ALL, not for MIC recording, nor for Playback) + map.r.digital_filter_mode.ADCPF = 1; // new code addition , ADCPF bit swith ("0" Mic after ADC Output connected (recording mode) to the DIGITAL FILTER BLOCK. ("1" Playback mode) + map.r.digital_filter_mode.PFSDO = 0; // new code addition , ADC bit switch ("0" : 1st order HPF) connectedto the Output. By bass DIGITAL block . + map.r.digital_filter_mode.PFDAC = 0b00; // new code addition , (Input selector for DAC (not used in MIC), SDTI= Audio Serial Data Input Pin) + update(Register::DigitalFilterMode); // Writing the Audio Path : NO DIGITAL BLOCK or DIG BLOCK FOR MIC , Audio mode path : Playback mode /-Recording mode. + + // Restore original condition , LPF , OFF . same as when not using DIGITAL Programmable block + map.r.digital_filter_select_2.LPF = 0; // LPF-Block, Coeffic Setting Enable (OFF-Default), When LPF bit is “0â€, audio data passes the LPF block by 0dB gain. + update(Register::DigitalFilterSelect2); + + map.r.lpf_coefficient_0.l = 0x00; // Pre-loading here LPF 6k , 1st Order from digital Block , Fc=6000 Hz, fs = 48khz + map.r.lpf_coefficient_1.h = 0x00; // LPF bit is activated down, for all ALC digital modes. + map.r.lpf_coefficient_2.l = 0x00; // Writting reg to AK4951 , down with update.... + map.r.lpf_coefficient_3.h = 0x00; + + update(Register::LPFCoefficient0); // Writing pre-loaded 4 bytes LPF CoefFiecients 14 bits (FSA13..0, FSB13..0 + update(Register::LPFCoefficient1); + update(Register::LPFCoefficient2); + update(Register::LPFCoefficient3); + + // Switch off all EQ 1,2,3,4,5 + map.r.digital_filter_select_3.EQ1 = 0; // EQ1 Coeffic Setting , (0: Disable-default, audio data passes EQ1 block by 0dB gain). When EQ1="1â€, the settings of E1A15-0, E1B15-0 and E1C15-0 bits are enabled + map.r.digital_filter_select_3.EQ2 = 0; // EQ2 Coeffic Setting , (0: Disable-default, audio data passes EQ2 block by 0dB gain). When EQ2="1â€, the settings of E2A15-0, E2B15-0 and E2C15-0 bits are enabled + map.r.digital_filter_select_3.EQ3 = 0; // EQ3 Coeffic Setting , (0: Disable-default, audio data passes EQ3 block by 0dB gain). When EQ3="1â€, the settings of E3A15-0, E3B15-0 and E3C15-0 bits are enabled + map.r.digital_filter_select_3.EQ4 = 0; // EQ4 Coeffic Setting , (0: Disable-default, audio data passes EQ4 block by 0dB gain). When EQ4="1â€, the settings of E4A15-0, E4B15-0 and E4C15-0 bits are enabled + map.r.digital_filter_select_3.EQ5 = 0; // EQ5 Coeffic Setting , (0: Disable-default, audio data passes EQ5 block by 0dB gain). When EQ5="1â€, the settings of E5A15-0, E5B15-0 and E5C15-0 bits are enabled + update(Register::DigitalFilterSelect3); } reg_t AK4951::read(const address_t reg_address) { - const std::array tx { reg_address }; - std::array rx { 0x00 }; - bus.transmit(bus_address, tx.data(), tx.size()); - bus.receive(bus_address, rx.data(), rx.size()); - return rx[0]; + const std::array tx{reg_address}; + std::array rx{0x00}; + bus.transmit(bus_address, tx.data(), tx.size()); + bus.receive(bus_address, rx.data(), rx.size()); + return rx[0]; } void AK4951::update(const Register reg) { - write(toUType(reg), map.w[toUType(reg)]); + write(toUType(reg), map.w[toUType(reg)]); } void AK4951::write(const address_t reg_address, const reg_t value) { - const std::array tx { reg_address, value }; - bus.transmit(bus_address, tx.data(), tx.size()); + const std::array tx{reg_address, value}; + bus.transmit(bus_address, tx.data(), tx.size()); } } /* namespace ak4951 */ diff --git a/firmware/common/ak4951.hpp b/firmware/common/ak4951.hpp index 3b1f9e1aa..c90b858df 100644 --- a/firmware/common/ak4951.hpp +++ b/firmware/common/ak4951.hpp @@ -40,228 +40,228 @@ using reg_t = uint8_t; constexpr size_t reg_count = 0x50; enum class Register : address_t { - PowerManagement1 = 0x00, - PowerManagement2 = 0x01, - SignalSelect1 = 0x02, - SignalSelect2 = 0x03, - SignalSelect3 = 0x04, - ModeControl1 = 0x05, - ModeControl2 = 0x06, - ModeControl3 = 0x07, - DigitalMic = 0x08, - TimerSelect = 0x09, - ALCTimerSelect = 0x0a, - ALCModeControl1 = 0x0b, - ALCModeControl2 = 0x0c, - LchInputVolumeControl = 0x0d, - RchInputVolumeControl = 0x0e, - ALCVolume = 0x0f, - _Reserved_0x10 = 0x10, - RchMicGainSetting = 0x11, - BeepControl = 0x12, - LchDigitalVolumeControl = 0x13, - RchDigitalVolumeControl = 0x14, - EQCommonGainSelect = 0x15, - EQ2CommonGainSetting = 0x16, - EQ3CommonGainSetting = 0x17, - EQ4CommonGainSetting = 0x18, - EQ5CommonGainSetting = 0x19, - AutoHPFControl = 0x1a, - DigitalFilterSelect1 = 0x1b, - DigitalFilterSelect2 = 0x1c, - DigitalFilterMode = 0x1d, - HPF2Coefficient0 = 0x1e, - HPF2Coefficient1 = 0x1f, - HPF2Coefficient2 = 0x20, - HPF2Coefficient3 = 0x21, - LPFCoefficient0 = 0x22, - LPFCoefficient1 = 0x23, - LPFCoefficient2 = 0x24, - LPFCoefficient3 = 0x25, - FIL3Coefficient0 = 0x26, - FIL3Coefficient1 = 0x27, - FIL3Coefficient2 = 0x28, - FIL3Coefficient3 = 0x29, - EQCoefficient0 = 0x2a, - EQCoefficient1 = 0x2b, - EQCoefficient2 = 0x2c, - EQCoefficient3 = 0x2d, - EQCoefficient4 = 0x2e, - EQCoefficient5 = 0x2f, - DigitalFilterSelect3 = 0x30, - DeviceInformation = 0x31, - E1Coefficient0 = 0x32, - E1Coefficient1 = 0x33, - E1Coefficient2 = 0x34, - E1Coefficient3 = 0x35, - E1Coefficient4 = 0x36, - E1Coefficient5 = 0x37, - E2Coefficient0 = 0x38, - E2Coefficient1 = 0x39, - E2Coefficient2 = 0x3a, - E2Coefficient3 = 0x3b, - E2Coefficient4 = 0x3c, - E2Coefficient5 = 0x3d, - E3Coefficient0 = 0x3e, - E3Coefficient1 = 0x3f, - E3Coefficient2 = 0x40, - E3Coefficient3 = 0x41, - E3Coefficient4 = 0x42, - E3Coefficient5 = 0x43, - E4Coefficient0 = 0x44, - E4Coefficient1 = 0x45, - E4Coefficient2 = 0x46, - E4Coefficient3 = 0x47, - E4Coefficient4 = 0x48, - E4Coefficient5 = 0x49, - E5Coefficient0 = 0x4a, - E5Coefficient1 = 0x4b, - E5Coefficient2 = 0x4c, - E5Coefficient3 = 0x4d, - E5Coefficient4 = 0x4e, - E5Coefficient5 = 0x4f, - _count, + PowerManagement1 = 0x00, + PowerManagement2 = 0x01, + SignalSelect1 = 0x02, + SignalSelect2 = 0x03, + SignalSelect3 = 0x04, + ModeControl1 = 0x05, + ModeControl2 = 0x06, + ModeControl3 = 0x07, + DigitalMic = 0x08, + TimerSelect = 0x09, + ALCTimerSelect = 0x0a, + ALCModeControl1 = 0x0b, + ALCModeControl2 = 0x0c, + LchInputVolumeControl = 0x0d, + RchInputVolumeControl = 0x0e, + ALCVolume = 0x0f, + _Reserved_0x10 = 0x10, + RchMicGainSetting = 0x11, + BeepControl = 0x12, + LchDigitalVolumeControl = 0x13, + RchDigitalVolumeControl = 0x14, + EQCommonGainSelect = 0x15, + EQ2CommonGainSetting = 0x16, + EQ3CommonGainSetting = 0x17, + EQ4CommonGainSetting = 0x18, + EQ5CommonGainSetting = 0x19, + AutoHPFControl = 0x1a, + DigitalFilterSelect1 = 0x1b, + DigitalFilterSelect2 = 0x1c, + DigitalFilterMode = 0x1d, + HPF2Coefficient0 = 0x1e, + HPF2Coefficient1 = 0x1f, + HPF2Coefficient2 = 0x20, + HPF2Coefficient3 = 0x21, + LPFCoefficient0 = 0x22, + LPFCoefficient1 = 0x23, + LPFCoefficient2 = 0x24, + LPFCoefficient3 = 0x25, + FIL3Coefficient0 = 0x26, + FIL3Coefficient1 = 0x27, + FIL3Coefficient2 = 0x28, + FIL3Coefficient3 = 0x29, + EQCoefficient0 = 0x2a, + EQCoefficient1 = 0x2b, + EQCoefficient2 = 0x2c, + EQCoefficient3 = 0x2d, + EQCoefficient4 = 0x2e, + EQCoefficient5 = 0x2f, + DigitalFilterSelect3 = 0x30, + DeviceInformation = 0x31, + E1Coefficient0 = 0x32, + E1Coefficient1 = 0x33, + E1Coefficient2 = 0x34, + E1Coefficient3 = 0x35, + E1Coefficient4 = 0x36, + E1Coefficient5 = 0x37, + E2Coefficient0 = 0x38, + E2Coefficient1 = 0x39, + E2Coefficient2 = 0x3a, + E2Coefficient3 = 0x3b, + E2Coefficient4 = 0x3c, + E2Coefficient5 = 0x3d, + E3Coefficient0 = 0x3e, + E3Coefficient1 = 0x3f, + E3Coefficient2 = 0x40, + E3Coefficient3 = 0x41, + E3Coefficient4 = 0x42, + E3Coefficient5 = 0x43, + E4Coefficient0 = 0x44, + E4Coefficient1 = 0x45, + E4Coefficient2 = 0x46, + E4Coefficient3 = 0x47, + E4Coefficient4 = 0x48, + E4Coefficient5 = 0x49, + E5Coefficient0 = 0x4a, + E5Coefficient1 = 0x4b, + E5Coefficient2 = 0x4c, + E5Coefficient3 = 0x4d, + E5Coefficient4 = 0x4e, + E5Coefficient5 = 0x4f, + _count, }; static_assert(toUType(Register::_count) == reg_count, "Register::_count != reg_count"); struct PowerManagement1 { - reg_t PMADL : 1; - reg_t PMADR : 1; - reg_t PMDAC : 1; - reg_t reserved0 : 2; - reg_t PMBP : 1; - reg_t PMVCM : 1; - reg_t PMPFIL : 1; + reg_t PMADL : 1; + reg_t PMADR : 1; + reg_t PMDAC : 1; + reg_t reserved0 : 2; + reg_t PMBP : 1; + reg_t PMVCM : 1; + reg_t PMPFIL : 1; }; static_assert(sizeof(PowerManagement1) == sizeof(reg_t), "wrong size `struct"); struct PowerManagement2 { - reg_t LOSEL : 1; - reg_t PMSL : 1; - reg_t PMPLL : 1; - reg_t MS : 1; - reg_t PMHPL : 1; - reg_t PMHPR : 1; - reg_t reserved0 : 1; - reg_t PMOSC : 1; + reg_t LOSEL : 1; + reg_t PMSL : 1; + reg_t PMPLL : 1; + reg_t MS : 1; + reg_t PMHPL : 1; + reg_t PMHPR : 1; + reg_t reserved0 : 1; + reg_t PMOSC : 1; }; static_assert(sizeof(PowerManagement2) == sizeof(reg_t), "wrong size struct"); struct SignalSelect1 { - reg_t MGAIN20 : 3; - reg_t PMMP : 1; - reg_t MPSEL : 1; - reg_t DACS : 1; - reg_t MGAIN3 : 1; - reg_t SLPSN : 1; + reg_t MGAIN20 : 3; + reg_t PMMP : 1; + reg_t MPSEL : 1; + reg_t DACS : 1; + reg_t MGAIN3 : 1; + reg_t SLPSN : 1; }; static_assert(sizeof(SignalSelect1) == sizeof(reg_t), "wrong size struct"); struct SignalSelect2 { - reg_t INR : 2; - reg_t INL : 2; - reg_t MICL : 1; - reg_t reserved0 : 1; - reg_t SPKG : 2; + reg_t INR : 2; + reg_t INL : 2; + reg_t MICL : 1; + reg_t reserved0 : 1; + reg_t SPKG : 2; }; static_assert(sizeof(SignalSelect2) == sizeof(reg_t), "wrong size struct"); struct SignalSelect3 { - reg_t MONO : 2; - reg_t PTS : 2; - reg_t reserved0 : 1; - reg_t DACL : 1; - reg_t LVCM : 2; + reg_t MONO : 2; + reg_t PTS : 2; + reg_t reserved0 : 1; + reg_t DACL : 1; + reg_t LVCM : 2; }; static_assert(sizeof(SignalSelect3) == sizeof(reg_t), "wrong size struct"); struct ModeControl1 { - reg_t DIF : 2; - reg_t CKOFF : 1; - reg_t BCKO : 1; - reg_t PLL : 4; + reg_t DIF : 2; + reg_t CKOFF : 1; + reg_t BCKO : 1; + reg_t PLL : 4; }; static_assert(sizeof(ModeControl1) == sizeof(reg_t), "wrong size struct"); struct ModeControl2 { - reg_t FS : 4; - reg_t reserved0 : 2; - reg_t CM : 2; + reg_t FS : 4; + reg_t reserved0 : 2; + reg_t CM : 2; }; static_assert(sizeof(ModeControl2) == sizeof(reg_t), "wrong size struct"); struct ModeControl3 { - reg_t reserved0 : 2; - reg_t IVOLC : 1; - reg_t reserved1 : 1; - reg_t DVOLC : 1; - reg_t SMUTE : 1; - reg_t THDET : 1; - reg_t TSDSEL : 1; + reg_t reserved0 : 2; + reg_t IVOLC : 1; + reg_t reserved1 : 1; + reg_t DVOLC : 1; + reg_t SMUTE : 1; + reg_t THDET : 1; + reg_t TSDSEL : 1; }; static_assert(sizeof(ModeControl3) == sizeof(reg_t), "wrong size struct"); struct DigitalMIC { - reg_t DMIC : 1; - reg_t DCLKP : 1; - reg_t reserved0 : 1; - reg_t DCLKE : 1; - reg_t PMDML : 1; - reg_t PMDMR : 1; - reg_t reserved1 : 1; - reg_t READ : 1; + reg_t DMIC : 1; + reg_t DCLKP : 1; + reg_t reserved0 : 1; + reg_t DCLKE : 1; + reg_t PMDML : 1; + reg_t PMDMR : 1; + reg_t reserved1 : 1; + reg_t READ : 1; }; static_assert(sizeof(DigitalMIC) == sizeof(reg_t), "wrong size struct"); struct TimerSelect { - reg_t DVTM : 1; - reg_t MOFF : 1; - reg_t reserved0 : 2; - reg_t FRN : 1; - reg_t FRATT : 1; - reg_t ADRST : 2; + reg_t DVTM : 1; + reg_t MOFF : 1; + reg_t reserved0 : 2; + reg_t FRN : 1; + reg_t FRATT : 1; + reg_t ADRST : 2; }; static_assert(sizeof(TimerSelect) == sizeof(reg_t), "wrong size struct"); struct ALCTimerSelect { - reg_t RFST : 2; - reg_t WTM : 2; - reg_t EQFC : 2; - reg_t IVTM : 1; - reg_t reserved0 : 1; + reg_t RFST : 2; + reg_t WTM : 2; + reg_t EQFC : 2; + reg_t IVTM : 1; + reg_t reserved0 : 1; }; static_assert(sizeof(ALCTimerSelect) == sizeof(reg_t), "wrong size struct"); struct ALCModeControl1 { - reg_t LMTH10 : 2; - reg_t RGAIN : 3; - reg_t ALC : 1; - reg_t LMTH2 : 1; - reg_t ALCEQN : 1; + reg_t LMTH10 : 2; + reg_t RGAIN : 3; + reg_t ALC : 1; + reg_t LMTH2 : 1; + reg_t ALCEQN : 1; }; static_assert(sizeof(ALCModeControl1) == sizeof(reg_t), "wrong size struct"); struct ALCModeControl2 { - reg_t REF : 8; + reg_t REF : 8; }; static_assert(sizeof(ALCModeControl2) == sizeof(reg_t), "wrong size struct"); struct InputVolumeControl { - reg_t IV : 8; + reg_t IV : 8; }; static_assert(sizeof(InputVolumeControl) == sizeof(reg_t), "wrong size struct"); @@ -270,29 +270,29 @@ using LchInputVolumeControl = InputVolumeControl; using RchInputVolumeControl = InputVolumeControl; struct ALCVolume { - reg_t VOL : 8; + reg_t VOL : 8; }; static_assert(sizeof(ALCVolume) == sizeof(reg_t), "wrong size struct"); struct RchMICGainSetting { - reg_t MGR : 8; + reg_t MGR : 8; }; static_assert(sizeof(RchMICGainSetting) == sizeof(reg_t), "wrong size struct"); struct BeepControl { - reg_t BPLVL : 4; - reg_t BEEPH : 1; - reg_t BEEPS : 1; - reg_t BPVCM : 1; - reg_t HPZ : 1; + reg_t BPLVL : 4; + reg_t BEEPH : 1; + reg_t BEEPS : 1; + reg_t BPVCM : 1; + reg_t HPZ : 1; }; static_assert(sizeof(BeepControl) == sizeof(reg_t), "wrong size struct"); struct DigitalVolumeControl { - reg_t DV : 8; + reg_t DV : 8; }; static_assert(sizeof(DigitalVolumeControl) == sizeof(reg_t), "wrong size struct"); @@ -301,19 +301,19 @@ using LchDigitalVolumeControl = DigitalVolumeControl; using RchDigitalVolumeControl = DigitalVolumeControl; struct EQCommonGainSelect { - reg_t reserved0 : 1; - reg_t EQC2 : 1; - reg_t EQC3 : 1; - reg_t EQC4 : 1; - reg_t EQC5 : 1; - reg_t reserved1 : 3; + reg_t reserved0 : 1; + reg_t EQC2 : 1; + reg_t EQC3 : 1; + reg_t EQC4 : 1; + reg_t EQC5 : 1; + reg_t reserved1 : 3; }; static_assert(sizeof(EQCommonGainSelect) == sizeof(reg_t), "wrong size struct"); struct EQCommonGainSetting { - reg_t EQnT : 2; - reg_t EQnG : 6; + reg_t EQnT : 2; + reg_t EQnG : 6; }; static_assert(sizeof(EQCommonGainSetting) == sizeof(reg_t), "wrong size struct"); @@ -324,50 +324,50 @@ using EQ4CommonGainSetting = EQCommonGainSetting; using EQ5CommonGainSetting = EQCommonGainSetting; struct AutoHPFControl { - reg_t STG : 2; - reg_t SENC : 3; - reg_t AHPF : 1; - reg_t reserved0 : 2; + reg_t STG : 2; + reg_t SENC : 3; + reg_t AHPF : 1; + reg_t reserved0 : 2; }; static_assert(sizeof(AutoHPFControl) == sizeof(reg_t), "wrong size struct"); struct DigitalFilterSelect1 { - reg_t HPFAD : 1; - reg_t HPFC : 2; - reg_t reserved0 : 5; + reg_t HPFAD : 1; + reg_t HPFC : 2; + reg_t reserved0 : 5; }; static_assert(sizeof(DigitalFilterSelect1) == sizeof(reg_t), "wrong size struct"); struct DigitalFilterSelect2 { - reg_t HPF : 1; - reg_t LPF : 1; - reg_t reserved0 : 2; - reg_t FIL3 : 1; - reg_t EQ0 : 1; - reg_t GN : 2; + reg_t HPF : 1; + reg_t LPF : 1; + reg_t reserved0 : 2; + reg_t FIL3 : 1; + reg_t EQ0 : 1; + reg_t GN : 2; }; static_assert(sizeof(DigitalFilterSelect2) == sizeof(reg_t), "wrong size struct"); struct DigitalFilterMode { - reg_t PFSDO : 1; - reg_t ADCPF : 1; - reg_t PFDAC : 2; - reg_t PFVOL : 2; - reg_t reserved0 : 2; + reg_t PFSDO : 1; + reg_t ADCPF : 1; + reg_t PFDAC : 2; + reg_t PFVOL : 2; + reg_t reserved0 : 2; }; static_assert(sizeof(DigitalFilterMode) == sizeof(reg_t), "wrong size struct"); struct Coefficient14L { - reg_t l : 8; + reg_t l : 8; }; struct Coefficient14H { - reg_t h : 6; - reg_t reserved0 : 2; + reg_t h : 6; + reg_t reserved0 : 2; }; static_assert(sizeof(Coefficient14L) == sizeof(reg_t), "wrong size struct"); @@ -376,7 +376,7 @@ static_assert(sizeof(Coefficient14H) == sizeof(reg_t), "wrong size struct"); using Coefficient16L = Coefficient14L; struct Coefficient16H { - reg_t h : 8; + reg_t h : 8; }; static_assert(sizeof(Coefficient16H) == sizeof(reg_t), "wrong size struct"); @@ -394,9 +394,9 @@ using LPFCoefficient3 = Coefficient14H; using FIL3Coefficient0 = Coefficient14L; struct FIL3Coefficient1 { - reg_t h : 6; - reg_t reserved0 : 1; - reg_t s : 1; + reg_t h : 6; + reg_t reserved0 : 1; + reg_t s : 1; }; static_assert(sizeof(FIL3Coefficient1) == sizeof(reg_t), "wrong size struct"); @@ -412,19 +412,19 @@ using EQCoefficient4 = Coefficient16L; using EQCoefficient5 = Coefficient16H; struct DigitalFilterSelect3 { - reg_t EQ1 : 1; - reg_t EQ2 : 1; - reg_t EQ3 : 1; - reg_t EQ4 : 1; - reg_t EQ5 : 1; - reg_t reserved0 : 3; + reg_t EQ1 : 1; + reg_t EQ2 : 1; + reg_t EQ3 : 1; + reg_t EQ4 : 1; + reg_t EQ5 : 1; + reg_t reserved0 : 3; }; static_assert(sizeof(DigitalFilterSelect3) == sizeof(reg_t), "wrong size struct"); struct DeviceInformation { - reg_t DVN : 4; - reg_t REV : 4; + reg_t DVN : 4; + reg_t REV : 4; }; static_assert(sizeof(DeviceInformation) == sizeof(reg_t), "wrong size struct"); @@ -465,423 +465,421 @@ using E5Coefficient4 = Coefficient16L; using E5Coefficient5 = Coefficient16H; struct Register_Type { - PowerManagement1 power_management_1; - PowerManagement2 power_management_2; - SignalSelect1 signal_select_1; - SignalSelect2 signal_select_2; - SignalSelect3 signal_select_3; - ModeControl1 mode_control_1; - ModeControl2 mode_control_2; - ModeControl3 mode_control_3; - DigitalMIC digital_mic; - TimerSelect timer_select; - ALCTimerSelect alc_timer_select; - ALCModeControl1 alc_mode_control_1; - ALCModeControl2 alc_mode_control_2; - LchInputVolumeControl l_ch_input_volume_control; - RchInputVolumeControl r_ch_input_volume_control; - ALCVolume alc_volume; - reg_t _reserved_0x10; - RchMICGainSetting r_ch_mic_gain_setting; - BeepControl beep_control; - LchDigitalVolumeControl l_ch_digital_volume_control; - RchDigitalVolumeControl r_ch_digital_volume_control; - EQCommonGainSelect eq_common_gain_select; - EQ2CommonGainSetting eq2_common_gain_setting; - EQ3CommonGainSetting eq3_common_gain_setting; - EQ4CommonGainSetting eq4_common_gain_setting; - EQ5CommonGainSetting eq5_common_gain_setting; - AutoHPFControl auto_hpf_control; - DigitalFilterSelect1 digital_filter_select_1; - DigitalFilterSelect2 digital_filter_select_2; - DigitalFilterMode digital_filter_mode; - HPF2Coefficient0 hpf_2_coefficient_0; - HPF2Coefficient1 hpf_2_coefficient_1; - HPF2Coefficient2 hpf_2_coefficient_2; - HPF2Coefficient3 hpf_2_coefficient_3; - LPFCoefficient0 lpf_coefficient_0; - LPFCoefficient1 lpf_coefficient_1; - LPFCoefficient2 lpf_coefficient_2; - LPFCoefficient3 lpf_coefficient_3; - FIL3Coefficient0 fil_3_coefficient_0; - FIL3Coefficient1 fil_3_coefficient_1; - FIL3Coefficient2 fil_3_coefficient_2; - FIL3Coefficient3 fil_3_coefficient_3; - EQCoefficient0 eq_coefficient_0; - EQCoefficient1 eq_coefficient_1; - EQCoefficient2 eq_coefficient_2; - EQCoefficient3 eq_coefficient_3; - EQCoefficient4 eq_coefficient_4; - EQCoefficient5 eq_coefficient_5; - DigitalFilterSelect3 digital_filter_select_3; - DeviceInformation device_information; - E1Coefficient0 e1_coefficient_0; - E1Coefficient1 e1_coefficient_1; - E1Coefficient2 e1_coefficient_2; - E1Coefficient3 e1_coefficient_3; - E1Coefficient4 e1_coefficient_4; - E1Coefficient5 e1_coefficient_5; - E2Coefficient0 e2_coefficient_0; - E2Coefficient1 e2_coefficient_1; - E2Coefficient2 e2_coefficient_2; - E2Coefficient3 e2_coefficient_3; - E2Coefficient4 e2_coefficient_4; - E2Coefficient5 e2_coefficient_5; - E3Coefficient0 e3_coefficient_0; - E3Coefficient1 e3_coefficient_1; - E3Coefficient2 e3_coefficient_2; - E3Coefficient3 e3_coefficient_3; - E3Coefficient4 e3_coefficient_4; - E3Coefficient5 e3_coefficient_5; - E4Coefficient0 e4_coefficient_0; - E4Coefficient1 e4_coefficient_1; - E4Coefficient2 e4_coefficient_2; - E4Coefficient3 e4_coefficient_3; - E4Coefficient4 e4_coefficient_4; - E4Coefficient5 e4_coefficient_5; - E5Coefficient0 e5_coefficient_0; - E5Coefficient1 e5_coefficient_1; - E5Coefficient2 e5_coefficient_2; - E5Coefficient3 e5_coefficient_3; - E5Coefficient4 e5_coefficient_4; - E5Coefficient5 e5_coefficient_5; + PowerManagement1 power_management_1; + PowerManagement2 power_management_2; + SignalSelect1 signal_select_1; + SignalSelect2 signal_select_2; + SignalSelect3 signal_select_3; + ModeControl1 mode_control_1; + ModeControl2 mode_control_2; + ModeControl3 mode_control_3; + DigitalMIC digital_mic; + TimerSelect timer_select; + ALCTimerSelect alc_timer_select; + ALCModeControl1 alc_mode_control_1; + ALCModeControl2 alc_mode_control_2; + LchInputVolumeControl l_ch_input_volume_control; + RchInputVolumeControl r_ch_input_volume_control; + ALCVolume alc_volume; + reg_t _reserved_0x10; + RchMICGainSetting r_ch_mic_gain_setting; + BeepControl beep_control; + LchDigitalVolumeControl l_ch_digital_volume_control; + RchDigitalVolumeControl r_ch_digital_volume_control; + EQCommonGainSelect eq_common_gain_select; + EQ2CommonGainSetting eq2_common_gain_setting; + EQ3CommonGainSetting eq3_common_gain_setting; + EQ4CommonGainSetting eq4_common_gain_setting; + EQ5CommonGainSetting eq5_common_gain_setting; + AutoHPFControl auto_hpf_control; + DigitalFilterSelect1 digital_filter_select_1; + DigitalFilterSelect2 digital_filter_select_2; + DigitalFilterMode digital_filter_mode; + HPF2Coefficient0 hpf_2_coefficient_0; + HPF2Coefficient1 hpf_2_coefficient_1; + HPF2Coefficient2 hpf_2_coefficient_2; + HPF2Coefficient3 hpf_2_coefficient_3; + LPFCoefficient0 lpf_coefficient_0; + LPFCoefficient1 lpf_coefficient_1; + LPFCoefficient2 lpf_coefficient_2; + LPFCoefficient3 lpf_coefficient_3; + FIL3Coefficient0 fil_3_coefficient_0; + FIL3Coefficient1 fil_3_coefficient_1; + FIL3Coefficient2 fil_3_coefficient_2; + FIL3Coefficient3 fil_3_coefficient_3; + EQCoefficient0 eq_coefficient_0; + EQCoefficient1 eq_coefficient_1; + EQCoefficient2 eq_coefficient_2; + EQCoefficient3 eq_coefficient_3; + EQCoefficient4 eq_coefficient_4; + EQCoefficient5 eq_coefficient_5; + DigitalFilterSelect3 digital_filter_select_3; + DeviceInformation device_information; + E1Coefficient0 e1_coefficient_0; + E1Coefficient1 e1_coefficient_1; + E1Coefficient2 e1_coefficient_2; + E1Coefficient3 e1_coefficient_3; + E1Coefficient4 e1_coefficient_4; + E1Coefficient5 e1_coefficient_5; + E2Coefficient0 e2_coefficient_0; + E2Coefficient1 e2_coefficient_1; + E2Coefficient2 e2_coefficient_2; + E2Coefficient3 e2_coefficient_3; + E2Coefficient4 e2_coefficient_4; + E2Coefficient5 e2_coefficient_5; + E3Coefficient0 e3_coefficient_0; + E3Coefficient1 e3_coefficient_1; + E3Coefficient2 e3_coefficient_2; + E3Coefficient3 e3_coefficient_3; + E3Coefficient4 e3_coefficient_4; + E3Coefficient5 e3_coefficient_5; + E4Coefficient0 e4_coefficient_0; + E4Coefficient1 e4_coefficient_1; + E4Coefficient2 e4_coefficient_2; + E4Coefficient3 e4_coefficient_3; + E4Coefficient4 e4_coefficient_4; + E4Coefficient5 e4_coefficient_5; + E5Coefficient0 e5_coefficient_0; + E5Coefficient1 e5_coefficient_1; + E5Coefficient2 e5_coefficient_2; + E5Coefficient3 e5_coefficient_3; + E5Coefficient4 e5_coefficient_4; + E5Coefficient5 e5_coefficient_5; }; static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size"); struct RegisterMap { - constexpr RegisterMap( - Register_Type values - ) : r(values) - { - } + constexpr RegisterMap( + Register_Type values) + : r(values) { + } - union { - Register_Type r; - std::array w; - }; + union { + Register_Type r; + std::array w; + }; }; static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size"); -constexpr RegisterMap default_after_reset { Register_Type { - .power_management_1 = { - .PMADL = 0, - .PMADR = 0, - .PMDAC = 0, - .reserved0 = 0, - .PMBP = 0, - .PMVCM = 0, - .PMPFIL = 0, - }, - .power_management_2 = { - .LOSEL = 0, - .PMSL = 0, - .PMPLL = 0, - .MS = 0, - .PMHPL = 0, - .PMHPR = 0, - .reserved0 = 0, - .PMOSC = 0, - }, - .signal_select_1 = { - .MGAIN20 = 0b110, - .PMMP = 0, - .MPSEL = 0, - .DACS = 0, - .MGAIN3 = 0, - .SLPSN = 0, - }, - .signal_select_2 = { - .INR = 0b00, - .INL = 0b00, - .MICL = 0, - .reserved0 = 0, - .SPKG = 0b00, - }, - .signal_select_3 = { - .MONO = 0b00, - .PTS = 0b01, - .reserved0 = 0, - .DACL = 0, - .LVCM = 0b01, - }, - .mode_control_1 = { - .DIF = 0b10, - .CKOFF = 0, - .BCKO = 0, - .PLL = 0b0101, - }, - .mode_control_2 = { - .FS = 0b1011, - .reserved0 = 0, - .CM = 0b00, - }, - .mode_control_3 = { - .reserved0 = 0, - .IVOLC = 1, - .reserved1 = 0, - .DVOLC = 1, - .SMUTE = 0, - .THDET = 0, - .TSDSEL = 0, - }, - .digital_mic = { - .DMIC = 0, - .DCLKP = 0, - .reserved0 = 0, - .DCLKE = 0, - .PMDML = 0, - .PMDMR = 1, - .reserved1 = 0, - .READ = 0, - }, - .timer_select = { - .DVTM = 0, - .MOFF = 0, - .reserved0 = 0, - .FRN = 0, - .FRATT = 0, - .ADRST = 0b00, - }, - .alc_timer_select = { - .RFST = 0b00, - .WTM = 0b00, - .EQFC = 0b10, - .IVTM = 1, - .reserved0 = 0, - }, - .alc_mode_control_1 = { - .LMTH10 = 0b00, - .RGAIN = 0b000, - .ALC = 0, - .LMTH2 = 0, - .ALCEQN = 0, - }, - .alc_mode_control_2 = { - .REF = 0xe1, - }, - .l_ch_input_volume_control = { - .IV = 0xe1, - }, - .r_ch_input_volume_control = { - .IV = 0xe1, - }, - .alc_volume = { - .VOL = 0x00, // Read-only. - }, - ._reserved_0x10 = 0x80, - .r_ch_mic_gain_setting = { - .MGR = 0x80, - }, - .beep_control = { - .BPLVL = 0b0000, - .BEEPH = 0, - .BEEPS = 0, - .BPVCM = 0, - .HPZ = 0, - }, - .l_ch_digital_volume_control = { - .DV = 0x18, - }, - .r_ch_digital_volume_control = { - .DV = 0x18, - }, - .eq_common_gain_select = { - .reserved0 = 0, - .EQC2 = 0, - .EQC3 = 0, - .EQC4 = 0, - .EQC5 = 0, - .reserved1 = 0, - }, - .eq2_common_gain_setting = { - .EQnT = 0b00, - .EQnG = 0b000000, - }, - .eq3_common_gain_setting = { - .EQnT = 0b00, - .EQnG = 0b000000, - }, - .eq4_common_gain_setting = { - .EQnT = 0b00, - .EQnG = 0b000000, - }, - .eq5_common_gain_setting = { - .EQnT = 0b00, - .EQnG = 0b000000, - }, - .auto_hpf_control = { - .STG = 0b00, - .SENC = 0b011, - .AHPF = 0, - .reserved0 = 0, - }, - .digital_filter_select_1 = { - .HPFAD = 1, - .HPFC = 0b00, - .reserved0 = 0, - }, - .digital_filter_select_2 = { - .HPF = 0, - .LPF = 0, - .reserved0 = 0, - .FIL3 = 0, - .EQ0 = 0, - .GN = 0b00, - }, - .digital_filter_mode = { - .PFSDO = 1, - .ADCPF = 1, - .PFDAC = 0b00, - .PFVOL = 0b00, - .reserved0 = 0, - }, - - .hpf_2_coefficient_0 = { .l = 0xb0 }, - .hpf_2_coefficient_1 = { .h = 0x1f, .reserved0 = 0 }, - .hpf_2_coefficient_2 = { .l = 0x9f }, - .hpf_2_coefficient_3 = { .h = 0x20, .reserved0 = 0 }, - - .lpf_coefficient_0 = { .l = 0x00 }, - .lpf_coefficient_1 = { .h = 0x00, .reserved0 = 0 }, - .lpf_coefficient_2 = { .l = 0x00 }, - .lpf_coefficient_3 = { .h = 0x00, .reserved0 = 0 }, - - .fil_3_coefficient_0 = { .l = 0x00 }, - .fil_3_coefficient_1 = { .h = 0x00, .reserved0 = 0, .s = 0 }, - .fil_3_coefficient_2 = { .l = 0x00 }, - .fil_3_coefficient_3 = { .h = 0x00, .reserved0 = 0 }, - - .eq_coefficient_0 = { .l = 0x00 }, - .eq_coefficient_1 = { .h = 0x00 }, - .eq_coefficient_2 = { .l = 0x00 }, - .eq_coefficient_3 = { .h = 0x00, .reserved0 = 0 }, - .eq_coefficient_4 = { .l = 0x00 }, - .eq_coefficient_5 = { .h = 0x00 }, - - .digital_filter_select_3 = { - .EQ1 = 0, - .EQ2 = 0, - .EQ3 = 0, - .EQ4 = 0, - .EQ5 = 0, - .reserved0 = 0, - }, - .device_information = { - .DVN = 0b0001, - .REV = 0b1100, - }, - - // just pre-loading into memory, 30 bytes = EQ 1,2,3,4,5 x A,B,C (2 x bytes) coefficients, but it will be written from ak4951.cpp - .e1_coefficient_0 = { .l = 0xCA }, //EQ1 Coefficient A : A7...A0, BW : 300Hz - 1700Hz (fo = 1150Hz , fb= 1700Hz) , k=1,8 peaking - .e1_coefficient_1 = { .h = 0x05 }, //EQ1 Coefficient A : A15..A8 - .e1_coefficient_2 = { .l = 0xEB }, //EQ1 Coefficient B : B7...B0 - .e1_coefficient_3 = { .h = 0x38 }, //EQ1 Coefficient B : B15...B8 - .e1_coefficient_4 = { .l = 0x6F }, //EQ1 Coefficient C : C7...C0 - .e1_coefficient_5 = { .h = 0xE6 }, //EQ1 Coefficient C : C15..C8 - - .e2_coefficient_0 = { .l = 0x05 }, //EQ2 Coefficient A : A7...A0, BW : 250Hz - 2700Hz (fo = 1475Hz , fb= 2450Hz) , k=1,8 peaking - .e2_coefficient_1 = { .h = 0x08 }, //EQ2 Coefficient A : A15..A8 - .e2_coefficient_2 = { .l = 0x11 }, //EQ2 Coefficient B : B7...B0 - .e2_coefficient_3 = { .h = 0x36 }, //EQ2 Coefficient B : B15...B8 - .e2_coefficient_4 = { .l = 0xE9 }, //EQ2 Coefficient C : C7...C0 - .e2_coefficient_5 = { .h = 0xE8 }, //EQ2 Coefficient C : C15..C8 - - .e3_coefficient_0 = { .l = 0x00 }, //EQ3 Coefficient A : A7...A0, not used currently - .e3_coefficient_1 = { .h = 0x00 }, //EQ3 Coefficient A : A15..A8 - .e3_coefficient_2 = { .l = 0x00 }, //EQ3 Coefficient B : B7...B0 - .e3_coefficient_3 = { .h = 0x00 }, //EQ3 Coefficient B : B15...B8 - .e3_coefficient_4 = { .l = 0x00 }, //EQ3 Coefficient C : C7...C0 - .e3_coefficient_5 = { .h = 0x00 }, //EQ3 Coefficient C : C15..C8 - - .e4_coefficient_0 = { .l = 0x00 }, //EQ4 Coefficient A : A7...A0, not used currently - .e4_coefficient_1 = { .h = 0x00 }, //EQ4 Coefficient A : A15..A8 - .e4_coefficient_2 = { .l = 0x00 }, //EQ4 Coefficient B : B7...B0 - .e4_coefficient_3 = { .h = 0x00 }, //EQ4 Coefficient B : B15...B8 - .e4_coefficient_4 = { .l = 0x00 }, //EQ4 Coefficient C : C7...C0 - .e4_coefficient_5 = { .h = 0x00 }, //EQ4 Coefficient C : C15..C8 - - .e5_coefficient_0 = { .l = 0x00 }, //EQ5 Coefficient A : A7...A0, not used currently - .e5_coefficient_1 = { .h = 0x00 }, //EQ5 Coefficient A : A15..A8 - .e5_coefficient_2 = { .l = 0x00 }, //EQ5 Coefficient B : B7...B0 - .e5_coefficient_3 = { .h = 0x00 }, //EQ5 Coefficient B : B15...B8 - .e5_coefficient_4 = { .l = 0x00 }, //EQ5 Coefficient C : C7...C0 - .e5_coefficient_5 = { .h = 0x00 }, //EQ5 Coefficient C : C15..C8 -} }; - -class AK4951 : public audio::Codec { -public: - constexpr AK4951( - I2C& bus, - const I2C::address_t bus_address - ) : bus(bus), - bus_address(bus_address) - { - } - - std::string name() const override { - return "AK4951"; - } - - bool detected(); - - void init() override; - bool reset() override; - - volume_range_t headphone_gain_range() const override { - return { -89.5_dB, 12.0_dB }; - } - - void headphone_enable() override; - void headphone_disable() override; - - void speaker_enable(); - void speaker_disable(); - - void set_headphone_volume(const volume_t volume) override; - void headphone_mute(); - - void microphone_enable(int8_t alc_mode); // added user GUI parameter , to set up AK4951 ALC mode. - void microphone_disable(); - - size_t reg_count() const override { - return asahi_kasei::ak4951::reg_count; - } - - size_t reg_bits() const override { - return 8; - } - - uint32_t reg_read(const size_t reg_address) override { - return read(reg_address); - } - -private: - I2C& bus; - const I2C::address_t bus_address; - RegisterMap map { default_after_reset }; - - enum class LineOutSelect { - Speaker, - Line, - }; - - void configure_digital_interface_i2s(); - void configure_digital_interface_external_slave(); - void configure_digital_interface_external_master(); - void set_digtal_volume_control(const reg_t value); - void set_dac_power(const bool enable); - void set_headphone_power(const bool enable); - void set_speaker_power(const bool enable); - void select_line_out(const LineOutSelect value); - - reg_t read(const address_t reg_address); - void update(const Register reg); - void write(const address_t reg_address, const reg_t value); +constexpr RegisterMap default_after_reset{Register_Type{ + .power_management_1 = { + .PMADL = 0, + .PMADR = 0, + .PMDAC = 0, + .reserved0 = 0, + .PMBP = 0, + .PMVCM = 0, + .PMPFIL = 0, + }, + .power_management_2 = { + .LOSEL = 0, + .PMSL = 0, + .PMPLL = 0, + .MS = 0, + .PMHPL = 0, + .PMHPR = 0, + .reserved0 = 0, + .PMOSC = 0, + }, + .signal_select_1 = { + .MGAIN20 = 0b110, + .PMMP = 0, + .MPSEL = 0, + .DACS = 0, + .MGAIN3 = 0, + .SLPSN = 0, + }, + .signal_select_2 = { + .INR = 0b00, + .INL = 0b00, + .MICL = 0, + .reserved0 = 0, + .SPKG = 0b00, + }, + .signal_select_3 = { + .MONO = 0b00, + .PTS = 0b01, + .reserved0 = 0, + .DACL = 0, + .LVCM = 0b01, + }, + .mode_control_1 = { + .DIF = 0b10, + .CKOFF = 0, + .BCKO = 0, + .PLL = 0b0101, + }, + .mode_control_2 = { + .FS = 0b1011, + .reserved0 = 0, + .CM = 0b00, + }, + .mode_control_3 = { + .reserved0 = 0, + .IVOLC = 1, + .reserved1 = 0, + .DVOLC = 1, + .SMUTE = 0, + .THDET = 0, + .TSDSEL = 0, + }, + .digital_mic = { + .DMIC = 0, + .DCLKP = 0, + .reserved0 = 0, + .DCLKE = 0, + .PMDML = 0, + .PMDMR = 1, + .reserved1 = 0, + .READ = 0, + }, + .timer_select = { + .DVTM = 0, + .MOFF = 0, + .reserved0 = 0, + .FRN = 0, + .FRATT = 0, + .ADRST = 0b00, + }, + .alc_timer_select = { + .RFST = 0b00, + .WTM = 0b00, + .EQFC = 0b10, + .IVTM = 1, + .reserved0 = 0, + }, + .alc_mode_control_1 = { + .LMTH10 = 0b00, + .RGAIN = 0b000, + .ALC = 0, + .LMTH2 = 0, + .ALCEQN = 0, + }, + .alc_mode_control_2 = { + .REF = 0xe1, + }, + .l_ch_input_volume_control = { + .IV = 0xe1, + }, + .r_ch_input_volume_control = { + .IV = 0xe1, + }, + .alc_volume = { + .VOL = 0x00, // Read-only. + }, + ._reserved_0x10 = 0x80, + .r_ch_mic_gain_setting = { + .MGR = 0x80, + }, + .beep_control = { + .BPLVL = 0b0000, + .BEEPH = 0, + .BEEPS = 0, + .BPVCM = 0, + .HPZ = 0, + }, + .l_ch_digital_volume_control = { + .DV = 0x18, + }, + .r_ch_digital_volume_control = { + .DV = 0x18, + }, + .eq_common_gain_select = { + .reserved0 = 0, + .EQC2 = 0, + .EQC3 = 0, + .EQC4 = 0, + .EQC5 = 0, + .reserved1 = 0, + }, + .eq2_common_gain_setting = { + .EQnT = 0b00, + .EQnG = 0b000000, + }, + .eq3_common_gain_setting = { + .EQnT = 0b00, + .EQnG = 0b000000, + }, + .eq4_common_gain_setting = { + .EQnT = 0b00, + .EQnG = 0b000000, + }, + .eq5_common_gain_setting = { + .EQnT = 0b00, + .EQnG = 0b000000, + }, + .auto_hpf_control = { + .STG = 0b00, + .SENC = 0b011, + .AHPF = 0, + .reserved0 = 0, + }, + .digital_filter_select_1 = { + .HPFAD = 1, + .HPFC = 0b00, + .reserved0 = 0, + }, + .digital_filter_select_2 = { + .HPF = 0, + .LPF = 0, + .reserved0 = 0, + .FIL3 = 0, + .EQ0 = 0, + .GN = 0b00, + }, + .digital_filter_mode = { + .PFSDO = 1, + .ADCPF = 1, + .PFDAC = 0b00, + .PFVOL = 0b00, + .reserved0 = 0, + }, + + .hpf_2_coefficient_0 = {.l = 0xb0}, + .hpf_2_coefficient_1 = {.h = 0x1f, .reserved0 = 0}, + .hpf_2_coefficient_2 = {.l = 0x9f}, + .hpf_2_coefficient_3 = {.h = 0x20, .reserved0 = 0}, + + .lpf_coefficient_0 = {.l = 0x00}, + .lpf_coefficient_1 = {.h = 0x00, .reserved0 = 0}, + .lpf_coefficient_2 = {.l = 0x00}, + .lpf_coefficient_3 = {.h = 0x00, .reserved0 = 0}, + + .fil_3_coefficient_0 = {.l = 0x00}, + .fil_3_coefficient_1 = {.h = 0x00, .reserved0 = 0, .s = 0}, + .fil_3_coefficient_2 = {.l = 0x00}, + .fil_3_coefficient_3 = {.h = 0x00, .reserved0 = 0}, + + .eq_coefficient_0 = {.l = 0x00}, + .eq_coefficient_1 = {.h = 0x00}, + .eq_coefficient_2 = {.l = 0x00}, + .eq_coefficient_3 = {.h = 0x00, .reserved0 = 0}, + .eq_coefficient_4 = {.l = 0x00}, + .eq_coefficient_5 = {.h = 0x00}, + + .digital_filter_select_3 = { + .EQ1 = 0, + .EQ2 = 0, + .EQ3 = 0, + .EQ4 = 0, + .EQ5 = 0, + .reserved0 = 0, + }, + .device_information = { + .DVN = 0b0001, + .REV = 0b1100, + }, + + // just pre-loading into memory, 30 bytes = EQ 1,2,3,4,5 x A,B,C (2 x bytes) coefficients, but it will be written from ak4951.cpp + .e1_coefficient_0 = {.l = 0xCA}, // EQ1 Coefficient A : A7...A0, BW : 300Hz - 1700Hz (fo = 1150Hz , fb= 1700Hz) , k=1,8 peaking + .e1_coefficient_1 = {.h = 0x05}, // EQ1 Coefficient A : A15..A8 + .e1_coefficient_2 = {.l = 0xEB}, // EQ1 Coefficient B : B7...B0 + .e1_coefficient_3 = {.h = 0x38}, // EQ1 Coefficient B : B15...B8 + .e1_coefficient_4 = {.l = 0x6F}, // EQ1 Coefficient C : C7...C0 + .e1_coefficient_5 = {.h = 0xE6}, // EQ1 Coefficient C : C15..C8 + + .e2_coefficient_0 = {.l = 0x05}, // EQ2 Coefficient A : A7...A0, BW : 250Hz - 2700Hz (fo = 1475Hz , fb= 2450Hz) , k=1,8 peaking + .e2_coefficient_1 = {.h = 0x08}, // EQ2 Coefficient A : A15..A8 + .e2_coefficient_2 = {.l = 0x11}, // EQ2 Coefficient B : B7...B0 + .e2_coefficient_3 = {.h = 0x36}, // EQ2 Coefficient B : B15...B8 + .e2_coefficient_4 = {.l = 0xE9}, // EQ2 Coefficient C : C7...C0 + .e2_coefficient_5 = {.h = 0xE8}, // EQ2 Coefficient C : C15..C8 + + .e3_coefficient_0 = {.l = 0x00}, // EQ3 Coefficient A : A7...A0, not used currently + .e3_coefficient_1 = {.h = 0x00}, // EQ3 Coefficient A : A15..A8 + .e3_coefficient_2 = {.l = 0x00}, // EQ3 Coefficient B : B7...B0 + .e3_coefficient_3 = {.h = 0x00}, // EQ3 Coefficient B : B15...B8 + .e3_coefficient_4 = {.l = 0x00}, // EQ3 Coefficient C : C7...C0 + .e3_coefficient_5 = {.h = 0x00}, // EQ3 Coefficient C : C15..C8 + + .e4_coefficient_0 = {.l = 0x00}, // EQ4 Coefficient A : A7...A0, not used currently + .e4_coefficient_1 = {.h = 0x00}, // EQ4 Coefficient A : A15..A8 + .e4_coefficient_2 = {.l = 0x00}, // EQ4 Coefficient B : B7...B0 + .e4_coefficient_3 = {.h = 0x00}, // EQ4 Coefficient B : B15...B8 + .e4_coefficient_4 = {.l = 0x00}, // EQ4 Coefficient C : C7...C0 + .e4_coefficient_5 = {.h = 0x00}, // EQ4 Coefficient C : C15..C8 + + .e5_coefficient_0 = {.l = 0x00}, // EQ5 Coefficient A : A7...A0, not used currently + .e5_coefficient_1 = {.h = 0x00}, // EQ5 Coefficient A : A15..A8 + .e5_coefficient_2 = {.l = 0x00}, // EQ5 Coefficient B : B7...B0 + .e5_coefficient_3 = {.h = 0x00}, // EQ5 Coefficient B : B15...B8 + .e5_coefficient_4 = {.l = 0x00}, // EQ5 Coefficient C : C7...C0 + .e5_coefficient_5 = {.h = 0x00}, // EQ5 Coefficient C : C15..C8 +}}; + +class AK4951 : public audio::Codec { + public: + constexpr AK4951( + I2C& bus, + const I2C::address_t bus_address) + : bus(bus), + bus_address(bus_address) { + } + + std::string name() const override { + return "AK4951"; + } + + bool detected(); + + void init() override; + bool reset() override; + + volume_range_t headphone_gain_range() const override { + return {-89.5_dB, 12.0_dB}; + } + + void headphone_enable() override; + void headphone_disable() override; + + void speaker_enable(); + void speaker_disable(); + + void set_headphone_volume(const volume_t volume) override; + void headphone_mute(); + + void microphone_enable(int8_t alc_mode); // added user GUI parameter , to set up AK4951 ALC mode. + void microphone_disable(); + + size_t reg_count() const override { + return asahi_kasei::ak4951::reg_count; + } + + size_t reg_bits() const override { + return 8; + } + + uint32_t reg_read(const size_t reg_address) override { + return read(reg_address); + } + + private: + I2C& bus; + const I2C::address_t bus_address; + RegisterMap map{default_after_reset}; + + enum class LineOutSelect { + Speaker, + Line, + }; + + void configure_digital_interface_i2s(); + void configure_digital_interface_external_slave(); + void configure_digital_interface_external_master(); + void set_digtal_volume_control(const reg_t value); + void set_dac_power(const bool enable); + void set_headphone_power(const bool enable); + void set_speaker_power(const bool enable); + void select_line_out(const LineOutSelect value); + + reg_t read(const address_t reg_address); + void update(const Register reg); + void write(const address_t reg_address, const reg_t value); }; } /* namespace ak4951 */ } /* namespace asahi_kasei */ -#endif/*__AK4951_H__*/ +#endif /*__AK4951_H__*/ diff --git a/firmware/common/aprs_packet.cpp b/firmware/common/aprs_packet.cpp index e7347ca08..ea1103aa3 100644 --- a/firmware/common/aprs_packet.cpp +++ b/firmware/common/aprs_packet.cpp @@ -24,4 +24,4 @@ namespace aprs { -} /* namespace zwave */ +} // namespace aprs diff --git a/firmware/common/aprs_packet.hpp b/firmware/common/aprs_packet.hpp index c7e7c1db4..47bcf3d40 100644 --- a/firmware/common/aprs_packet.hpp +++ b/firmware/common/aprs_packet.hpp @@ -31,465 +31,450 @@ namespace aprs { -const int APRS_MIN_LENGTH = 18; //14 bytes address, control byte and pid. 2 CRC. +const int APRS_MIN_LENGTH = 18; // 14 bytes address, control byte and pid. 2 CRC. -struct aprs_pos{ - float latitude; - float longitude; - uint8_t symbol_code; - uint8_t sym_table_id; +struct aprs_pos { + float latitude; + float longitude; + uint8_t symbol_code; + uint8_t sym_table_id; }; enum ADDRESS_TYPE { - SOURCE, - DESTINATION, - REPEATER + SOURCE, + DESTINATION, + REPEATER }; class APRSPacket { -public: - void set_timestamp(const Timestamp& value) { - timestamp_ = value; - } - - Timestamp timestamp() const { - return timestamp_; - } - - void set(const size_t index, const uint8_t data) { - payload[index] = data; - - if(index + 1 > payload_size){ - payload_size = index + 1; - } - } - - uint32_t operator[](const size_t index) const { - return payload[index]; - } - - - uint8_t size() const { - return payload_size; - } - - void set_valid_checksum(const bool valid) { - valid_checksum = valid; - } - - bool is_valid_checksum() const { - return valid_checksum; - } - - uint64_t get_source(){ - uint64_t source = 0x0; - - for(uint8_t i = SOURCE_START; i < SOURCE_START + ADDRESS_SIZE; i++){ - source |= ( ((uint64_t)payload[i]) << ((i - SOURCE_START) * 8)); - } - - return source; - } - - std::string get_source_formatted(){ - parse_address(SOURCE_START, SOURCE); - return std::string(address_buffer); - } - - std::string get_destination_formatted(){ - parse_address(DESTINATION_START, DESTINATION); - return std::string(address_buffer); - } - - std::string get_digipeaters_formatted(){ - uint8_t position = DIGIPEATER_START; - bool has_more = parse_address(SOURCE_START, REPEATER); - - std::string repeaters = ""; - while(has_more){ - has_more = parse_address(position, REPEATER); - repeaters += std::string(address_buffer); - - position += ADDRESS_SIZE; - - if(has_more){ - repeaters += ">"; - } - } - - return repeaters; - } - - uint8_t get_number_of_digipeaters(){ - uint8_t position = DIGIPEATER_START; - bool has_more = parse_address(SOURCE_START, REPEATER); - uint8_t repeaters = 0; - while(has_more){ - has_more = parse_address(position, REPEATER); - position += ADDRESS_SIZE; - repeaters++; - } - - return repeaters; - } - - uint8_t get_information_start_index(){ - return DIGIPEATER_START + (get_number_of_digipeaters() * ADDRESS_SIZE) + 2; - } - - std::string get_information_text_formatted(){ - std::string information_text = ""; - for(uint8_t i = get_information_start_index(); i < payload_size - 2; i++){ - information_text += payload[i]; - } - - return information_text; - } - - std::string get_stream_text(){ - std::string stream = get_source_formatted() + ">" + get_destination_formatted() + ";" + get_digipeaters_formatted() + ";" + get_information_text_formatted(); - - return stream; - } - - char get_data_type_identifier(){ - char ident = '\0'; - for(uint8_t i = get_information_start_index(); i < payload_size - 2; i++){ - ident = payload[i]; - break; - } - return ident; - } - - bool has_position(){ - char ident = get_data_type_identifier(); - - return - ident == '!' || - ident == '=' || - ident == '/' || - ident == '@' || - ident == ';' || - ident == '`' || - ident == '\''|| - ident == 0x1d|| - ident == 0x1c; - } - - aprs_pos get_position(){ - aprs::aprs_pos pos; - - char ident = get_data_type_identifier(); - std::string info_text = get_information_text_formatted(); - - std::string lat_str, lng_str; - char first; - //bool supports_compression = true; - bool is_mic_e_format = false; - std::string::size_type start; - - switch(ident){ - case '/': - case '@': - start = 8; - break; - case '=': - case '!': - start = 1; - break; - case ';': - start = 18; - //supports_compression = false; - break; - case '`': - case '\'': - case 0x1c: - case 0x1d: - is_mic_e_format = true; - break; - default: - return pos; - } - if(is_mic_e_format){ - parse_mic_e_format(pos); - } - else { - if(start < info_text.size()){ - first = info_text.at(start); - - if(std::isdigit(first)){ - if(start + 18 < info_text.size()){ - lat_str = info_text.substr(start, 8); - pos.sym_table_id = info_text.at(start + 8); - lng_str = info_text.substr(start + 9, 9); - pos.symbol_code = info_text.at(start + 18); - - pos.latitude = parse_lat_str(lat_str); - pos.longitude = parse_lng_str(lng_str); - } - - } - else { - if(start + 9 < info_text.size()){ - pos.sym_table_id = info_text.at(start); - lat_str = info_text.substr(start + 1, 4); - lng_str = info_text.substr(start + 5, 4); - pos.symbol_code = info_text.at(start + 9); - - pos.latitude = parse_lat_str_cmp(lat_str); - pos.longitude = parse_lng_str_cmp(lng_str); - } - } - } - } - - return pos; - } - - void clear() { - payload_size = 0; - } - -private: - const uint8_t DIGIPEATER_START = 14; - const uint8_t SOURCE_START = 7; - const uint8_t DESTINATION_START = 0; - const uint8_t ADDRESS_SIZE = 7; - - bool valid_checksum = false; - uint8_t payload[256]; - char address_buffer[15]; - uint8_t payload_size = 0 ; - Timestamp timestamp_ { }; - - float parse_lat_str_cmp(const std::string& lat_str){ - return 90.0 - ( (lat_str.at(0) - 33) * (91*91*91) + (lat_str.at(1) - 33) * (91*91) + (lat_str.at(2) - 33) * 91 + (lat_str.at(3)) ) / 380926.0; - } - - float parse_lng_str_cmp(const std::string& lng_str){ - return -180.0 + ( (lng_str.at(0) - 33) * (91*91*91) + (lng_str.at(1) - 33) * (91*91) + (lng_str.at(2) - 33) * 91 + (lng_str.at(3)) ) / 190463.0; - } - - uint8_t parse_digits(const std::string& str){ - if(str.at(0) == ' '){ - return 0; - } - uint8_t end = str.find_last_not_of(' ') + 1; - std::string sub = str.substr(0, end); - - if(!is_digits(sub)){ - return 0; - } - else { - return std::stoi(sub); - } - } - - bool is_digits(const std::string& str){ - return str.find_last_not_of("0123456789") == std::string::npos; - } - - float parse_lat_str(const std::string& lat_str){ - float lat = 0.0; - - std::string str_lat_deg = lat_str.substr(0, 2); - std::string str_lat_min = lat_str.substr(2, 2); - std::string str_lat_hund = lat_str.substr(5, 2); - std::string dir = lat_str.substr(7, 1); - - uint8_t lat_deg = parse_digits(str_lat_deg); - uint8_t lat_min = parse_digits(str_lat_min); - uint8_t lat_hund = parse_digits(str_lat_hund); - - lat += lat_deg; - lat += (lat_min + (lat_hund/ 100.0))/60.0; - - if(dir.c_str()[0] == 'S'){ - lat = -lat; - } - - return lat; - - } - - float parse_lng_str(std::string& lng_str){ - float lng = 0.0; - - std::string str_lng_deg = lng_str.substr(0, 3); - std::string str_lng_min = lng_str.substr(3, 2); - std::string str_lng_hund = lng_str.substr(6, 2); - std::string dir = lng_str.substr(8, 1); - - uint8_t lng_deg = parse_digits(str_lng_deg); - uint8_t lng_min = parse_digits(str_lng_min); - uint8_t lng_hund = parse_digits(str_lng_hund); - - lng += lng_deg; - lng += (lng_min + (lng_hund/ 100.0))/60.0; - - if(dir.c_str()[0] == 'W'){ - lng = -lng; - } - - return lng; - - } - - void parse_mic_e_format(aprs::aprs_pos& pos){ - std::string lat_str = ""; - std::string lng_str = ""; - - bool is_north = false; - bool is_west = false; - uint8_t lng_offset = 0; - for(uint8_t i = DESTINATION_START; i < DESTINATION_START + ADDRESS_SIZE - 1; i++){ - uint8_t ascii = payload[i] >> 1; - - lat_str += get_mic_e_lat_digit(ascii); - - if(i - DESTINATION_START == 3){ - lat_str += "."; - } - if(i - DESTINATION_START == 3){ - is_north = is_mic_e_lat_N(ascii); - } - if(i - DESTINATION_START == 4){ - lng_offset = get_mic_e_lng_offset(ascii); - } - if(i - DESTINATION_START == 5){ - is_west = is_mic_e_lng_W(ascii); - } - } - if(is_north){ - lat_str += "N"; - } - else { - lat_str += "S"; - } - - pos.latitude = parse_lat_str(lat_str); - - float lng = 0.0; - uint8_t information_start = get_information_start_index() + 1; - for(uint8_t i = information_start; i < information_start + 3 && i < payload_size - 2; i++){ - uint8_t ascii = payload[i]; - - if(i - information_start == 0){ //deg - ascii -=28; - ascii += lng_offset; - - if(ascii >= 180 && ascii <= 189){ - ascii -= 80; - } - else if (ascii >= 190 && ascii <= 199){ - ascii -= 190; - } - - lng += ascii; - } - else if(i - information_start == 1){ //min - ascii -= 28; - if(ascii >= 60){ - ascii -= 60; - } - lng += ascii/60.0; - } - else if(i - information_start == 2){ //hundredth minutes - ascii -= 28; - - lng += (ascii/100.0)/60.0; - } - } - - if(is_west){ - lng = -lng; - } - - pos.longitude = lng; - - } - - uint8_t get_mic_e_lat_digit(uint8_t ascii){ - if(ascii >= '0' && ascii <= '9'){ - return ascii; - } - if(ascii >= 'A' && ascii <= 'J'){ - return ascii - 17; - } - if(ascii >= 'P' && ascii <='Y'){ - return ascii - 32; - } - if(ascii == 'K' || ascii == 'L' || ascii == 'Z'){ - return ' '; - } - - return '\0'; - } - - bool is_mic_e_lat_N(uint8_t ascii){ - if(ascii >= 'P' && ascii <='Z'){ - return true; - } - return false; //not technical definition, but the other case is invalid - } - - bool is_mic_e_lng_W(uint8_t ascii){ - if(ascii >= 'P' && ascii <='Z'){ - return true; - } - return false; //not technical definition, but the other case is invalid - } - - uint8_t get_mic_e_lng_offset(uint8_t ascii){ - if(ascii >= 'P' && ascii <='Z'){ - return 100; - } - return 0; //not technical definition, but the other case is invalid - } - - bool parse_address(uint8_t start, ADDRESS_TYPE address_type){ - uint8_t byte = 0; - uint8_t has_more = false; - uint8_t ssid = 0; - uint8_t buffer_index = 0; - - for(uint8_t i = start; i < start + ADDRESS_SIZE && i < payload_size - 2; i++){ - byte = payload[i]; - - if(i - start == 6){ - has_more = (byte & 0x1) == 0; - ssid = (byte >> 1) & 0x0F; - - if(ssid != 0 || address_type == REPEATER){ - address_buffer[buffer_index++] = '-'; - - if(ssid < 10){ - address_buffer[buffer_index++] = '0' + ssid; - address_buffer[buffer_index++] = '\0'; - } - else { - address_buffer[buffer_index++] = '1'; - address_buffer[buffer_index++] = '0' + ssid - 10; - address_buffer[buffer_index++] = '\0'; - } - } - else { - address_buffer[buffer_index++] = '\0'; - } - } - else { - byte >>= 1; - - if(byte != ' '){ - address_buffer[buffer_index++] = byte; - } - } - } - - return has_more; - } + public: + void set_timestamp(const Timestamp& value) { + timestamp_ = value; + } + + Timestamp timestamp() const { + return timestamp_; + } + + void set(const size_t index, const uint8_t data) { + payload[index] = data; + + if (index + 1 > payload_size) { + payload_size = index + 1; + } + } + + uint32_t operator[](const size_t index) const { + return payload[index]; + } + + uint8_t size() const { + return payload_size; + } + + void set_valid_checksum(const bool valid) { + valid_checksum = valid; + } + + bool is_valid_checksum() const { + return valid_checksum; + } + + uint64_t get_source() { + uint64_t source = 0x0; + + for (uint8_t i = SOURCE_START; i < SOURCE_START + ADDRESS_SIZE; i++) { + source |= (((uint64_t)payload[i]) << ((i - SOURCE_START) * 8)); + } + + return source; + } + + std::string get_source_formatted() { + parse_address(SOURCE_START, SOURCE); + return std::string(address_buffer); + } + + std::string get_destination_formatted() { + parse_address(DESTINATION_START, DESTINATION); + return std::string(address_buffer); + } + + std::string get_digipeaters_formatted() { + uint8_t position = DIGIPEATER_START; + bool has_more = parse_address(SOURCE_START, REPEATER); + + std::string repeaters = ""; + while (has_more) { + has_more = parse_address(position, REPEATER); + repeaters += std::string(address_buffer); + + position += ADDRESS_SIZE; + + if (has_more) { + repeaters += ">"; + } + } + + return repeaters; + } + + uint8_t get_number_of_digipeaters() { + uint8_t position = DIGIPEATER_START; + bool has_more = parse_address(SOURCE_START, REPEATER); + uint8_t repeaters = 0; + while (has_more) { + has_more = parse_address(position, REPEATER); + position += ADDRESS_SIZE; + repeaters++; + } + + return repeaters; + } + + uint8_t get_information_start_index() { + return DIGIPEATER_START + (get_number_of_digipeaters() * ADDRESS_SIZE) + 2; + } + + std::string get_information_text_formatted() { + std::string information_text = ""; + for (uint8_t i = get_information_start_index(); i < payload_size - 2; i++) { + information_text += payload[i]; + } + + return information_text; + } + + std::string get_stream_text() { + std::string stream = get_source_formatted() + ">" + get_destination_formatted() + ";" + get_digipeaters_formatted() + ";" + get_information_text_formatted(); + + return stream; + } + + char get_data_type_identifier() { + char ident = '\0'; + for (uint8_t i = get_information_start_index(); i < payload_size - 2; i++) { + ident = payload[i]; + break; + } + return ident; + } + + bool has_position() { + char ident = get_data_type_identifier(); + + return ident == '!' || + ident == '=' || + ident == '/' || + ident == '@' || + ident == ';' || + ident == '`' || + ident == '\'' || + ident == 0x1d || + ident == 0x1c; + } + + aprs_pos get_position() { + aprs::aprs_pos pos; + + char ident = get_data_type_identifier(); + std::string info_text = get_information_text_formatted(); + + std::string lat_str, lng_str; + char first; + // bool supports_compression = true; + bool is_mic_e_format = false; + std::string::size_type start; + + switch (ident) { + case '/': + case '@': + start = 8; + break; + case '=': + case '!': + start = 1; + break; + case ';': + start = 18; + // supports_compression = false; + break; + case '`': + case '\'': + case 0x1c: + case 0x1d: + is_mic_e_format = true; + break; + default: + return pos; + } + if (is_mic_e_format) { + parse_mic_e_format(pos); + } else { + if (start < info_text.size()) { + first = info_text.at(start); + + if (std::isdigit(first)) { + if (start + 18 < info_text.size()) { + lat_str = info_text.substr(start, 8); + pos.sym_table_id = info_text.at(start + 8); + lng_str = info_text.substr(start + 9, 9); + pos.symbol_code = info_text.at(start + 18); + + pos.latitude = parse_lat_str(lat_str); + pos.longitude = parse_lng_str(lng_str); + } + + } else { + if (start + 9 < info_text.size()) { + pos.sym_table_id = info_text.at(start); + lat_str = info_text.substr(start + 1, 4); + lng_str = info_text.substr(start + 5, 4); + pos.symbol_code = info_text.at(start + 9); + + pos.latitude = parse_lat_str_cmp(lat_str); + pos.longitude = parse_lng_str_cmp(lng_str); + } + } + } + } + + return pos; + } + + void clear() { + payload_size = 0; + } + + private: + const uint8_t DIGIPEATER_START = 14; + const uint8_t SOURCE_START = 7; + const uint8_t DESTINATION_START = 0; + const uint8_t ADDRESS_SIZE = 7; + + bool valid_checksum = false; + uint8_t payload[256]; + char address_buffer[15]; + uint8_t payload_size = 0; + Timestamp timestamp_{}; + + float parse_lat_str_cmp(const std::string& lat_str) { + return 90.0 - ((lat_str.at(0) - 33) * (91 * 91 * 91) + (lat_str.at(1) - 33) * (91 * 91) + (lat_str.at(2) - 33) * 91 + (lat_str.at(3))) / 380926.0; + } + + float parse_lng_str_cmp(const std::string& lng_str) { + return -180.0 + ((lng_str.at(0) - 33) * (91 * 91 * 91) + (lng_str.at(1) - 33) * (91 * 91) + (lng_str.at(2) - 33) * 91 + (lng_str.at(3))) / 190463.0; + } + + uint8_t parse_digits(const std::string& str) { + if (str.at(0) == ' ') { + return 0; + } + uint8_t end = str.find_last_not_of(' ') + 1; + std::string sub = str.substr(0, end); + + if (!is_digits(sub)) { + return 0; + } else { + return std::stoi(sub); + } + } + + bool is_digits(const std::string& str) { + return str.find_last_not_of("0123456789") == std::string::npos; + } + + float parse_lat_str(const std::string& lat_str) { + float lat = 0.0; + + std::string str_lat_deg = lat_str.substr(0, 2); + std::string str_lat_min = lat_str.substr(2, 2); + std::string str_lat_hund = lat_str.substr(5, 2); + std::string dir = lat_str.substr(7, 1); + + uint8_t lat_deg = parse_digits(str_lat_deg); + uint8_t lat_min = parse_digits(str_lat_min); + uint8_t lat_hund = parse_digits(str_lat_hund); + + lat += lat_deg; + lat += (lat_min + (lat_hund / 100.0)) / 60.0; + + if (dir.c_str()[0] == 'S') { + lat = -lat; + } + + return lat; + } + + float parse_lng_str(std::string& lng_str) { + float lng = 0.0; + + std::string str_lng_deg = lng_str.substr(0, 3); + std::string str_lng_min = lng_str.substr(3, 2); + std::string str_lng_hund = lng_str.substr(6, 2); + std::string dir = lng_str.substr(8, 1); + + uint8_t lng_deg = parse_digits(str_lng_deg); + uint8_t lng_min = parse_digits(str_lng_min); + uint8_t lng_hund = parse_digits(str_lng_hund); + + lng += lng_deg; + lng += (lng_min + (lng_hund / 100.0)) / 60.0; + + if (dir.c_str()[0] == 'W') { + lng = -lng; + } + + return lng; + } + + void parse_mic_e_format(aprs::aprs_pos& pos) { + std::string lat_str = ""; + std::string lng_str = ""; + + bool is_north = false; + bool is_west = false; + uint8_t lng_offset = 0; + for (uint8_t i = DESTINATION_START; i < DESTINATION_START + ADDRESS_SIZE - 1; i++) { + uint8_t ascii = payload[i] >> 1; + + lat_str += get_mic_e_lat_digit(ascii); + + if (i - DESTINATION_START == 3) { + lat_str += "."; + } + if (i - DESTINATION_START == 3) { + is_north = is_mic_e_lat_N(ascii); + } + if (i - DESTINATION_START == 4) { + lng_offset = get_mic_e_lng_offset(ascii); + } + if (i - DESTINATION_START == 5) { + is_west = is_mic_e_lng_W(ascii); + } + } + if (is_north) { + lat_str += "N"; + } else { + lat_str += "S"; + } + + pos.latitude = parse_lat_str(lat_str); + + float lng = 0.0; + uint8_t information_start = get_information_start_index() + 1; + for (uint8_t i = information_start; i < information_start + 3 && i < payload_size - 2; i++) { + uint8_t ascii = payload[i]; + + if (i - information_start == 0) { // deg + ascii -= 28; + ascii += lng_offset; + + if (ascii >= 180 && ascii <= 189) { + ascii -= 80; + } else if (ascii >= 190 && ascii <= 199) { + ascii -= 190; + } + + lng += ascii; + } else if (i - information_start == 1) { // min + ascii -= 28; + if (ascii >= 60) { + ascii -= 60; + } + lng += ascii / 60.0; + } else if (i - information_start == 2) { // hundredth minutes + ascii -= 28; + + lng += (ascii / 100.0) / 60.0; + } + } + + if (is_west) { + lng = -lng; + } + + pos.longitude = lng; + } + + uint8_t get_mic_e_lat_digit(uint8_t ascii) { + if (ascii >= '0' && ascii <= '9') { + return ascii; + } + if (ascii >= 'A' && ascii <= 'J') { + return ascii - 17; + } + if (ascii >= 'P' && ascii <= 'Y') { + return ascii - 32; + } + if (ascii == 'K' || ascii == 'L' || ascii == 'Z') { + return ' '; + } + + return '\0'; + } + + bool is_mic_e_lat_N(uint8_t ascii) { + if (ascii >= 'P' && ascii <= 'Z') { + return true; + } + return false; // not technical definition, but the other case is invalid + } + + bool is_mic_e_lng_W(uint8_t ascii) { + if (ascii >= 'P' && ascii <= 'Z') { + return true; + } + return false; // not technical definition, but the other case is invalid + } + + uint8_t get_mic_e_lng_offset(uint8_t ascii) { + if (ascii >= 'P' && ascii <= 'Z') { + return 100; + } + return 0; // not technical definition, but the other case is invalid + } + + bool parse_address(uint8_t start, ADDRESS_TYPE address_type) { + uint8_t byte = 0; + uint8_t has_more = false; + uint8_t ssid = 0; + uint8_t buffer_index = 0; + + for (uint8_t i = start; i < start + ADDRESS_SIZE && i < payload_size - 2; i++) { + byte = payload[i]; + + if (i - start == 6) { + has_more = (byte & 0x1) == 0; + ssid = (byte >> 1) & 0x0F; + + if (ssid != 0 || address_type == REPEATER) { + address_buffer[buffer_index++] = '-'; + + if (ssid < 10) { + address_buffer[buffer_index++] = '0' + ssid; + address_buffer[buffer_index++] = '\0'; + } else { + address_buffer[buffer_index++] = '1'; + address_buffer[buffer_index++] = '0' + ssid - 10; + address_buffer[buffer_index++] = '\0'; + } + } else { + address_buffer[buffer_index++] = '\0'; + } + } else { + byte >>= 1; + + if (byte != ' ') { + address_buffer[buffer_index++] = byte; + } + } + } + + return has_more; + } }; } /* namespace aprs */ -#endif/*__APRS_PACKET_H__*/ +#endif /*__APRS_PACKET_H__*/ diff --git a/firmware/common/backlight.cpp b/firmware/common/backlight.cpp index d4c004794..5fdf8fb20 100644 --- a/firmware/common/backlight.cpp +++ b/firmware/common/backlight.cpp @@ -26,75 +26,75 @@ namespace portapack { void BacklightOnOff::on() { - if( !is_on() ) { - io.lcd_backlight(true); - on_ = true; - } + if (!is_on()) { + io.lcd_backlight(true); + on_ = true; + } } void BacklightOnOff::off() { - if( is_on() ) { - io.lcd_backlight(false); - on_ = false; - } + if (is_on()) { + io.lcd_backlight(false); + on_ = false; + } } -void BacklightCAT4004::set_level(const value_t value) { - auto target = value; - - // Clip target value to valid range. - if( target < 0 ) { - target = 0; - } - if( target > maximum_level ) { - target = maximum_level; - } - - if( is_on() ) { - pulses(target); - } else { - level_ = target; - } +void BacklightCAT4004::set_level(const value_t value) { + auto target = value; + + // Clip target value to valid range. + if (target < 0) { + target = 0; + } + if (target > maximum_level) { + target = maximum_level; + } + + if (is_on()) { + pulses(target); + } else { + level_ = target; + } } void BacklightCAT4004::on() { - if( !is_on() ) { - io.lcd_backlight(true); - halPolledDelay(ticks_setup); - on_ = true; + if (!is_on()) { + io.lcd_backlight(true); + halPolledDelay(ticks_setup); + on_ = true; - // Just enabled driver, initial value is maximum. - const auto target_level = level(); - level_ = maximum_level; + // Just enabled driver, initial value is maximum. + const auto target_level = level(); + level_ = maximum_level; - pulses(target_level); - } + pulses(target_level); + } } void BacklightCAT4004::off() { - if( is_on() ) { - io.lcd_backlight(false); - chThdSleepMilliseconds(ms_pwrdwn); - on_ = false; - } + if (is_on()) { + io.lcd_backlight(false); + chThdSleepMilliseconds(ms_pwrdwn); + on_ = false; + } } void BacklightCAT4004::pulses(value_t target) { - while(level() != target) { - pulse(); - } + while (level() != target) { + pulse(); + } } void BacklightCAT4004::pulse() { - io.lcd_backlight(false); - halPolledDelay(ticks_lo); - io.lcd_backlight(true); - halPolledDelay(ticks_hi); - - level_ -= 1; - if( level_ < 0 ) { - level_ = levels() - 1; - } + io.lcd_backlight(false); + halPolledDelay(ticks_lo); + io.lcd_backlight(true); + halPolledDelay(ticks_hi); + + level_ -= 1; + if (level_ < 0) { + level_ = levels() - 1; + } } } /* namespace portapack */ diff --git a/firmware/common/backlight.hpp b/firmware/common/backlight.hpp index cb06471c9..703b5c243 100644 --- a/firmware/common/backlight.hpp +++ b/firmware/common/backlight.hpp @@ -26,95 +26,95 @@ namespace portapack { class Backlight { -public: - using value_t = int_fast8_t; + public: + using value_t = int_fast8_t; - virtual ~Backlight() = default; + virtual ~Backlight() = default; - virtual value_t levels() const = 0; + virtual value_t levels() const = 0; - virtual void set_level(const value_t value) = 0; - virtual value_t level() const = 0; + virtual void set_level(const value_t value) = 0; + virtual value_t level() const = 0; - virtual void increase() = 0; - virtual void decrease() = 0; + virtual void increase() = 0; + virtual void decrease() = 0; - virtual void on() = 0; - virtual void off() = 0; + virtual void on() = 0; + virtual void off() = 0; - virtual bool is_on() const = 0; + virtual bool is_on() const = 0; }; class BacklightBase : public Backlight { -public: - void increase() override { - set_level(level() + 1); - } - - void decrease() override { - set_level(level() - 1); - } + public: + void increase() override { + set_level(level() + 1); + } + + void decrease() override { + set_level(level() - 1); + } }; class BacklightOnOff : public BacklightBase { -public: - value_t levels() const override { - return 1; - } + public: + value_t levels() const override { + return 1; + } - void set_level(const value_t) override { - } + void set_level(const value_t) override { + } - value_t level() const override { - return levels() - 1; - } + value_t level() const override { + return levels() - 1; + } - void on() override; - void off() override; + void on() override; + void off() override; - bool is_on() const override { - return on_; - } + bool is_on() const override { + return on_; + } -private: - static constexpr value_t maximum_level = 1; + private: + static constexpr value_t maximum_level = 1; - bool on_ { false }; + bool on_{false}; }; class BacklightCAT4004 : public BacklightBase { -public: - value_t levels() const override { - return maximum_level + 1; - } + public: + value_t levels() const override { + return maximum_level + 1; + } - void set_level(const value_t value) override; + void set_level(const value_t value) override; - value_t level() const override { - return level_; - } + value_t level() const override { + return level_; + } - void on() override; - void off() override; + void on() override; + void off() override; - bool is_on() const override { - return on_; - } + bool is_on() const override { + return on_; + } -private: - static constexpr value_t initial_brightness = 25; - static constexpr value_t maximum_level = 31; + private: + static constexpr value_t initial_brightness = 25; + static constexpr value_t maximum_level = 31; - static constexpr uint32_t ticks_setup = 204e6 * 10e-6; - static constexpr uint32_t ms_pwrdwn = 5; - static constexpr uint32_t ticks_lo = 204e6 * 1e-6; - static constexpr uint32_t ticks_hi = 204e6 * 1e-6; + static constexpr uint32_t ticks_setup = 204e6 * 10e-6; + static constexpr uint32_t ms_pwrdwn = 5; + static constexpr uint32_t ticks_lo = 204e6 * 1e-6; + static constexpr uint32_t ticks_hi = 204e6 * 1e-6; - value_t level_ { initial_brightness }; - bool on_ { false }; + value_t level_{initial_brightness}; + bool on_{false}; - void pulses(value_t target); - void pulse(); + void pulses(value_t target); + void pulse(); }; } /* namespace portapack */ diff --git a/firmware/common/baseband.hpp b/firmware/common/baseband.hpp index dfeef046a..b8f0aff5d 100644 --- a/firmware/common/baseband.hpp +++ b/firmware/common/baseband.hpp @@ -31,10 +31,10 @@ using sample_t = complex8_t; using buffer_t = buffer_t; enum class Direction { - Receive = 0, - Transmit = 1, + Receive = 0, + Transmit = 1, }; } /* namespace baseband */ -#endif/*__BASEBAND_H__*/ +#endif /*__BASEBAND_H__*/ diff --git a/firmware/common/baseband_cpld.cpp b/firmware/common/baseband_cpld.cpp index 8eb8869a9..8e2044c69 100644 --- a/firmware/common/baseband_cpld.cpp +++ b/firmware/common/baseband_cpld.cpp @@ -27,12 +27,12 @@ using namespace hackrf::one; namespace baseband { void CPLD::init() { - set_invert(false); - gpio_q_invert.output(); + set_invert(false); + gpio_q_invert.output(); } void CPLD::set_invert(const bool invert) { - gpio_q_invert.write(invert); + gpio_q_invert.write(invert); } -} +} // namespace baseband diff --git a/firmware/common/baseband_cpld.hpp b/firmware/common/baseband_cpld.hpp index 6bcb63aa6..80e425946 100644 --- a/firmware/common/baseband_cpld.hpp +++ b/firmware/common/baseband_cpld.hpp @@ -27,14 +27,14 @@ namespace baseband { class CPLD { -public: - void init(); + public: + void init(); - void set_invert(const bool invert); - -private: + void set_invert(const bool invert); + + private: }; -} +} // namespace baseband -#endif/*__BASEBAND_CPLD_H__*/ +#endif /*__BASEBAND_CPLD_H__*/ diff --git a/firmware/common/baseband_packet.hpp b/firmware/common/baseband_packet.hpp index c947fac45..05d7c033c 100644 --- a/firmware/common/baseband_packet.hpp +++ b/firmware/common/baseband_packet.hpp @@ -30,43 +30,43 @@ namespace baseband { class Packet { -public: - void set_timestamp(const Timestamp& value) { - timestamp_ = value; - } - - Timestamp timestamp() const { - return timestamp_; - } + public: + void set_timestamp(const Timestamp& value) { + timestamp_ = value; + } - void add(const bool symbol) { - if( count < capacity() ) { - data[count++] = symbol; - } - } + Timestamp timestamp() const { + return timestamp_; + } - uint_fast8_t operator[](const size_t index) const { - return (index < size()) ? data[index] : 0; - } + void add(const bool symbol) { + if (count < capacity()) { + data[count++] = symbol; + } + } - size_t size() const { - return count; - } + uint_fast8_t operator[](const size_t index) const { + return (index < size()) ? data[index] : 0; + } - size_t capacity() const { - return data.size(); - } + size_t size() const { + return count; + } - void clear() { - count = 0; - } + size_t capacity() const { + return data.size(); + } -private: - std::bitset<2560> data { }; - Timestamp timestamp_ { }; - size_t count { 0 }; + void clear() { + count = 0; + } + + private: + std::bitset<2560> data{}; + Timestamp timestamp_{}; + size_t count{0}; }; } /* namespace baseband */ -#endif/*__BASEBAND_PACKET_H__*/ +#endif /*__BASEBAND_PACKET_H__*/ diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index 1177a4c07..611d8aaa9 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -29,134 +29,134 @@ namespace baseband { /* struct PinConfig { - P_OUT_CFG p_out_cfg; - P_OE_CFG p_oe_cfg { P_OE_CFG::GPIO_OE }; - - constexpr SGPIOPinConfig( - P_OUT_CFG p_out_cfg - ) : - p_out_cfg(p_out_cfg) - { - } + P_OUT_CFG p_out_cfg; + P_OE_CFG p_oe_cfg { P_OE_CFG::GPIO_OE }; + + constexpr SGPIOPinConfig( + P_OUT_CFG p_out_cfg + ) : + p_out_cfg(p_out_cfg) + { + } }; static constexpr bool slice_mode_multislice = false; static constexpr P_OUT_CFG output_multiplexing_mode = - slice_mode_multislice ? P_OUT_CFG::DOUT_DOUTM8C : P_OUT_CFG::DOUT_DOUTM8A; + slice_mode_multislice ? P_OUT_CFG::DOUT_DOUTM8C : P_OUT_CFG::DOUT_DOUTM8A; static constexpr std::array pin_config { { - [PIN_D0] = { output_multiplexing_mode, SLICE_A }, - [PIN_D1] = { output_multiplexing_mode, SLICE_I }, - [PIN_D2] = { output_multiplexing_mode, }, - [PIN_D3] = { output_multiplexing_mode, }, - [PIN_D4] = { output_multiplexing_mode, }, - [PIN_D5] = { output_multiplexing_mode, }, - [PIN_D6] = { output_multiplexing_mode, }, - [PIN_D7] = { output_multiplexing_mode, }, - [PIN_CLKIN] = { P_OUT_CFG::DOUT_DOUTM1, }, - [PIN_CAPTURE] = { P_OUT_CFG::DOUT_DOUTM1, }, - [PIN_DISABLE] = { P_OUT_CFG::GPIO_OUT, }, - [PIN_DIRECTION] = { P_OUT_CFG::GPIO_OUT, }, - [PIN_INVERT] = { P_OUT_CFG::GPIO_OUT, }, - [PIN_DECIM0] = { P_OUT_CFG::GPIO_OUT, }, - [PIN_DECIM1] = { P_OUT_CFG::DOUT_DOUTM1, }, - [PIN_DECIM2] = { P_OUT_CFG::GPIO_OUT, }, + [PIN_D0] = { output_multiplexing_mode, SLICE_A }, + [PIN_D1] = { output_multiplexing_mode, SLICE_I }, + [PIN_D2] = { output_multiplexing_mode, }, + [PIN_D3] = { output_multiplexing_mode, }, + [PIN_D4] = { output_multiplexing_mode, }, + [PIN_D5] = { output_multiplexing_mode, }, + [PIN_D6] = { output_multiplexing_mode, }, + [PIN_D7] = { output_multiplexing_mode, }, + [PIN_CLKIN] = { P_OUT_CFG::DOUT_DOUTM1, }, + [PIN_CAPTURE] = { P_OUT_CFG::DOUT_DOUTM1, }, + [PIN_DISABLE] = { P_OUT_CFG::GPIO_OUT, }, + [PIN_DIRECTION] = { P_OUT_CFG::GPIO_OUT, }, + [PIN_INVERT] = { P_OUT_CFG::GPIO_OUT, }, + [PIN_DECIM0] = { P_OUT_CFG::GPIO_OUT, }, + [PIN_DECIM1] = { P_OUT_CFG::DOUT_DOUTM1, }, + [PIN_DECIM2] = { P_OUT_CFG::GPIO_OUT, }, } }; */ /* static constexpr std::array out_mux_cfg_receive { - { }, + { }, }; static constexpr std::array out_mux_cfg_transmit { - { }, + { }, }; */ enum class P_OUT_CFG : uint8_t { - DOUT_DOUTM1 = 0x0, - DOUT_DOUTM2A = 0x1, - DOUT_DOUTM2B = 0x2, - DOUT_DOUTM2C = 0x3, - GPIO_OUT = 0x4, - DOUT_DOUTM4A = 0x5, - DOUT_DOUTM4B = 0x6, - DOUT_DOUTM4C = 0x7, - CLK_OUT = 0x8, - DOUT_DOUTM8A = 0x9, - DOUT_DOUTM8B = 0xa, - DOUT_DOUTM8C = 0xb, + DOUT_DOUTM1 = 0x0, + DOUT_DOUTM2A = 0x1, + DOUT_DOUTM2B = 0x2, + DOUT_DOUTM2C = 0x3, + GPIO_OUT = 0x4, + DOUT_DOUTM4A = 0x5, + DOUT_DOUTM4B = 0x6, + DOUT_DOUTM4C = 0x7, + CLK_OUT = 0x8, + DOUT_DOUTM8A = 0x9, + DOUT_DOUTM8B = 0xa, + DOUT_DOUTM8C = 0xb, }; enum class P_OE_CFG : uint8_t { - GPIO_OE = 0x0, - DOUT_OEM1 = 0x4, - DOUT_OEM2 = 0x5, - DOUT_OEM4 = 0x6, - DOUT_OEM8 = 0x7, + GPIO_OE = 0x0, + DOUT_OEM1 = 0x4, + DOUT_OEM2 = 0x5, + DOUT_OEM4 = 0x6, + DOUT_OEM8 = 0x7, }; enum class CONCAT_ORDER : uint8_t { - SELF_LOOP = 0x0, - TWO_SLICES = 0x1, - FOUR_SLICES = 0x2, - EIGHT_SLICES = 0x3, + SELF_LOOP = 0x0, + TWO_SLICES = 0x1, + FOUR_SLICES = 0x2, + EIGHT_SLICES = 0x3, }; enum class CONCAT_ENABLE : uint8_t { - EXTERNAL_DATA_PIN = 0x0, - CONCATENATE_DATA = 0x1, + EXTERNAL_DATA_PIN = 0x0, + CONCATENATE_DATA = 0x1, }; enum class CLK_CAPTURE_MODE : uint8_t { - RISING_CLOCK_EDGE = 0, - FALLING_CLOCK_EDGE = 1, + RISING_CLOCK_EDGE = 0, + FALLING_CLOCK_EDGE = 1, }; enum class PARALLEL_MODE : uint8_t { - SHIFT_1_BIT_PER_CLOCK = 0x0, - SHIFT_2_BITS_PER_CLOCK = 0x1, - SHIFT_4_BITS_PER_CLOCK = 0x2, - SHIFT_1_BYTE_PER_CLOCK = 0x3, + SHIFT_1_BIT_PER_CLOCK = 0x0, + SHIFT_2_BITS_PER_CLOCK = 0x1, + SHIFT_4_BITS_PER_CLOCK = 0x2, + SHIFT_1_BYTE_PER_CLOCK = 0x3, }; enum { - PIN_D0 = 0, - PIN_D1 = 1, - PIN_D2 = 2, - PIN_D3 = 3, - PIN_D4 = 4, - PIN_D5 = 5, - PIN_D6 = 6, - PIN_D7 = 7, - PIN_CLKIN = 8, - PIN_CAPTURE = 9, - PIN_DISABLE = 10, - PIN_DIRECTION = 11, - PIN_INVERT = 12, - PIN_SYNC_EN = 13, - PIN_P81 = 14, - PIN_P78 = 15, + PIN_D0 = 0, + PIN_D1 = 1, + PIN_D2 = 2, + PIN_D3 = 3, + PIN_D4 = 4, + PIN_D5 = 5, + PIN_D6 = 6, + PIN_D7 = 7, + PIN_CLKIN = 8, + PIN_CAPTURE = 9, + PIN_DISABLE = 10, + PIN_DIRECTION = 11, + PIN_INVERT = 12, + PIN_SYNC_EN = 13, + PIN_P81 = 14, + PIN_P78 = 15, }; enum class Slice : uint8_t { - A = 0, - B = 1, - C = 2, - D = 3, - E = 4, - F = 5, - G = 6, - H = 7, - I = 8, - J = 9, - K = 10, - L = 11, - M = 12, - N = 13, - O = 14, - P = 15, + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, + F = 5, + G = 6, + H = 7, + I = 8, + J = 9, + K = 10, + L = 11, + M = 12, + N = 13, + O = 14, + P = 15, }; constexpr bool slice_mode_multislice = false; @@ -164,229 +164,179 @@ constexpr bool slice_mode_multislice = false; constexpr uint8_t pos_count_multi_slice = 0x1f; constexpr uint8_t pos_count_single_slice = 0x03; -constexpr Slice slice_order[] { - Slice::A, - Slice::I, - Slice::E, - Slice::J, - Slice::C, - Slice::K, - Slice::F, - Slice::L, - Slice::B, - Slice::M, - Slice::G, - Slice::N, - Slice::D, - Slice::O, - Slice::H, - Slice::P, +constexpr Slice slice_order[]{ + Slice::A, + Slice::I, + Slice::E, + Slice::J, + Slice::C, + Slice::K, + Slice::F, + Slice::L, + Slice::B, + Slice::M, + Slice::G, + Slice::N, + Slice::D, + Slice::O, + Slice::H, + Slice::P, }; constexpr uint32_t gpio_outreg(const Direction direction) { - return ((direction == Direction::Transmit) ? (1U << PIN_DIRECTION) : 0U) | (1U << PIN_DISABLE); + return ((direction == Direction::Transmit) ? (1U << PIN_DIRECTION) : 0U) | (1U << PIN_DISABLE); } constexpr uint32_t gpio_oenreg(const Direction direction) { - return - (0U << PIN_P78) - | (0U << PIN_P81) - | (0U << PIN_SYNC_EN) - | (0U << PIN_INVERT) - | (1U << PIN_DIRECTION) - | (1U << PIN_DISABLE) - | (0U << PIN_CAPTURE) - | (0U << PIN_CLKIN) - | ((direction == Direction::Transmit) ? 0xffU : 0x00U) - ; + return (0U << PIN_P78) | (0U << PIN_P81) | (0U << PIN_SYNC_EN) | (0U << PIN_INVERT) | (1U << PIN_DIRECTION) | (1U << PIN_DISABLE) | (0U << PIN_CAPTURE) | (0U << PIN_CLKIN) | ((direction == Direction::Transmit) ? 0xffU : 0x00U); } constexpr uint32_t out_mux_cfg(const P_OUT_CFG out, const P_OE_CFG oe) { - return - (toUType(out) << 0) - | (toUType(oe) << 4) - ; + return (toUType(out) << 0) | (toUType(oe) << 4); } constexpr uint32_t data_sgpio_mux_cfg( - const CONCAT_ENABLE concat_enable, - const CONCAT_ORDER concat_order -) { - return - (1U << 0) - | (0U << 1) - | (0U << 3) - | (3U << 5) - | (1U << 7) - | (0U << 9) - | (toUType(concat_enable) << 11) - | (toUType(concat_order) << 12) - ; + const CONCAT_ENABLE concat_enable, + const CONCAT_ORDER concat_order) { + return (1U << 0) | (0U << 1) | (0U << 3) | (3U << 5) | (1U << 7) | (0U << 9) | (toUType(concat_enable) << 11) | (toUType(concat_order) << 12); } constexpr uint32_t data_slice_mux_cfg( - const PARALLEL_MODE parallel_mode, - const CLK_CAPTURE_MODE clk_capture_mode -) { - return - (0U << 0) - | (toUType(clk_capture_mode) << 1) - | (1U << 2) - | (0U << 3) - | (0U << 4) - | (toUType(parallel_mode) << 6) - | (0U << 8) - ; + const PARALLEL_MODE parallel_mode, + const CLK_CAPTURE_MODE clk_capture_mode) { + return (0U << 0) | (toUType(clk_capture_mode) << 1) | (1U << 2) | (0U << 3) | (0U << 4) | (toUType(parallel_mode) << 6) | (0U << 8); } constexpr uint32_t pos( - const uint32_t pos, - const uint32_t pos_reset -) { - return - (pos << 0) - | (pos_reset << 8) - ; + const uint32_t pos, + const uint32_t pos_reset) { + return (pos << 0) | (pos_reset << 8); } constexpr uint32_t data_pos( - const bool multi_slice -) { - return pos( - (multi_slice ? pos_count_multi_slice : pos_count_single_slice), - (multi_slice ? pos_count_multi_slice : pos_count_single_slice) - ); + const bool multi_slice) { + return pos( + (multi_slice ? pos_count_multi_slice : pos_count_single_slice), + (multi_slice ? pos_count_multi_slice : pos_count_single_slice)); } constexpr CONCAT_ENABLE data_concat_enable( - const bool input_slice, - const bool single_slice -) { - return (input_slice || single_slice) - ? CONCAT_ENABLE::EXTERNAL_DATA_PIN - : CONCAT_ENABLE::CONCATENATE_DATA - ; + const bool input_slice, + const bool single_slice) { + return (input_slice || single_slice) + ? CONCAT_ENABLE::EXTERNAL_DATA_PIN + : CONCAT_ENABLE::CONCATENATE_DATA; } constexpr CONCAT_ORDER data_concat_order( - const bool input_slice, - const bool single_slice -) { - return (input_slice || single_slice) - ? CONCAT_ORDER::SELF_LOOP - : CONCAT_ORDER::EIGHT_SLICES - ; + const bool input_slice, + const bool single_slice) { + return (input_slice || single_slice) + ? CONCAT_ORDER::SELF_LOOP + : CONCAT_ORDER::EIGHT_SLICES; } constexpr CLK_CAPTURE_MODE data_clk_capture_mode( - const Direction direction -) { - return (direction == Direction::Transmit) - ? CLK_CAPTURE_MODE::RISING_CLOCK_EDGE - : CLK_CAPTURE_MODE::RISING_CLOCK_EDGE - ; + const Direction direction) { + return (direction == Direction::Transmit) + ? CLK_CAPTURE_MODE::RISING_CLOCK_EDGE + : CLK_CAPTURE_MODE::RISING_CLOCK_EDGE; } constexpr P_OUT_CFG data_p_out_cfg( - const bool multi_slice -) { - return (multi_slice) - ? P_OUT_CFG::DOUT_DOUTM8C - : P_OUT_CFG::DOUT_DOUTM8A - ; + const bool multi_slice) { + return (multi_slice) + ? P_OUT_CFG::DOUT_DOUTM8C + : P_OUT_CFG::DOUT_DOUTM8A; } static const sgpio_resources_t sgpio_resources = { - .base = { .clk = &LPC_CGU->BASE_PERIPH_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 6) }, - .branch = { .cfg = &LPC_CCU1->CLK_PERIPH_SGPIO_CFG, .stat = &LPC_CCU1->CLK_PERIPH_SGPIO_STAT }, - .reset = { .output_index = 57 }, + .base = {.clk = &LPC_CGU->BASE_PERIPH_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 6)}, + .branch = {.cfg = &LPC_CCU1->CLK_PERIPH_SGPIO_CFG, .stat = &LPC_CCU1->CLK_PERIPH_SGPIO_STAT}, + .reset = {.output_index = 57}, }; void SGPIO::init() { - base_clock_enable(&sgpio_resources.base); - branch_clock_enable(&sgpio_resources.branch); - peripheral_reset(&sgpio_resources.reset); + base_clock_enable(&sgpio_resources.base); + branch_clock_enable(&sgpio_resources.branch); + peripheral_reset(&sgpio_resources.reset); } void SGPIO::configure(const Direction direction) { - disable_all_slice_counters(); - - // Set data pins as input, temporarily. - LPC_SGPIO->GPIO_OENREG = gpio_oenreg(Direction::Receive); - - // Now that data pins are inputs, safe to change CPLD direction. - LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction); - - LPC_SGPIO->OUT_MUX_CFG[ 8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[ 9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[11] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[12] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[13] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[14] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); - LPC_SGPIO->OUT_MUX_CFG[15] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); - - const auto data_out_mux_cfg = out_mux_cfg(data_p_out_cfg(slice_mode_multislice), P_OE_CFG::GPIO_OE); - for(size_t i=0; i<8; i++) { - LPC_SGPIO->OUT_MUX_CFG[i] = data_out_mux_cfg; - } - - // Now that output enable sources are set, enable data bus in correct direction. - LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction); - - const auto slice_gpdma = Slice::H; - - const size_t slice_count = slice_mode_multislice ? 8 : 1; - const auto clk_capture_mode = data_clk_capture_mode(direction); - const auto single_slice = !slice_mode_multislice; - - uint32_t slice_enable_mask = 0; - for(size_t i=0; iSGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg( - concat_enable, - concat_order - ); - LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg( - PARALLEL_MODE::SHIFT_1_BYTE_PER_CLOCK, - clk_capture_mode - ); - - LPC_SGPIO->PRESET[slice_index] = 0; - LPC_SGPIO->COUNT[slice_index] = 0; - LPC_SGPIO->POS[slice_index] = data_pos(slice_mode_multislice); - LPC_SGPIO->REG[slice_index] = 0; - LPC_SGPIO->REG_SS[slice_index] = 0; - - slice_enable_mask |= (1U << slice_index); - } - - if( !slice_mode_multislice ) { - const auto slice_index = toUType(slice_gpdma); - - LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg( - CONCAT_ENABLE::CONCATENATE_DATA, - CONCAT_ORDER::SELF_LOOP - ); - LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg( - PARALLEL_MODE::SHIFT_1_BIT_PER_CLOCK, - clk_capture_mode - ); - - LPC_SGPIO->PRESET[slice_index] = 0; - LPC_SGPIO->COUNT[slice_index] = 0; - LPC_SGPIO->POS[slice_index] = pos(0x1f, 0x1f); - LPC_SGPIO->REG[slice_index] = 0x11111111; - LPC_SGPIO->REG_SS[slice_index] = 0x11111111; - - slice_enable_mask |= (1 << slice_index); - } - - set_slice_counter_enables(slice_enable_mask); + disable_all_slice_counters(); + + // Set data pins as input, temporarily. + LPC_SGPIO->GPIO_OENREG = gpio_oenreg(Direction::Receive); + + // Now that data pins are inputs, safe to change CPLD direction. + LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction); + + LPC_SGPIO->OUT_MUX_CFG[8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[11] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[12] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[13] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[14] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); + LPC_SGPIO->OUT_MUX_CFG[15] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); + + const auto data_out_mux_cfg = out_mux_cfg(data_p_out_cfg(slice_mode_multislice), P_OE_CFG::GPIO_OE); + for (size_t i = 0; i < 8; i++) { + LPC_SGPIO->OUT_MUX_CFG[i] = data_out_mux_cfg; + } + + // Now that output enable sources are set, enable data bus in correct direction. + LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction); + + const auto slice_gpdma = Slice::H; + + const size_t slice_count = slice_mode_multislice ? 8 : 1; + const auto clk_capture_mode = data_clk_capture_mode(direction); + const auto single_slice = !slice_mode_multislice; + + uint32_t slice_enable_mask = 0; + for (size_t i = 0; i < slice_count; i++) { + const auto slice = slice_order[i]; + const auto slice_index = toUType(slice); + const auto input_slice = (i == 0) && (direction != Direction::Transmit); + const auto concat_order = data_concat_order(input_slice, single_slice); + const auto concat_enable = data_concat_enable(input_slice, single_slice); + + LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg( + concat_enable, + concat_order); + LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg( + PARALLEL_MODE::SHIFT_1_BYTE_PER_CLOCK, + clk_capture_mode); + + LPC_SGPIO->PRESET[slice_index] = 0; + LPC_SGPIO->COUNT[slice_index] = 0; + LPC_SGPIO->POS[slice_index] = data_pos(slice_mode_multislice); + LPC_SGPIO->REG[slice_index] = 0; + LPC_SGPIO->REG_SS[slice_index] = 0; + + slice_enable_mask |= (1U << slice_index); + } + + if (!slice_mode_multislice) { + const auto slice_index = toUType(slice_gpdma); + + LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg( + CONCAT_ENABLE::CONCATENATE_DATA, + CONCAT_ORDER::SELF_LOOP); + LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg( + PARALLEL_MODE::SHIFT_1_BIT_PER_CLOCK, + clk_capture_mode); + + LPC_SGPIO->PRESET[slice_index] = 0; + LPC_SGPIO->COUNT[slice_index] = 0; + LPC_SGPIO->POS[slice_index] = pos(0x1f, 0x1f); + LPC_SGPIO->REG[slice_index] = 0x11111111; + LPC_SGPIO->REG_SS[slice_index] = 0x11111111; + + slice_enable_mask |= (1 << slice_index); + } + + set_slice_counter_enables(slice_enable_mask); } } /* namespace baseband */ diff --git a/firmware/common/baseband_sgpio.hpp b/firmware/common/baseband_sgpio.hpp index 442507df4..a40f28b2e 100644 --- a/firmware/common/baseband_sgpio.hpp +++ b/firmware/common/baseband_sgpio.hpp @@ -31,36 +31,36 @@ namespace baseband { class SGPIO { -public: - void init(); + public: + void init(); - void configure(const Direction direction); + void configure(const Direction direction); - void streaming_enable() { - /* TODO: Any reason not to control from general GPIO facility? */ - LPC_SGPIO->GPIO_OUTREG &= ~(1U << 10); - } + void streaming_enable() { + /* TODO: Any reason not to control from general GPIO facility? */ + LPC_SGPIO->GPIO_OUTREG &= ~(1U << 10); + } - void streaming_disable() { - /* TODO: Any reason not to control from general GPIO facility? */ - LPC_SGPIO->GPIO_OUTREG |= (1U << 10); - } + void streaming_disable() { + /* TODO: Any reason not to control from general GPIO facility? */ + LPC_SGPIO->GPIO_OUTREG |= (1U << 10); + } - bool streaming_is_enabled() const { - /* TODO: Any reason not to control from general GPIO facility? */ - return (LPC_SGPIO->GPIO_OUTREG >> 10) & 1; - } + bool streaming_is_enabled() const { + /* TODO: Any reason not to control from general GPIO facility? */ + return (LPC_SGPIO->GPIO_OUTREG >> 10) & 1; + } -private: - void disable_all_slice_counters() { - set_slice_counter_enables(0); - } + private: + void disable_all_slice_counters() { + set_slice_counter_enables(0); + } - void set_slice_counter_enables(const uint16_t enable_mask) { - LPC_SGPIO->CTRL_ENABLE = enable_mask; - } + void set_slice_counter_enables(const uint16_t enable_mask) { + LPC_SGPIO->CTRL_ENABLE = enable_mask; + } }; -} +} // namespace baseband -#endif/*__BASEBAND_GPIO_H__*/ +#endif /*__BASEBAND_GPIO_H__*/ diff --git a/firmware/common/bch_code.cpp b/firmware/common/bch_code.cpp index 16422f354..e6d8148e2 100644 --- a/firmware/common/bch_code.cpp +++ b/firmware/common/bch_code.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2016 Furrtek * * BCH Encoder/Decoder - Adapted from GNURadio - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -29,280 +29,264 @@ #include "bch_code.hpp" void BCHCode::generate_gf() { - /* - * generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] - * lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; - * polynomial form -> index form index_of[j=alpha**i] = i alpha=2 is the - * primitive element of GF(2**m) - */ - - int i, mask; - mask = 1; - alpha_to[m] = 0; - - for (i = 0; i < m; i++) - { - alpha_to[i] = mask; - - index_of[alpha_to[i]] = i; - - if (p[i] != 0) - alpha_to[m] ^= mask; - - mask <<= 1; - } - - index_of[alpha_to[m]] = m; - - mask >>= 1; - - for (i = m + 1; i < n; i++) - { - if (alpha_to[i - 1] >= mask) - alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1); - else - alpha_to[i] = alpha_to[i - 1] << 1; - - index_of[alpha_to[i]] = i; - } - - index_of[0] = -1; -} + /* + * generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] + * lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; + * polynomial form -> index form index_of[j=alpha**i] = i alpha=2 is the + * primitive element of GF(2**m) + */ + + int i, mask; + mask = 1; + alpha_to[m] = 0; + + for (i = 0; i < m; i++) { + alpha_to[i] = mask; + + index_of[alpha_to[i]] = i; + + if (p[i] != 0) + alpha_to[m] ^= mask; + + mask <<= 1; + } + index_of[alpha_to[m]] = m; + + mask >>= 1; + + for (i = m + 1; i < n; i++) { + if (alpha_to[i - 1] >= mask) + alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1); + else + alpha_to[i] = alpha_to[i - 1] << 1; + + index_of[alpha_to[i]] = i; + } + + index_of[0] = -1; +} void BCHCode::gen_poly() { - /* - * Compute generator polynomial of BCH code of length = 31, redundancy = 10 - * (OK, this is not very efficient, but we only do it once, right? :) - */ - - int ii, jj, ll, kaux; - int test, aux, nocycles, root, noterms, rdncy; - int cycle[15][6], size[15], min[11], zeros[11]; - - // Generate cycle sets modulo 31 - cycle[0][0] = 0; size[0] = 1; - cycle[1][0] = 1; size[1] = 1; - jj = 1; // cycle set index - - do - { - // Generate the jj-th cycle set - ii = 0; - do - { - ii++; - cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n; - size[jj]++; - aux = (cycle[jj][ii] * 2) % n; - - } while (aux != cycle[jj][0]); - - // Next cycle set representative - ll = 0; - do - { - ll++; - test = 0; - for (ii = 1; ((ii <= jj) && (!test)); ii++) - // Examine previous cycle sets - for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++) - if (ll == cycle[ii][kaux]) - test = 1; - } - while ((test) && (ll < (n - 1))); - - if (!(test)) - { - jj++; // next cycle set index - cycle[jj][0] = ll; - size[jj] = 1; - } - - } while (ll < (n - 1)); - - nocycles = jj; // number of cycle sets modulo n - // Search for roots 1, 2, ..., d-1 in cycle sets - - kaux = 0; - rdncy = 0; - - for (ii = 1; ii <= nocycles; ii++) - { - min[kaux] = 0; - - for (jj = 0; jj < size[ii]; jj++) - for (root = 1; root < d; root++) - if (root == cycle[ii][jj]) - min[kaux] = ii; - - if (min[kaux]) - { - rdncy += size[min[kaux]]; - kaux++; - } - } - - noterms = kaux; - kaux = 1; - - for (ii = 0; ii < noterms; ii++) - for (jj = 0; jj < size[min[ii]]; jj++) - { - zeros[kaux] = cycle[min[ii]][jj]; - kaux++; - } - - // Compute generator polynomial - g[0] = alpha_to[zeros[1]]; - g[1] = 1; // g(x) = (X + zeros[1]) initially - - for (ii = 2; ii <= rdncy; ii++) - { - g[ii] = 1; - for (jj = ii - 1; jj > 0; jj--) - if (g[jj] != 0) - g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n]; - else - g[jj] = g[jj - 1]; - - g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n]; - } + /* + * Compute generator polynomial of BCH code of length = 31, redundancy = 10 + * (OK, this is not very efficient, but we only do it once, right? :) + */ + + int ii, jj, ll, kaux; + int test, aux, nocycles, root, noterms, rdncy; + int cycle[15][6], size[15], min[11], zeros[11]; + + // Generate cycle sets modulo 31 + cycle[0][0] = 0; + size[0] = 1; + cycle[1][0] = 1; + size[1] = 1; + jj = 1; // cycle set index + + do { + // Generate the jj-th cycle set + ii = 0; + do { + ii++; + cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n; + size[jj]++; + aux = (cycle[jj][ii] * 2) % n; + + } while (aux != cycle[jj][0]); + + // Next cycle set representative + ll = 0; + do { + ll++; + test = 0; + for (ii = 1; ((ii <= jj) && (!test)); ii++) + // Examine previous cycle sets + for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++) + if (ll == cycle[ii][kaux]) + test = 1; + } while ((test) && (ll < (n - 1))); + + if (!(test)) { + jj++; // next cycle set index + cycle[jj][0] = ll; + size[jj] = 1; + } + + } while (ll < (n - 1)); + + nocycles = jj; // number of cycle sets modulo n + // Search for roots 1, 2, ..., d-1 in cycle sets + + kaux = 0; + rdncy = 0; + + for (ii = 1; ii <= nocycles; ii++) { + min[kaux] = 0; + + for (jj = 0; jj < size[ii]; jj++) + for (root = 1; root < d; root++) + if (root == cycle[ii][jj]) + min[kaux] = ii; + + if (min[kaux]) { + rdncy += size[min[kaux]]; + kaux++; + } + } + + noterms = kaux; + kaux = 1; + + for (ii = 0; ii < noterms; ii++) + for (jj = 0; jj < size[min[ii]]; jj++) { + zeros[kaux] = cycle[min[ii]][jj]; + kaux++; + } + + // Compute generator polynomial + g[0] = alpha_to[zeros[1]]; + g[1] = 1; // g(x) = (X + zeros[1]) initially + + for (ii = 2; ii <= rdncy; ii++) { + g[ii] = 1; + for (jj = ii - 1; jj > 0; jj--) + if (g[jj] != 0) + g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n]; + else + g[jj] = g[jj - 1]; + + g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n]; + } } -int * BCHCode::encode(int data[]) { - // Calculate redundant bits bb[] - - int h, i, j=0, start=0, end=(n-k); // 10 - int Mr[31]; - - if (!valid) return nullptr; - - for (i = 0; i < n; i++) { - Mr[i] = 0; - } - - for (h = 0; h < k; ++h) - Mr[h] = data[h]; - - while (end < n) - { - for (i=end; i>start-2; --i) - { - if (Mr[start] != 0) - { - Mr[i]^=g[j]; - ++j; - } - else - { - ++start; - j = 0; - end = start+(n-k); - break; - } - } - } - - j = 0; - for (i = start; i start - 2; --i) { + if (Mr[start] != 0) { + Mr[i] ^= g[j]; + ++j; + } else { + ++start; + j = 0; + end = start + (n - k); + break; + } + } + } + + j = 0; + for (i = start; i < end; ++i) { + bb[j] = Mr[i]; + ++j; + } + + return bb; +}; int BCHCode::decode(int recd[]) { - // We do not need the Berlekamp algorithm to decode. - // We solve before hand two equations in two variables. - - int i, j, q; - int elp[3], s[5], s3; - int count = 0, syn_error = 0; - int loc[3], reg[3]; - int aux; - int retval=0; - - if (!valid) return 0; - - for (i = 1; i <= 4; i++) { - s[i] = 0; - for (j = 0; j < n; j++) { - if (recd[j] != 0) { - s[i] ^= alpha_to[(i * j) % n]; - } - } - if (s[i] != 0) { - syn_error = 1; // set flag if non-zero syndrome - } - /* NOTE: If only error detection is needed, - * then exit the program here... - */ - // Convert syndrome from polynomial form to index form - s[i] = index_of[s[i]]; - }; - - if (syn_error) { // If there are errors, try to correct them - if (s[1] != -1) { - s3 = (s[1] * 3) % n; - if ( s[3] == s3 ) { // Was it a single error ? - //printf("One error at %d\n", s[1]); - recd[s[1]] ^= 1; // Yes: Correct it - } else { - /* Assume two errors occurred and solve - * for the coefficients of sigma(x), the - * error locator polynomial - */ - if (s[3] != -1) { - aux = alpha_to[s3] ^ alpha_to[s[3]]; - } else { - aux = alpha_to[s3]; - } - elp[0] = 0; - elp[1] = (s[2] - index_of[aux] + n) % n; - elp[2] = (s[1] - index_of[aux] + n) % n; - //printf("sigma(x) = "); - //for (i = 0; i <= 2; i++) { - // printf("%3d ", elp[i]); - //} - //printf("\n"); - //printf("Roots: "); - - // Find roots of the error location polynomial - for (i = 1; i <= 2; i++) { - reg[i] = elp[i]; - } - count = 0; - for (i = 1; i <= n; i++) { // Chien search - q = 1; - for (j = 1; j <= 2; j++) { - if (reg[j] != -1) { - reg[j] = (reg[j] + j) % n; - q ^= alpha_to[reg[j]]; - } - } - if (!q) { // store error location number indices - loc[count] = i % n; - count++; - } - } - - if (count == 2) { - // no. roots = degree of elp hence 2 errors - for (i = 0; i < 2; i++) - recd[loc[i]] ^= 1; - } else { // Cannot solve: Error detection - retval=1; - } - } - } else if (s[2] != -1) { // Error detection - retval=1; - } - } - - return retval; + // We do not need the Berlekamp algorithm to decode. + // We solve before hand two equations in two variables. + + int i, j, q; + int elp[3], s[5], s3; + int count = 0, syn_error = 0; + int loc[3], reg[3]; + int aux; + int retval = 0; + + if (!valid) return 0; + + for (i = 1; i <= 4; i++) { + s[i] = 0; + for (j = 0; j < n; j++) { + if (recd[j] != 0) { + s[i] ^= alpha_to[(i * j) % n]; + } + } + if (s[i] != 0) { + syn_error = 1; // set flag if non-zero syndrome + } + /* NOTE: If only error detection is needed, + * then exit the program here... + */ + // Convert syndrome from polynomial form to index form + s[i] = index_of[s[i]]; + }; + + if (syn_error) { // If there are errors, try to correct them + if (s[1] != -1) { + s3 = (s[1] * 3) % n; + if (s[3] == s3) { // Was it a single error ? + // printf("One error at %d\n", s[1]); + recd[s[1]] ^= 1; // Yes: Correct it + } else { + /* Assume two errors occurred and solve + * for the coefficients of sigma(x), the + * error locator polynomial + */ + if (s[3] != -1) { + aux = alpha_to[s3] ^ alpha_to[s[3]]; + } else { + aux = alpha_to[s3]; + } + elp[0] = 0; + elp[1] = (s[2] - index_of[aux] + n) % n; + elp[2] = (s[1] - index_of[aux] + n) % n; + // printf("sigma(x) = "); + // for (i = 0; i <= 2; i++) { + // printf("%3d ", elp[i]); + // } + // printf("\n"); + // printf("Roots: "); + + // Find roots of the error location polynomial + for (i = 1; i <= 2; i++) { + reg[i] = elp[i]; + } + count = 0; + for (i = 1; i <= n; i++) { // Chien search + q = 1; + for (j = 1; j <= 2; j++) { + if (reg[j] != -1) { + reg[j] = (reg[j] + j) % n; + q ^= alpha_to[reg[j]]; + } + } + if (!q) { // store error location number indices + loc[count] = i % n; + count++; + } + } + + if (count == 2) { + // no. roots = degree of elp hence 2 errors + for (i = 0; i < 2; i++) + recd[loc[i]] ^= 1; + } else { // Cannot solve: Error detection + retval = 1; + } + } + } else if (s[2] != -1) { // Error detection + retval = 1; + } + } + + return retval; } /* @@ -321,44 +305,48 @@ int BCHCode::decode(int recd[]) { * bb[] = coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x) */ BCHCode::BCHCode( - std::vector p_init, int m, int n, int k, int t -) : m { m }, - n { n }, - k { k }, - t { t } { - size_t i; - - d = 5; - - alpha_to = (int *)chHeapAlloc(NULL, sizeof(int) * (n + 1)); - index_of = (int *)chHeapAlloc(0, sizeof(int) * (n + 1)); - p = (int *)chHeapAlloc(0, sizeof(int) * (m + 1)); - g = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1)); - bb = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1)); - - if (alpha_to == NULL || - index_of == NULL || - p == NULL || - g == NULL || - bb == NULL) - valid = false; - else - valid = true; - - if (valid) { - for (i = 0; i < (size_t)(m + 1); i++) { - p[i] = p_init[i]; - } - - generate_gf(); /* generate the Galois Field GF(2**m) */ - gen_poly(); /* Compute the generator polynomial of BCH code */ - } + std::vector p_init, + int m, + int n, + int k, + int t) + : m{m}, + n{n}, + k{k}, + t{t} { + size_t i; + + d = 5; + + alpha_to = (int*)chHeapAlloc(NULL, sizeof(int) * (n + 1)); + index_of = (int*)chHeapAlloc(0, sizeof(int) * (n + 1)); + p = (int*)chHeapAlloc(0, sizeof(int) * (m + 1)); + g = (int*)chHeapAlloc(0, sizeof(int) * (n - k + 1)); + bb = (int*)chHeapAlloc(0, sizeof(int) * (n - k + 1)); + + if (alpha_to == NULL || + index_of == NULL || + p == NULL || + g == NULL || + bb == NULL) + valid = false; + else + valid = true; + + if (valid) { + for (i = 0; i < (size_t)(m + 1); i++) { + p[i] = p_init[i]; + } + + generate_gf(); /* generate the Galois Field GF(2**m) */ + gen_poly(); /* Compute the generator polynomial of BCH code */ + } } BCHCode::~BCHCode() { - if (alpha_to != NULL) chHeapFree(alpha_to); - if (index_of != NULL) chHeapFree(index_of); - if (p != NULL) chHeapFree(p); - if (g != NULL) chHeapFree(g); - if (bb != NULL) chHeapFree(bb); + if (alpha_to != NULL) chHeapFree(alpha_to); + if (index_of != NULL) chHeapFree(index_of); + if (p != NULL) chHeapFree(p); + if (g != NULL) chHeapFree(g); + if (bb != NULL) chHeapFree(bb); } diff --git a/firmware/common/bch_code.hpp b/firmware/common/bch_code.hpp index 9145ed357..64de9bd6c 100644 --- a/firmware/common/bch_code.hpp +++ b/firmware/common/bch_code.hpp @@ -3,7 +3,7 @@ * Copyright (C) 2016 Furrtek * * BCH Encoder/Decoder - Adapted from GNURadio - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,34 +28,34 @@ #include class BCHCode { -public: - BCHCode(std::vector p_init, int m, int n, int k, int t); - ~BCHCode(); - - BCHCode(const BCHCode&) = delete; - BCHCode(BCHCode&&) = delete; - BCHCode& operator=(const BCHCode&) = delete; - BCHCode& operator=(BCHCode&&) = delete; - - int * encode(int data[]); - int decode(int recd[]); - -private: - void gen_poly(); - void generate_gf(); - - bool valid { false }; - - int d { }; - int * p { }; // coefficients of primitive polynomial used to generate GF(2**5) - int m { }; // order of the field GF(2**5) = 5 - int n { }; // 2**5 - 1 = 31 - int k { }; // n - deg(g(x)) = 21 = dimension - int t { }; // 2 = error correcting capability - int * alpha_to { }; // log table of GF(2**5) - int * index_of { }; // antilog table of GF(2**5) - int * g { }; // coefficients of generator polynomial, g(x) [n - k + 1]=[11] - int * bb { }; // coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x) + public: + BCHCode(std::vector p_init, int m, int n, int k, int t); + ~BCHCode(); + + BCHCode(const BCHCode&) = delete; + BCHCode(BCHCode&&) = delete; + BCHCode& operator=(const BCHCode&) = delete; + BCHCode& operator=(BCHCode&&) = delete; + + int* encode(int data[]); + int decode(int recd[]); + + private: + void gen_poly(); + void generate_gf(); + + bool valid{false}; + + int d{}; + int* p{}; // coefficients of primitive polynomial used to generate GF(2**5) + int m{}; // order of the field GF(2**5) = 5 + int n{}; // 2**5 - 1 = 31 + int k{}; // n - deg(g(x)) = 21 = dimension + int t{}; // 2 = error correcting capability + int* alpha_to{}; // log table of GF(2**5) + int* index_of{}; // antilog table of GF(2**5) + int* g{}; // coefficients of generator polynomial, g(x) [n - k + 1]=[11] + int* bb{}; // coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x) }; -#endif/*__BCHCODE_H__*/ +#endif /*__BCHCODE_H__*/ diff --git a/firmware/common/bit_pattern.hpp b/firmware/common/bit_pattern.hpp index 8014e0226..54f77e58a 100644 --- a/firmware/common/bit_pattern.hpp +++ b/firmware/common/bit_pattern.hpp @@ -26,48 +26,46 @@ #include class BitHistory { -public: - void add(const uint_fast8_t bit) { - history = (history << 1) | (bit & 1); - } + public: + void add(const uint_fast8_t bit) { + history = (history << 1) | (bit & 1); + } - uint64_t value() const { - return history; - } + uint64_t value() const { + return history; + } -private: - uint64_t history { 0 }; + private: + uint64_t history{0}; }; class BitPattern { -public: - constexpr BitPattern( - ) : code_ { 0 }, - mask_ { 0 }, - maximum_hanning_distance_ { 0 } - { - } - - constexpr BitPattern( - const uint64_t code, - const size_t code_length, - const size_t maximum_hanning_distance = 0 - ) : code_ { code }, - mask_ { (1ULL << code_length) - 1ULL }, - maximum_hanning_distance_ { maximum_hanning_distance } - { - } + public: + constexpr BitPattern() + : code_{0}, + mask_{0}, + maximum_hanning_distance_{0} { + } - bool operator()(const BitHistory& history, const size_t) const { - const auto delta_bits = (history.value() ^ code_) & mask_; - const size_t count = __builtin_popcountll(delta_bits); - return (count <= maximum_hanning_distance_); - } + constexpr BitPattern( + const uint64_t code, + const size_t code_length, + const size_t maximum_hanning_distance = 0) + : code_{code}, + mask_{(1ULL << code_length) - 1ULL}, + maximum_hanning_distance_{maximum_hanning_distance} { + } -private: - uint64_t code_; - uint64_t mask_; - size_t maximum_hanning_distance_; + bool operator()(const BitHistory& history, const size_t) const { + const auto delta_bits = (history.value() ^ code_) & mask_; + const size_t count = __builtin_popcountll(delta_bits); + return (count <= maximum_hanning_distance_); + } + + private: + uint64_t code_; + uint64_t mask_; + size_t maximum_hanning_distance_; }; -#endif/*__BIT_PATTERN_H__*/ +#endif /*__BIT_PATTERN_H__*/ diff --git a/firmware/common/bmp.hpp b/firmware/common/bmp.hpp index 8081ee26c..1b6a3fbc8 100644 --- a/firmware/common/bmp.hpp +++ b/firmware/common/bmp.hpp @@ -22,32 +22,32 @@ #pragma pack(push, 1) struct bmp_header_t { - uint16_t signature; - uint32_t size; - uint16_t reserved_1; - uint16_t reserved_2; - uint32_t image_data; - uint32_t BIH_size; - uint32_t width; - uint32_t height; - uint16_t planes; - uint16_t bpp; - uint32_t compression; - uint32_t data_size; - uint32_t h_res; - uint32_t v_res; - uint32_t colors_count; - uint32_t icolors_count; + uint16_t signature; + uint32_t size; + uint16_t reserved_1; + uint16_t reserved_2; + uint32_t image_data; + uint32_t BIH_size; + uint32_t width; + uint32_t height; + uint16_t planes; + uint16_t bpp; + uint32_t compression; + uint32_t data_size; + uint32_t h_res; + uint32_t v_res; + uint32_t colors_count; + uint32_t icolors_count; }; #pragma pack(pop) #pragma pack(push, 1) struct bmp_palette_t { - struct color_t { - uint8_t B; - uint8_t G; - uint8_t R; - uint8_t A; - } color[16]; + struct color_t { + uint8_t B; + uint8_t G; + uint8_t R; + uint8_t A; + } color[16]; }; #pragma pack(pop) diff --git a/firmware/common/buffer.cpp b/firmware/common/buffer.cpp index 344b91a4e..553b185ee 100644 --- a/firmware/common/buffer.cpp +++ b/firmware/common/buffer.cpp @@ -25,12 +25,12 @@ #include "lpc43xx_m4.h" Timestamp Timestamp::now() { - // Code stolen from LPC43xx rtc_lld.c - Timestamp timestamp; + // Code stolen from LPC43xx rtc_lld.c + Timestamp timestamp; do { - timestamp.tv_time = LPC_RTC->CTIME0; - timestamp.tv_date = LPC_RTC->CTIME1; - } while( (timestamp.tv_time != LPC_RTC->CTIME0) || (timestamp.tv_date != LPC_RTC->CTIME1) ); + timestamp.tv_time = LPC_RTC->CTIME0; + timestamp.tv_date = LPC_RTC->CTIME1; + } while ((timestamp.tv_time != LPC_RTC->CTIME0) || (timestamp.tv_date != LPC_RTC->CTIME1)); return timestamp; } #endif diff --git a/firmware/common/buffer.hpp b/firmware/common/buffer.hpp index 8676f6f59..975f1e698 100644 --- a/firmware/common/buffer.hpp +++ b/firmware/common/buffer.hpp @@ -34,10 +34,10 @@ */ #if defined(LPC43XX_M4) struct Timestamp { - uint32_t tv_date { 0 }; - uint32_t tv_time { 0 }; + uint32_t tv_date{0}; + uint32_t tv_time{0}; - static Timestamp now(); + static Timestamp now(); }; #endif @@ -47,45 +47,42 @@ struct Timestamp { using Timestamp = lpc43xx::rtc::RTC; #endif -template +template struct buffer_t { - T* const p; - const size_t count; - const uint32_t sampling_rate; - const Timestamp timestamp; + T* const p; + const size_t count; + const uint32_t sampling_rate; + const Timestamp timestamp; - constexpr buffer_t( - ) : p { nullptr }, - count { 0 }, - sampling_rate { 0 }, - timestamp { } - { - } + constexpr buffer_t() + : p{nullptr}, + count{0}, + sampling_rate{0}, + timestamp{} { + } - constexpr buffer_t( - const buffer_t& other - ) : p { other.p }, - count { other.count }, - sampling_rate { other.sampling_rate }, - timestamp { other.timestamp } - { - } + constexpr buffer_t( + const buffer_t& other) + : p{other.p}, + count{other.count}, + sampling_rate{other.sampling_rate}, + timestamp{other.timestamp} { + } - constexpr buffer_t( - T* const p, - const size_t count, - const uint32_t sampling_rate = 0, - const Timestamp timestamp = { } - ) : p { p }, - count { count }, - sampling_rate { sampling_rate }, - timestamp { timestamp } - { - } + constexpr buffer_t( + T* const p, + const size_t count, + const uint32_t sampling_rate = 0, + const Timestamp timestamp = {}) + : p{p}, + count{count}, + sampling_rate{sampling_rate}, + timestamp{timestamp} { + } - operator bool() const { - return (p != nullptr); - } + operator bool() const { + return (p != nullptr); + } }; -#endif/*__BUFFER_H__*/ +#endif /*__BUFFER_H__*/ diff --git a/firmware/common/buffer_exchange.cpp b/firmware/common/buffer_exchange.cpp index 2b0ae5530..29a6a07a8 100644 --- a/firmware/common/buffer_exchange.cpp +++ b/firmware/common/buffer_exchange.cpp @@ -21,54 +21,52 @@ #include "buffer_exchange.hpp" -BufferExchange* BufferExchange::obj { nullptr }; +BufferExchange* BufferExchange::obj{nullptr}; BufferExchange::BufferExchange( - CaptureConfig* const config -) // : config_capture { config } + CaptureConfig* const config) // : config_capture { config } { - obj = this; - // In capture mode, baseband wants empty buffers, app waits for full buffers - fifo_buffers_for_baseband = config->fifo_buffers_empty; - fifo_buffers_for_application = config->fifo_buffers_full; + obj = this; + // In capture mode, baseband wants empty buffers, app waits for full buffers + fifo_buffers_for_baseband = config->fifo_buffers_empty; + fifo_buffers_for_application = config->fifo_buffers_full; } BufferExchange::BufferExchange( - ReplayConfig* const config -) // : config_replay { config } + ReplayConfig* const config) // : config_replay { config } { - obj = this; - // In replay mode, baseband wants full buffers, app waits for empty buffers - fifo_buffers_for_baseband = config->fifo_buffers_full; - fifo_buffers_for_application = config->fifo_buffers_empty; + obj = this; + // In replay mode, baseband wants full buffers, app waits for empty buffers + fifo_buffers_for_baseband = config->fifo_buffers_full; + fifo_buffers_for_application = config->fifo_buffers_empty; } BufferExchange::~BufferExchange() { - obj = nullptr; - fifo_buffers_for_baseband = nullptr; - fifo_buffers_for_application = nullptr; + obj = nullptr; + fifo_buffers_for_baseband = nullptr; + fifo_buffers_for_application = nullptr; } StreamBuffer* BufferExchange::get(FIFO* fifo) { - while(true) { - StreamBuffer* p { nullptr }; - fifo->out(p); - - if( p ) { - return p; - } + while (true) { + StreamBuffer* p{nullptr}; + fifo->out(p); - // Put thread to sleep, woken up by M4 IRQ - chSysLock(); - thread = chThdSelf(); - chSchGoSleepS(THD_STATE_SUSPENDED); - chSysUnlock(); - } + if (p) { + return p; + } + + // Put thread to sleep, woken up by M4 IRQ + chSysLock(); + thread = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + chSysUnlock(); + } } StreamBuffer* BufferExchange::get_prefill(FIFO* fifo) { - StreamBuffer* p { nullptr }; - fifo->out(p); - - return p; + StreamBuffer* p{nullptr}; + fifo->out(p); + + return p; } diff --git a/firmware/common/buffer_exchange.hpp b/firmware/common/buffer_exchange.hpp index 8315c3080..b188ba6b6 100644 --- a/firmware/common/buffer_exchange.hpp +++ b/firmware/common/buffer_exchange.hpp @@ -28,85 +28,85 @@ #include "io.hpp" class BufferExchange { -public: - BufferExchange(CaptureConfig* const config); - BufferExchange(ReplayConfig* const config); - ~BufferExchange(); + public: + BufferExchange(CaptureConfig* const config); + BufferExchange(ReplayConfig* const config); + ~BufferExchange(); - BufferExchange(const BufferExchange&) = delete; - BufferExchange(BufferExchange&&) = delete; - BufferExchange& operator=(const BufferExchange&) = delete; - BufferExchange& operator=(BufferExchange&&) = delete; + BufferExchange(const BufferExchange&) = delete; + BufferExchange(BufferExchange&&) = delete; + BufferExchange& operator=(const BufferExchange&) = delete; + BufferExchange& operator=(BufferExchange&&) = delete; #if defined(LPC43XX_M0) - bool empty() const { - return fifo_buffers_for_application->is_empty(); - } - - StreamBuffer* get() { - return get(fifo_buffers_for_application); - } - - StreamBuffer* get_prefill() { - return get_prefill(fifo_buffers_for_application); - } - - bool put(StreamBuffer* const p) { - return fifo_buffers_for_baseband->in(p); - } - - bool put_app(StreamBuffer* const p) { - return fifo_buffers_for_application->in(p); - } + bool empty() const { + return fifo_buffers_for_application->is_empty(); + } + + StreamBuffer* get() { + return get(fifo_buffers_for_application); + } + + StreamBuffer* get_prefill() { + return get_prefill(fifo_buffers_for_application); + } + + bool put(StreamBuffer* const p) { + return fifo_buffers_for_baseband->in(p); + } + + bool put_app(StreamBuffer* const p) { + return fifo_buffers_for_application->in(p); + } #endif #if defined(LPC43XX_M4) - bool empty() const { - return fifo_buffers_for_baseband->is_empty(); - } + bool empty() const { + return fifo_buffers_for_baseband->is_empty(); + } - StreamBuffer* get() { - return get(fifo_buffers_for_baseband); - } + StreamBuffer* get() { + return get(fifo_buffers_for_baseband); + } - bool put(StreamBuffer* const p) { - return fifo_buffers_for_application->in(p); - } + bool put(StreamBuffer* const p) { + return fifo_buffers_for_application->in(p); + } #endif - static void handle_isr() { - if( obj ) { - obj->check_fifo_isr(); - } - } - -private: - //CaptureConfig* const config_capture; - //ReplayConfig* const config_replay; - FIFO* fifo_buffers_for_baseband { nullptr }; - FIFO* fifo_buffers_for_application { nullptr }; - Thread* thread { nullptr }; - static BufferExchange* obj; - - enum { - CAPTURE, - REPLAY - } direction { }; - - void check_fifo_isr() { - if (!empty()) - wakeup_isr(); - } - - void wakeup_isr() { - auto thread_tmp = thread; - if( thread_tmp ) { - thread = nullptr; - chSchReadyI(thread_tmp); - } - } - - StreamBuffer* get(FIFO* fifo); - - StreamBuffer* get_prefill(FIFO* fifo); + static void handle_isr() { + if (obj) { + obj->check_fifo_isr(); + } + } + + private: + // CaptureConfig* const config_capture; + // ReplayConfig* const config_replay; + FIFO* fifo_buffers_for_baseband{nullptr}; + FIFO* fifo_buffers_for_application{nullptr}; + Thread* thread{nullptr}; + static BufferExchange* obj; + + enum { + CAPTURE, + REPLAY + } direction{}; + + void check_fifo_isr() { + if (!empty()) + wakeup_isr(); + } + + void wakeup_isr() { + auto thread_tmp = thread; + if (thread_tmp) { + thread = nullptr; + chSchReadyI(thread_tmp); + } + } + + StreamBuffer* get(FIFO* fifo); + + StreamBuffer* get_prefill(FIFO* fifo); }; diff --git a/firmware/common/chibios_cpp.cpp b/firmware/common/chibios_cpp.cpp index 97ca30b1a..7af3eb768 100644 --- a/firmware/common/chibios_cpp.cpp +++ b/firmware/common/chibios_cpp.cpp @@ -26,33 +26,33 @@ #include void* operator new(size_t size) { - void *p = chHeapAlloc(0x0, size); - if (p == nullptr) - chDbgPanic("Out of Memory"); - return p; + void* p = chHeapAlloc(0x0, size); + if (p == nullptr) + chDbgPanic("Out of Memory"); + return p; } void* operator new[](size_t size) { - void *p = chHeapAlloc(0x0, size); - if (p == nullptr) - chDbgPanic("Out of Memory"); - return p; + void* p = chHeapAlloc(0x0, size); + if (p == nullptr) + chDbgPanic("Out of Memory"); + return p; } void operator delete(void* p) noexcept { - chHeapFree(p); + chHeapFree(p); } void operator delete[](void* p) noexcept { - chHeapFree(p); + chHeapFree(p); } void operator delete(void* ptr, std::size_t) noexcept { - ::operator delete(ptr); + ::operator delete(ptr); } void operator delete[](void* ptr, std::size_t) noexcept { - ::operator delete(ptr); + ::operator delete(ptr); } extern uint8_t __heap_base__[]; @@ -61,14 +61,14 @@ extern uint8_t __heap_end__[]; namespace chibios { size_t heap_size() { - return __heap_end__ - __heap_base__; + return __heap_end__ - __heap_base__; } size_t heap_used() { - const auto core_free = chCoreStatus(); - size_t heap_free = 0; - chHeapStatus(NULL, &heap_free); - return heap_size() - (core_free + heap_free); + const auto core_free = chCoreStatus(); + size_t heap_free = 0; + chHeapStatus(NULL, &heap_free); + return heap_size() - (core_free + heap_free); } } /* namespace chibios */ diff --git a/firmware/common/chibios_cpp.hpp b/firmware/common/chibios_cpp.hpp index fd952aafa..cd1e5c927 100644 --- a/firmware/common/chibios_cpp.hpp +++ b/firmware/common/chibios_cpp.hpp @@ -40,4 +40,4 @@ size_t heap_used(); } /* namespace chibios */ -#endif/*__CHIBIOS_CPP_H__*/ +#endif /*__CHIBIOS_CPP_H__*/ diff --git a/firmware/common/complex.hpp b/firmware/common/complex.hpp index 645a9b439..e1bd2d714 100644 --- a/firmware/common/complex.hpp +++ b/firmware/common/complex.hpp @@ -26,109 +26,108 @@ #include #include -constexpr float pi { 3.141592653589793238462643383279502884f }; +constexpr float pi{3.141592653589793238462643383279502884f}; namespace std { -template<> struct complex { -public: - typedef int8_t value_type; - typedef uint16_t rep_type; - - // constexpr complex( - // rep_type r - // ) : _rep { r } - // { - // } - - constexpr complex( - int8_t re = 0, - int8_t im = 0 - ) : _v { re, im } - { - } - - // constexpr complex( - // const complex& o - // ) : _rep { o._rep } - // { - // } - - constexpr int8_t real() const { return _v[0]; } - constexpr int8_t imag() const { return _v[1]; } - - void real(int8_t v) { _v[0] = v; } - void imag(int8_t v) { _v[1] = v; } - - constexpr uint16_t __rep() const { - return _rep; - } - -private: - union { - value_type _v[2]; - rep_type _rep; - }; +template <> +struct complex { + public: + typedef int8_t value_type; + typedef uint16_t rep_type; + + // constexpr complex( + // rep_type r + // ) : _rep { r } + // { + // } + + constexpr complex( + int8_t re = 0, + int8_t im = 0) + : _v{re, im} { + } + + // constexpr complex( + // const complex& o + // ) : _rep { o._rep } + // { + // } + + constexpr int8_t real() const { return _v[0]; } + constexpr int8_t imag() const { return _v[1]; } + + void real(int8_t v) { _v[0] = v; } + void imag(int8_t v) { _v[1] = v; } + + constexpr uint16_t __rep() const { + return _rep; + } + + private: + union { + value_type _v[2]; + rep_type _rep; + }; }; -template<> struct complex { -public: - typedef int16_t value_type; - typedef uint32_t rep_type; - - // constexpr complex( - // rep_type r - // ) : _rep { r } - // { - // } - - constexpr complex( - int16_t re = 0, - int16_t im = 0 - ) : _v { re, im } - { - } - - // constexpr complex( - // const complex& o - // ) : _rep { o._rep } - // { - // } - - constexpr int16_t real() const { return _v[0]; } - constexpr int16_t imag() const { return _v[1]; } - - void real(int16_t v) { _v[0] = v; } - void imag(int16_t v) { _v[1] = v; } - - template - complex& operator+=(const complex& other) { - _v[0] += other.real(); - _v[1] += other.imag(); - return *this; - } - - constexpr uint32_t __rep() const { - return _rep; - } - - constexpr operator std::complex() const { - return { - static_cast(_v[0]), - static_cast(_v[1]) - }; - } - -private: - union { - value_type _v[2]; - rep_type _rep; - }; +template <> +struct complex { + public: + typedef int16_t value_type; + typedef uint32_t rep_type; + + // constexpr complex( + // rep_type r + // ) : _rep { r } + // { + // } + + constexpr complex( + int16_t re = 0, + int16_t im = 0) + : _v{re, im} { + } + + // constexpr complex( + // const complex& o + // ) : _rep { o._rep } + // { + // } + + constexpr int16_t real() const { return _v[0]; } + constexpr int16_t imag() const { return _v[1]; } + + void real(int16_t v) { _v[0] = v; } + void imag(int16_t v) { _v[1] = v; } + + template + complex& operator+=(const complex& other) { + _v[0] += other.real(); + _v[1] += other.imag(); + return *this; + } + + constexpr uint32_t __rep() const { + return _rep; + } + + constexpr operator std::complex() const { + return { + static_cast(_v[0]), + static_cast(_v[1])}; + } + + private: + union { + value_type _v[2]; + rep_type _rep; + }; }; } /* namespace std */ -using complex8_t = std::complex; +using complex8_t = std::complex; using complex16_t = std::complex; using complex32_t = std::complex; @@ -136,4 +135,4 @@ static_assert(sizeof(complex8_t) == 2, "complex8_t size wrong"); static_assert(sizeof(complex16_t) == 4, "complex16_t size wrong"); static_assert(sizeof(complex32_t) == 8, "complex32_t size wrong"); -#endif/*__COMPLEX_H__*/ +#endif /*__COMPLEX_H__*/ diff --git a/firmware/common/cpld_max5.cpp b/firmware/common/cpld_max5.cpp index c73dc0d8a..9defc4d9d 100644 --- a/firmware/common/cpld_max5.cpp +++ b/firmware/common/cpld_max5.cpp @@ -30,42 +30,42 @@ namespace cpld { namespace max5 { void CPLD::bypass() { - shift_ir(instruction_t::BYPASS); - jtag.runtest_tck(18003); + shift_ir(instruction_t::BYPASS); + jtag.runtest_tck(18003); } void CPLD::sample() { - shift_ir(instruction_t::SAMPLE); - jtag.runtest_tck(93); - for(size_t i=0; i<80; i++) { - jtag.shift_dr(3, 0b111); - } + shift_ir(instruction_t::SAMPLE); + jtag.runtest_tck(93); + for (size_t i = 0; i < 80; i++) { + jtag.shift_dr(3, 0b111); + } } void CPLD::sample(std::bitset<240>& value) { - shift_ir(instruction_t::SAMPLE); - jtag.runtest_tck(93); - shift_dr(value); + shift_ir(instruction_t::SAMPLE); + jtag.runtest_tck(93); + shift_dr(value); } void CPLD::extest(std::bitset<240>& value) { - shift_ir(instruction_t::EXTEST); - shift_dr(value); + shift_ir(instruction_t::EXTEST); + shift_dr(value); } void CPLD::clamp() { - shift_ir(instruction_t::CLAMP); - jtag.runtest_tck(93); + shift_ir(instruction_t::CLAMP); + jtag.runtest_tck(93); } void CPLD::enable() { - shift_ir(instruction_t::ISC_ENABLE); - jtag.runtest_tck(18003); // 1ms + shift_ir(instruction_t::ISC_ENABLE); + jtag.runtest_tck(18003); // 1ms } void CPLD::disable() { - shift_ir(instruction_t::ISC_DISABLE); - jtag.runtest_tck(18003); // 1ms + shift_ir(instruction_t::ISC_DISABLE); + jtag.runtest_tck(18003); // 1ms } /* Sector erase: @@ -76,96 +76,94 @@ void CPLD::disable() { * each sector of the user flash memory (UFM) block. */ void CPLD::bulk_erase() { - erase_sector(0x0011); - erase_sector(0x0001); - erase_sector(0x0000); + erase_sector(0x0011); + erase_sector(0x0001); + erase_sector(0x0000); } bool CPLD::program( - const std::array& block_0, - const std::array& block_1 -) { - bulk_erase(); - - /* Program: - * involves shifting in the address, data, and program instruction and - * generating the program pulse to program the flash cells. The program - * pulse is automatically generated internally by waiting in the run/test/ - * idle state for the specified program pulse time of 75 μs. This process - * is repeated for each address in the CFM and UFM blocks. - */ - program_block(0x0000, block_0); - program_block(0x0001, block_1); - - const auto verify_ok = verify(block_0, block_1); - - if( verify_ok ) { - /* Do "something". Not sure what, but it happens after verify. */ - /* Starts with a sequence the same as Program: Block 0. */ - /* Perhaps it is a write to tell the CPLD that the bitstream - * verified OK, and it's OK to load and execute? And despite only - * one bit changing, a write must be a multiple of a particular - * length (64 bits)? */ - sector_select(0x0000); - shift_ir(instruction_t::ISC_PROGRAM); - jtag.runtest_tck(93); // 5 us - - /* TODO: Use data from cpld_block_0, with appropriate bit(s) changed */ - /* Perhaps this is the "ISP_DONE" bit? */ - jtag.shift_dr(16, block_0[0] & 0xfbff); - jtag.runtest_tck(1800); // 100us - jtag.shift_dr(16, block_0[1]); - jtag.runtest_tck(1800); // 100us - jtag.shift_dr(16, block_0[2]); - jtag.runtest_tck(1800); // 100us - jtag.shift_dr(16, block_0[3]); - jtag.runtest_tck(1800); // 100us - } - - return verify_ok; + const std::array& block_0, + const std::array& block_1) { + bulk_erase(); + + /* Program: + * involves shifting in the address, data, and program instruction and + * generating the program pulse to program the flash cells. The program + * pulse is automatically generated internally by waiting in the run/test/ + * idle state for the specified program pulse time of 75 μs. This process + * is repeated for each address in the CFM and UFM blocks. + */ + program_block(0x0000, block_0); + program_block(0x0001, block_1); + + const auto verify_ok = verify(block_0, block_1); + + if (verify_ok) { + /* Do "something". Not sure what, but it happens after verify. */ + /* Starts with a sequence the same as Program: Block 0. */ + /* Perhaps it is a write to tell the CPLD that the bitstream + * verified OK, and it's OK to load and execute? And despite only + * one bit changing, a write must be a multiple of a particular + * length (64 bits)? */ + sector_select(0x0000); + shift_ir(instruction_t::ISC_PROGRAM); + jtag.runtest_tck(93); // 5 us + + /* TODO: Use data from cpld_block_0, with appropriate bit(s) changed */ + /* Perhaps this is the "ISP_DONE" bit? */ + jtag.shift_dr(16, block_0[0] & 0xfbff); + jtag.runtest_tck(1800); // 100us + jtag.shift_dr(16, block_0[1]); + jtag.runtest_tck(1800); // 100us + jtag.shift_dr(16, block_0[2]); + jtag.runtest_tck(1800); // 100us + jtag.shift_dr(16, block_0[3]); + jtag.runtest_tck(1800); // 100us + } + + return verify_ok; } bool CPLD::verify( - const std::array& block_0, - const std::array& block_1 -) { - /* Verify */ - const auto block_0_success = verify_block(0x0000, block_0); - const auto block_1_success = verify_block(0x0001, block_1); - return block_0_success && block_1_success; + const std::array& block_0, + const std::array& block_1) { + /* Verify */ + const auto block_0_success = verify_block(0x0000, block_0); + const auto block_1_success = verify_block(0x0001, block_1); + return block_0_success && block_1_success; } uint32_t CPLD::crc() { - crc_t crc { 0x04c11db7, 0xffffffff, 0xffffffff }; - block_crc(0, 3328, crc); - block_crc(1, 512, crc); - return crc.checksum(); + crc_t crc{0x04c11db7, 0xffffffff, 0xffffffff}; + block_crc(0, 3328, crc); + block_crc(1, 512, crc); + return crc.checksum(); } void CPLD::sector_select(const uint16_t id) { - shift_ir(instruction_t::ISC_ADDRESS_SHIFT); - jtag.runtest_tck(93); // 5us - jtag.shift_dr(13, id); // Sector ID + shift_ir(instruction_t::ISC_ADDRESS_SHIFT); + jtag.runtest_tck(93); // 5us + jtag.shift_dr(13, id); // Sector ID } bool CPLD::idcode_ok() { - shift_ir(instruction_t::IDCODE); - const auto idcode_read = jtag.shift_dr(idcode_length, 0); - return (idcode_read == idcode); + shift_ir(instruction_t::IDCODE); + const auto idcode_read = jtag.shift_dr(idcode_length, 0); + return (idcode_read == idcode); } std::array CPLD::read_silicon_id() { - sector_select(0x0089); - shift_ir(instruction_t::ISC_READ); - jtag.runtest_tck(93); // 5us - - std::array silicon_id; - silicon_id[0] = jtag.shift_dr(16, 0xffff); - silicon_id[1] = jtag.shift_dr(16, 0xffff); - silicon_id[2] = jtag.shift_dr(16, 0xffff); - silicon_id[3] = jtag.shift_dr(16, 0xffff); - silicon_id[4] = jtag.shift_dr(16, 0xffff); - return silicon_id; + sector_select(0x0089); + shift_ir(instruction_t::ISC_READ); + jtag.runtest_tck(93); // 5us + + std::array silicon_id; + silicon_id[0] = jtag.shift_dr(16, 0xffff); + silicon_id[1] = jtag.shift_dr(16, 0xffff); + silicon_id[2] = jtag.shift_dr(16, 0xffff); + silicon_id[3] = jtag.shift_dr(16, 0xffff); + silicon_id[4] = jtag.shift_dr(16, 0xffff); + return silicon_id; } /* Check ID: @@ -174,100 +172,97 @@ std::array CPLD::read_silicon_id() { * the overall programming time. */ bool CPLD::silicon_id_ok() { - const auto silicon_id = read_silicon_id(); - - return ( - (silicon_id[0] == 0x8232) && - (silicon_id[1] == 0x2aa2) && - (silicon_id[2] == 0x4a82) && - (silicon_id[3] == 0x8c0c) && - (silicon_id[4] == 0x0000) - ); + const auto silicon_id = read_silicon_id(); + + return ( + (silicon_id[0] == 0x8232) && + (silicon_id[1] == 0x2aa2) && + (silicon_id[2] == 0x4a82) && + (silicon_id[3] == 0x8c0c) && + (silicon_id[4] == 0x0000)); } uint32_t CPLD::usercode() { - shift_ir(instruction_t::USERCODE); - jtag.runtest_tck(93); // 5us - return jtag.shift_dr(32, 0xffffffff); + shift_ir(instruction_t::USERCODE); + jtag.runtest_tck(93); // 5us + return jtag.shift_dr(32, 0xffffffff); } void CPLD::erase_sector(const uint16_t id) { - sector_select(id); - shift_ir(instruction_t::ISC_ERASE); - jtag.runtest_tck(9000003); // 500ms + sector_select(id); + shift_ir(instruction_t::ISC_ERASE); + jtag.runtest_tck(9000003); // 500ms } void CPLD::program_block( - const uint16_t id, - const uint16_t* const data, - const size_t count -) { - sector_select(id); - shift_ir(instruction_t::ISC_PROGRAM); - jtag.runtest_tck(93); // 5us - - for(size_t i=0; i& block_0; - const std::array& block_1; + const std::array& block_0; + const std::array& block_1; }; class CPLD { -public: - constexpr CPLD( - jtag::JTAG& jtag - ) : jtag(jtag) - { - } - - void bypass(); - void sample(); - void sample(std::bitset<240>& value); - void extest(std::bitset<240>& value); - void clamp(); - - void reset() { - jtag.reset(); - } + public: + constexpr CPLD( + jtag::JTAG& jtag) + : jtag(jtag) { + } - void run_test_idle() { - jtag.run_test_idle(); - } + void bypass(); + void sample(); + void sample(std::bitset<240>& value); + void extest(std::bitset<240>& value); + void clamp(); - bool idcode_ok(); + void reset() { + jtag.reset(); + } - void enable(); + void run_test_idle() { + jtag.run_test_idle(); + } + + bool idcode_ok(); - /* Check ID: - * The silicon ID is checked before any Program or Verify process. The - * time required to read this silicon ID is relatively small compared to - * the overall programming time. - */ - bool silicon_id_ok(); + void enable(); + + /* Check ID: + * The silicon ID is checked before any Program or Verify process. The + * time required to read this silicon ID is relatively small compared to + * the overall programming time. + */ + bool silicon_id_ok(); - uint32_t usercode(); + uint32_t usercode(); + + void disable(); + + void bulk_erase(); + + bool program( + const std::array& block_0, + const std::array& block_1); + + bool verify( + const std::array& block_0, + const std::array& block_1); + + bool is_blank(); + + uint32_t crc(); + + std::pair boundary_scan(); + + private: + using idcode_t = uint32_t; + static constexpr size_t idcode_length = 32; + static constexpr idcode_t idcode = 0b00000010000010100101000011011101; + static constexpr idcode_t idcode_mask = 0b11111111111111111111111111111111; + + static constexpr size_t ir_length = 10; + + using ir_t = uint16_t; + + enum class instruction_t : ir_t { + BYPASS = 0b1111111111, // 0x3ff + EXTEST = 0b0000001111, // 0x00f + SAMPLE = 0b0000000101, // 0x005 + IDCODE = 0b0000000110, // 0x006 + USERCODE = 0b0000000111, // 0x007 + CLAMP = 0b0000001010, // 0x00a + HIGHZ = 0b0000001011, // 0x00b + ISC_ENABLE = 0b1011001100, // 0x2cc + ISC_DISABLE = 0b1000000001, // 0x201 + ISC_PROGRAM = 0b1011110100, // 0x2f4 + ISC_ERASE = 0b1011110010, // 0x2f2 + ISC_ADDRESS_SHIFT = 0b1000000011, // 0x203 + ISC_READ = 0b1000000101, // 0x205 + ISC_NOOP = 0b1000010000, // 0x210 + }; + + void shift_ir(const instruction_t instruction) { + shift_ir(static_cast(instruction)); + } + + void shift_ir(const uint32_t value) { + jtag.shift_ir(ir_length, value); + } + + void shift_dr(std::bitset<240>& value) { + for (size_t i = 0; i < value.size(); i++) { + value[i] = shift_dr(1, value[i]); + } + } + + uint32_t shift_dr(const size_t count, const uint32_t value) { + return jtag.shift_dr(count, value); + } + + jtag::JTAG& jtag; + + std::array read_silicon_id(); + + void sector_select(const uint16_t id); - void disable(); + void erase_sector(const uint16_t id); - void bulk_erase(); + void program_block( + const uint16_t id, + const uint16_t* const data, + const size_t count); - bool program( - const std::array& block_0, - const std::array& block_1 - ); + template + void program_block( + const uint16_t id, + const std::array& data) { + program_block(id, data.data(), data.size()); + } - bool verify( - const std::array& block_0, - const std::array& block_1 - ); + bool verify_block( + const uint16_t id, + const uint16_t* const data, + const size_t count); - bool is_blank(); + template + bool verify_block( + const uint16_t id, + const std::array& data) { + return verify_block(id, data.data(), data.size()); + } - uint32_t crc(); + bool is_blank_block(const uint16_t id, const size_t count); - std::pair boundary_scan(); - -private: - using idcode_t = uint32_t; - static constexpr size_t idcode_length = 32; - static constexpr idcode_t idcode = 0b00000010000010100101000011011101; - static constexpr idcode_t idcode_mask = 0b11111111111111111111111111111111; - - static constexpr size_t ir_length = 10; - - using ir_t = uint16_t; - - enum class instruction_t : ir_t { - BYPASS = 0b1111111111, // 0x3ff - EXTEST = 0b0000001111, // 0x00f - SAMPLE = 0b0000000101, // 0x005 - IDCODE = 0b0000000110, // 0x006 - USERCODE = 0b0000000111, // 0x007 - CLAMP = 0b0000001010, // 0x00a - HIGHZ = 0b0000001011, // 0x00b - ISC_ENABLE = 0b1011001100, // 0x2cc - ISC_DISABLE = 0b1000000001, // 0x201 - ISC_PROGRAM = 0b1011110100, // 0x2f4 - ISC_ERASE = 0b1011110010, // 0x2f2 - ISC_ADDRESS_SHIFT = 0b1000000011, // 0x203 - ISC_READ = 0b1000000101, // 0x205 - ISC_NOOP = 0b1000010000, // 0x210 - }; - - void shift_ir(const instruction_t instruction) { - shift_ir(static_cast(instruction)); - } - - void shift_ir(const uint32_t value) { - jtag.shift_ir(ir_length, value); - } - - void shift_dr(std::bitset<240>& value) { - for(size_t i=0; i read_silicon_id(); - - void sector_select(const uint16_t id); - - void erase_sector(const uint16_t id); - - void program_block( - const uint16_t id, - const uint16_t* const data, - const size_t count - ); - - template - void program_block( - const uint16_t id, - const std::array& data - ) { - program_block(id, data.data(), data.size()); - } - - bool verify_block( - const uint16_t id, - const uint16_t* const data, - const size_t count - ); - - template - bool verify_block( - const uint16_t id, - const std::array& data - ) { - return verify_block(id, data.data(), data.size()); - } - - bool is_blank_block(const uint16_t id, const size_t count); - - using crc_t = CRC<32, true, true>; - void block_crc(const uint16_t id, const size_t count, crc_t& crc); + using crc_t = CRC<32, true, true>; + void block_crc(const uint16_t id, const size_t count, crc_t& crc); }; /* class ModeISP { public: - ModeISP( - CPLD& cpld - ) : cpld(cpld) - { - cpld.enter_isp(); - } + ModeISP( + CPLD& cpld + ) : cpld(cpld) + { + cpld.enter_isp(); + } - ~ModeISP() { - cpld.exit_isp(); - } + ~ModeISP() { + cpld.exit_isp(); + } private: - CPLD& cpld; + CPLD& cpld; }; */ } /* namespace max5 */ } /* namespace cpld */ -#endif/*__CPLD_MAX5_H__*/ +#endif /*__CPLD_MAX5_H__*/ diff --git a/firmware/common/cpld_update.cpp b/firmware/common/cpld_update.cpp index fa3e62e53..ce1fe84e5 100644 --- a/firmware/common/cpld_update.cpp +++ b/firmware/common/cpld_update.cpp @@ -34,59 +34,57 @@ namespace portapack { namespace cpld { CpldUpdateStatus update_if_necessary( - const Config config -) { - jtag::GPIOTarget target { - portapack::gpio_cpld_tck, - portapack::gpio_cpld_tms, - portapack::gpio_cpld_tdi, - portapack::gpio_cpld_tdo - }; - jtag::JTAG jtag { target }; - CPLD cpld { jtag }; - - /* Unknown state */ - cpld.reset(); - cpld.run_test_idle(); - - /* Run-Test/Idle */ - if( !cpld.idcode_ok() ) { - return CpldUpdateStatus::Idcode_check_failed; - } - - cpld.sample(); - cpld.bypass(); - cpld.enable(); - - /* If silicon ID doesn't match, there's a serious problem. Leave CPLD - * in passive state. - */ - if( !cpld.silicon_id_ok() ) { - return CpldUpdateStatus::Silicon_id_check_failed; - } - - /* Verify CPLD contents against current bitstream. */ - auto ok = cpld.verify(config.block_0, config.block_1); - - /* CPLD verifies incorrectly. Erase and program with current bitstream. */ - if( !ok ) { - ok = cpld.program(config.block_0, config.block_1); - } - - /* If programming OK, reset CPLD to user mode. Otherwise leave it in - * passive (ISP) state. - */ - if( ok ) { - cpld.disable(); - cpld.bypass(); - - /* Initiate SRAM reload from flash we just programmed. */ - cpld.sample(); - cpld.clamp(); - cpld.disable(); - } - - return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed; + const Config config) { + jtag::GPIOTarget target{ + portapack::gpio_cpld_tck, + portapack::gpio_cpld_tms, + portapack::gpio_cpld_tdi, + portapack::gpio_cpld_tdo}; + jtag::JTAG jtag{target}; + CPLD cpld{jtag}; + + /* Unknown state */ + cpld.reset(); + cpld.run_test_idle(); + + /* Run-Test/Idle */ + if (!cpld.idcode_ok()) { + return CpldUpdateStatus::Idcode_check_failed; + } + + cpld.sample(); + cpld.bypass(); + cpld.enable(); + + /* If silicon ID doesn't match, there's a serious problem. Leave CPLD + * in passive state. + */ + if (!cpld.silicon_id_ok()) { + return CpldUpdateStatus::Silicon_id_check_failed; + } + + /* Verify CPLD contents against current bitstream. */ + auto ok = cpld.verify(config.block_0, config.block_1); + + /* CPLD verifies incorrectly. Erase and program with current bitstream. */ + if (!ok) { + ok = cpld.program(config.block_0, config.block_1); + } + + /* If programming OK, reset CPLD to user mode. Otherwise leave it in + * passive (ISP) state. + */ + if (ok) { + cpld.disable(); + cpld.bypass(); + + /* Initiate SRAM reload from flash we just programmed. */ + cpld.sample(); + cpld.clamp(); + cpld.disable(); + } + + return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed; } } /* namespace cpld */ @@ -96,52 +94,52 @@ namespace hackrf { namespace cpld { static jtag::GPIOTarget jtag_target_hackrf() { - return { - hackrf::one::gpio_cpld_tck, - hackrf::one::gpio_cpld_tms, - hackrf::one::gpio_cpld_tdi, - hackrf::one::gpio_cpld_tdo, - }; + return { + hackrf::one::gpio_cpld_tck, + hackrf::one::gpio_cpld_tms, + hackrf::one::gpio_cpld_tdi, + hackrf::one::gpio_cpld_tdo, + }; } bool load_sram() { - auto jtag_target_hackrf_cpld = jtag_target_hackrf(); - hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld }; + auto jtag_target_hackrf_cpld = jtag_target_hackrf(); + hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld}; - hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks); - const auto ok = hackrf_cpld.verify_sram(hackrf::one::cpld::verify_blocks); + hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks); + const auto ok = hackrf_cpld.verify_sram(hackrf::one::cpld::verify_blocks); - return ok; + return ok; } void load_sram_no_verify() { - // CoolRunner II family has Hybrid memory CPLD arquitecture (SRAM+NVM) - // It seems that after using TX App somehow , I do not why , the CPLD_SRAM part needs to be re_loaded to solve #637 ghost beat + // CoolRunner II family has Hybrid memory CPLD arquitecture (SRAM+NVM) + // It seems that after using TX App somehow , I do not why , the CPLD_SRAM part needs to be re_loaded to solve #637 ghost beat // load_sram() it is already called at each boot in portapack.cpp ,including verify CPLD part. - // Here we skipped CPLD verify part,just to be quicker (in case any CPLD problem it will be detected in the boot process). + // Here we skipped CPLD verify part,just to be quicker (in case any CPLD problem it will be detected in the boot process). - auto jtag_target_hackrf_cpld = jtag_target_hackrf(); - hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld }; + auto jtag_target_hackrf_cpld = jtag_target_hackrf(); + hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld}; - hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks); + hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks); - return; + return; } bool verify_eeprom() { - auto jtag_target_hackrf_cpld = jtag_target_hackrf(); - hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld }; + auto jtag_target_hackrf_cpld = jtag_target_hackrf(); + hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld}; - const auto ok = hackrf_cpld.verify_eeprom(hackrf::one::cpld::verify_blocks); - - return ok; + const auto ok = hackrf_cpld.verify_eeprom(hackrf::one::cpld::verify_blocks); + + return ok; } void init_from_eeprom() { - auto jtag_target_hackrf_cpld = jtag_target_hackrf(); - hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld }; + auto jtag_target_hackrf_cpld = jtag_target_hackrf(); + hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld}; - hackrf_cpld.init_from_eeprom(); + hackrf_cpld.init_from_eeprom(); } } /* namespace cpld */ diff --git a/firmware/common/cpld_update.hpp b/firmware/common/cpld_update.hpp index ea7ad8be1..0d1f69462 100644 --- a/firmware/common/cpld_update.hpp +++ b/firmware/common/cpld_update.hpp @@ -28,15 +28,14 @@ namespace portapack { namespace cpld { enum class CpldUpdateStatus { - Success = 0, - Idcode_check_failed = 1, - Silicon_id_check_failed = 2, - Program_failed = 3 + Success = 0, + Idcode_check_failed = 1, + Silicon_id_check_failed = 2, + Program_failed = 3 }; CpldUpdateStatus update_if_necessary( - const Config config -); + const Config config); } /* namespace cpld */ } /* namespace portapack */ @@ -45,11 +44,11 @@ namespace hackrf { namespace cpld { bool load_sram(); -void load_sram_no_verify(); // added to solve issue #637 , "ghost" signal at RX , after using any TX App +void load_sram_no_verify(); // added to solve issue #637 , "ghost" signal at RX , after using any TX App bool verify_eeprom(); void init_from_eeprom(); } /* namespace cpld */ } /* namespace hackrf */ -#endif/*__CPLD_UPDATE_H__*/ +#endif /*__CPLD_UPDATE_H__*/ diff --git a/firmware/common/cpld_xilinx.cpp b/firmware/common/cpld_xilinx.cpp index 4690f6652..e43a1d962 100644 --- a/firmware/common/cpld_xilinx.cpp +++ b/firmware/common/cpld_xilinx.cpp @@ -25,154 +25,154 @@ namespace cpld { namespace xilinx { void XC2C64A::write_sram(const verify_blocks_t& blocks) { - tap.set_repeat(0); - tap.set_end_ir(state_t::run_test_idle); - tap.set_end_dr(state_t::run_test_idle); + tap.set_repeat(0); + tap.set_end_ir(state_t::run_test_idle); + tap.set_end_dr(state_t::run_test_idle); - reset(); - enable(); + reset(); + enable(); - shift_ir(instruction_t::ISC_WRITE); - for(const auto& block : blocks) { - tap.state(state_t::shift_dr); - tap.shift({ block.data.data(), block_length }, false); - tap.shift({ &block.id, block_id_length }, true); - tap.state(state_t::run_test_idle); - } + shift_ir(instruction_t::ISC_WRITE); + for (const auto& block : blocks) { + tap.state(state_t::shift_dr); + tap.shift({block.data.data(), block_length}, false); + tap.shift({&block.id, block_id_length}, true); + tap.state(state_t::run_test_idle); + } - disable(); - bypass(); + disable(); + bypass(); - tap.state(state_t::test_logic_reset); + tap.state(state_t::test_logic_reset); } bool XC2C64A::verify_sram(const verify_blocks_t& blocks) { - tap.set_repeat(0); - tap.set_end_ir(state_t::run_test_idle); - tap.set_end_dr(state_t::run_test_idle); - - reset(); - enable(); - - shift_ir(instruction_t::ISC_SRAM_READ); - - // Prime the operation with a read of an empty row. - const jtag::tap::bits_t empty_row { block_length }; - - tap.state(state_t::shift_dr); - tap.shift(empty_row, false); - - auto error = false; - for(const auto& block : blocks) { - tap.shift({ &block.id, block_id_length }, true); - tap.state(state_t::run_test_idle); - - tap.state(state_t::shift_dr); - error |= tap.shift(empty_row, { block.data.data(), block_length }, { block.mask.data(), block_length }, false); - } - // Redundant operation to finish the row. - tap.shift({ &blocks[0].id, block_id_length }, true); - tap.state(state_t::run_test_idle); - tap.set_end_dr(state_t::run_test_idle); - - disable(); - bypass(); - - tap.state(state_t::test_logic_reset); - - return !error; + tap.set_repeat(0); + tap.set_end_ir(state_t::run_test_idle); + tap.set_end_dr(state_t::run_test_idle); + + reset(); + enable(); + + shift_ir(instruction_t::ISC_SRAM_READ); + + // Prime the operation with a read of an empty row. + const jtag::tap::bits_t empty_row{block_length}; + + tap.state(state_t::shift_dr); + tap.shift(empty_row, false); + + auto error = false; + for (const auto& block : blocks) { + tap.shift({&block.id, block_id_length}, true); + tap.state(state_t::run_test_idle); + + tap.state(state_t::shift_dr); + error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false); + } + // Redundant operation to finish the row. + tap.shift({&blocks[0].id, block_id_length}, true); + tap.state(state_t::run_test_idle); + tap.set_end_dr(state_t::run_test_idle); + + disable(); + bypass(); + + tap.state(state_t::test_logic_reset); + + return !error; } bool XC2C64A::verify_eeprom(const verify_blocks_t& blocks) { - tap.set_repeat(0); - tap.set_end_ir(state_t::run_test_idle); - tap.set_end_dr(state_t::run_test_idle); + tap.set_repeat(0); + tap.set_end_ir(state_t::run_test_idle); + tap.set_end_dr(state_t::run_test_idle); - reset(); - bypass(); - enable(); + reset(); + bypass(); + enable(); - shift_ir(instruction_t::ISC_READ); + shift_ir(instruction_t::ISC_READ); - const jtag::tap::bits_t empty_row { block_length }; + const jtag::tap::bits_t empty_row{block_length}; - auto error = false; - for(const auto& block : blocks) { - tap.set_end_dr(state_t::pause_dr); - tap.shift_dr({ &block.id, block_id_length }); - tap.set_end_ir(state_t::run_test_idle); - tap.wait(state_t::pause_dr, state_t::pause_dr, 20); - tap.set_end_ir(state_t::run_test_idle); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); - error |= tap.shift_dr(empty_row, { block.data.data(), block_length }, { block.mask.data(), block_length }); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); - } + auto error = false; + for (const auto& block : blocks) { + tap.set_end_dr(state_t::pause_dr); + tap.shift_dr({&block.id, block_id_length}); + tap.set_end_ir(state_t::run_test_idle); + tap.wait(state_t::pause_dr, state_t::pause_dr, 20); + tap.set_end_ir(state_t::run_test_idle); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); + error |= tap.shift_dr(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); + } - disable(); - bypass(); + disable(); + bypass(); - tap.state(state_t::test_logic_reset); + tap.state(state_t::test_logic_reset); - return !error; + return !error; } void XC2C64A::init_from_eeprom() { - tap.set_repeat(0); - tap.set_end_ir(state_t::run_test_idle); - tap.set_end_dr(state_t::run_test_idle); + tap.set_repeat(0); + tap.set_end_ir(state_t::run_test_idle); + tap.set_end_dr(state_t::run_test_idle); + + reset(); + enable(); - reset(); - enable(); + discharge(); + init(); - discharge(); - init(); - - disable(); - bypass(); + disable(); + bypass(); - tap.state(state_t::test_logic_reset); + tap.state(state_t::test_logic_reset); } bool XC2C64A::shift_ir(const instruction_t instruction) { - const ir_t ir_buffer = toUType(instruction); - const jtag::tap::bits_t bits { &ir_buffer, ir_length }; - return tap.shift_ir(bits); + const ir_t ir_buffer = toUType(instruction); + const jtag::tap::bits_t bits{&ir_buffer, ir_length}; + return tap.shift_ir(bits); } void XC2C64A::reset() { - tap.state(state_t::test_logic_reset); - tap.state(state_t::run_test_idle); + tap.state(state_t::test_logic_reset); + tap.state(state_t::run_test_idle); } void XC2C64A::enable() { - shift_ir(instruction_t::ISC_ENABLE); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 800); + shift_ir(instruction_t::ISC_ENABLE); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 800); } void XC2C64A::enable_otf() { - shift_ir(instruction_t::ISC_ENABLE_OTF); + shift_ir(instruction_t::ISC_ENABLE_OTF); } void XC2C64A::discharge() { - shift_ir(instruction_t::ISC_INIT); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 20); + shift_ir(instruction_t::ISC_INIT); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 20); } void XC2C64A::init() { - tap.set_end_ir(state_t::update_ir); - shift_ir(instruction_t::ISC_INIT); - tap.set_end_ir(state_t::run_test_idle); - tap.state(state_t::capture_dr); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 800); + tap.set_end_ir(state_t::update_ir); + shift_ir(instruction_t::ISC_INIT); + tap.set_end_ir(state_t::run_test_idle); + tap.state(state_t::capture_dr); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 800); } void XC2C64A::disable() { - shift_ir(instruction_t::ISC_DISABLE); - tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); + shift_ir(instruction_t::ISC_DISABLE); + tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100); } bool XC2C64A::bypass() { - return shift_ir(instruction_t::BYPASS); + return shift_ir(instruction_t::BYPASS); } } /* namespace xilinx */ diff --git a/firmware/common/cpld_xilinx.hpp b/firmware/common/cpld_xilinx.hpp index ce7020424..7de4de0a8 100644 --- a/firmware/common/cpld_xilinx.hpp +++ b/firmware/common/cpld_xilinx.hpp @@ -36,91 +36,90 @@ namespace xilinx { using jtag::tap::state_t; class XC2C64A { -public: - using block_id_t = uint8_t; - - static constexpr size_t block_length = 274; - static constexpr size_t blocks_count = 98; - - static constexpr size_t block_bytes = (block_length + 7) >> 3; - - struct verify_block_t { - block_id_t id; - std::array data; - std::array mask; - }; - - struct program_block_t { - block_id_t id; - std::array data; - }; - - using verify_blocks_t = std::array; - - constexpr XC2C64A( - jtag::Target& jtag_interface - ) : tap { jtag_interface } - { - } - - void write_sram(const verify_blocks_t& blocks); - bool verify_sram(const verify_blocks_t& blocks); - - bool verify_eeprom(const verify_blocks_t& blocks); - void init_from_eeprom(); - -private: - static constexpr size_t idcode_length = 32; - using idcode_t = uint32_t; - - static constexpr size_t ir_length = 8; - static constexpr size_t block_id_length = 7; - - static constexpr idcode_t idcode = 0x06e58093; - static constexpr idcode_t idcode_mask = 0x0fff8fff; - - using ir_t = uint8_t; - - jtag::tap::TAPMachine tap; - - enum class instruction_t : ir_t { - INTEST = 0b00000010, // -> boundary-scan - BYPASS = 0b11111111, // -> bypass - SAMPLE = 0b00000011, // -> boundary-scan - EXTEST = 0b00000000, // -> boundary-scan - IDCODE = 0b00000001, // -> device ID - USERCODE = 0b11111101, // -> device ID - HIGHZ = 0b11111100, // -> bypass - ISC_ENABLE_CLAMP = 0b11101001, // -> ISC shift - ISC_ENABLE_OTF = 0b11100100, // -> ISC shift - ISC_ENABLE = 0b11101000, // -> ISC shift - ISC_SRAM_READ = 0b11100111, // -> ISC shift - ISC_WRITE = 0b11100110, // -> ISC shift, alias ISC_SRAM_WRITE - ISC_ERASE = 0b11101101, // -> ISC shift - ISC_PROGRAM = 0b11101010, // -> ISC shift - ISC_READ = 0b11101110, // -> ISC shift, alias ISC_VERIFY - ISC_INIT = 0b11110000, // -> ISC shift - ISC_DISABLE = 0b11000000, // -> ISC shift - TEST_ENABLE = 0b00010001, // alias Private1 - BULKPROG = 0b00010010, // alias Private2 - ERASE_ALL = 0b00010100, // alias Private4 - MVERIFY = 0b00010011, // alias Private3 - TEST_DISABLE = 0b00010101, // alias Private5 - ISC_NOOP = 0b11100000, // -> bypass - }; - - bool shift_ir(const instruction_t instruction); - - void reset(); - void enable(); - void enable_otf(); - void discharge(); - void init(); - void disable(); - bool bypass(); + public: + using block_id_t = uint8_t; + + static constexpr size_t block_length = 274; + static constexpr size_t blocks_count = 98; + + static constexpr size_t block_bytes = (block_length + 7) >> 3; + + struct verify_block_t { + block_id_t id; + std::array data; + std::array mask; + }; + + struct program_block_t { + block_id_t id; + std::array data; + }; + + using verify_blocks_t = std::array; + + constexpr XC2C64A( + jtag::Target& jtag_interface) + : tap{jtag_interface} { + } + + void write_sram(const verify_blocks_t& blocks); + bool verify_sram(const verify_blocks_t& blocks); + + bool verify_eeprom(const verify_blocks_t& blocks); + void init_from_eeprom(); + + private: + static constexpr size_t idcode_length = 32; + using idcode_t = uint32_t; + + static constexpr size_t ir_length = 8; + static constexpr size_t block_id_length = 7; + + static constexpr idcode_t idcode = 0x06e58093; + static constexpr idcode_t idcode_mask = 0x0fff8fff; + + using ir_t = uint8_t; + + jtag::tap::TAPMachine tap; + + enum class instruction_t : ir_t { + INTEST = 0b00000010, // -> boundary-scan + BYPASS = 0b11111111, // -> bypass + SAMPLE = 0b00000011, // -> boundary-scan + EXTEST = 0b00000000, // -> boundary-scan + IDCODE = 0b00000001, // -> device ID + USERCODE = 0b11111101, // -> device ID + HIGHZ = 0b11111100, // -> bypass + ISC_ENABLE_CLAMP = 0b11101001, // -> ISC shift + ISC_ENABLE_OTF = 0b11100100, // -> ISC shift + ISC_ENABLE = 0b11101000, // -> ISC shift + ISC_SRAM_READ = 0b11100111, // -> ISC shift + ISC_WRITE = 0b11100110, // -> ISC shift, alias ISC_SRAM_WRITE + ISC_ERASE = 0b11101101, // -> ISC shift + ISC_PROGRAM = 0b11101010, // -> ISC shift + ISC_READ = 0b11101110, // -> ISC shift, alias ISC_VERIFY + ISC_INIT = 0b11110000, // -> ISC shift + ISC_DISABLE = 0b11000000, // -> ISC shift + TEST_ENABLE = 0b00010001, // alias Private1 + BULKPROG = 0b00010010, // alias Private2 + ERASE_ALL = 0b00010100, // alias Private4 + MVERIFY = 0b00010011, // alias Private3 + TEST_DISABLE = 0b00010101, // alias Private5 + ISC_NOOP = 0b11100000, // -> bypass + }; + + bool shift_ir(const instruction_t instruction); + + void reset(); + void enable(); + void enable_otf(); + void discharge(); + void init(); + void disable(); + bool bypass(); }; } /* namespace xilinx */ } /* namespace cpld */ -#endif/*__CPLD_XILINX_H__*/ +#endif /*__CPLD_XILINX_H__*/ diff --git a/firmware/common/crc.hpp b/firmware/common/crc.hpp index fab78a6b2..c278ca93e 100644 --- a/firmware/common/crc.hpp +++ b/firmware/common/crc.hpp @@ -40,156 +40,154 @@ * */ -template +template class CRC { -public: - using value_type = uint32_t; - - constexpr CRC( - const value_type truncated_polynomial, - const value_type initial_remainder = 0, - const value_type final_xor_value = 0 - ) : truncated_polynomial { truncated_polynomial }, - initial_remainder { initial_remainder }, - final_xor_value { final_xor_value }, - remainder { initial_remainder } - { - } - - value_type get_initial_remainder() const { - return initial_remainder; - } - - void reset(value_type new_initial_remainder) { - remainder = new_initial_remainder; - } - - void reset() { - remainder = initial_remainder; - } - - void process_bit(bool bit) { - remainder ^= (bit ? top_bit() : 0U); - const auto do_poly_div = static_cast(remainder & top_bit()); - remainder <<= 1; - if( do_poly_div ) { - remainder ^= truncated_polynomial; - } - } - - void process_bits(value_type bits, size_t bit_count) { - if( RevIn ) { - process_bits_lsb_first(bits, bit_count); - } else { - process_bits_msb_first(bits, bit_count); - } - } - - void process_byte(const uint8_t byte) { - process_bits(byte, 8); - } - - void process_bytes(const void* const data, const size_t length) { - const uint8_t* const p = reinterpret_cast(data); - for(size_t i=0; i - void process_bytes(const std::array& data) { - process_bytes(data.data(), data.size()); - } - - value_type checksum() const { - return ((RevOut ? reflect(remainder) : remainder) ^ final_xor_value) & mask(); - } - -private: - const value_type truncated_polynomial; - const value_type initial_remainder; - const value_type final_xor_value; - value_type remainder; - - static constexpr size_t width() { - return Width; - } - - static constexpr value_type top_bit() { - return 1U << (width() - 1); - } - - static constexpr value_type mask() { + public: + using value_type = uint32_t; + + constexpr CRC( + const value_type truncated_polynomial, + const value_type initial_remainder = 0, + const value_type final_xor_value = 0) + : truncated_polynomial{truncated_polynomial}, + initial_remainder{initial_remainder}, + final_xor_value{final_xor_value}, + remainder{initial_remainder} { + } + + value_type get_initial_remainder() const { + return initial_remainder; + } + + void reset(value_type new_initial_remainder) { + remainder = new_initial_remainder; + } + + void reset() { + remainder = initial_remainder; + } + + void process_bit(bool bit) { + remainder ^= (bit ? top_bit() : 0U); + const auto do_poly_div = static_cast(remainder & top_bit()); + remainder <<= 1; + if (do_poly_div) { + remainder ^= truncated_polynomial; + } + } + + void process_bits(value_type bits, size_t bit_count) { + if (RevIn) { + process_bits_lsb_first(bits, bit_count); + } else { + process_bits_msb_first(bits, bit_count); + } + } + + void process_byte(const uint8_t byte) { + process_bits(byte, 8); + } + + void process_bytes(const void* const data, const size_t length) { + const uint8_t* const p = reinterpret_cast(data); + for (size_t i = 0; i < length; i++) { + process_byte(p[i]); + } + } + + template + void process_bytes(const std::array& data) { + process_bytes(data.data(), data.size()); + } + + value_type checksum() const { + return ((RevOut ? reflect(remainder) : remainder) ^ final_xor_value) & mask(); + } + + private: + const value_type truncated_polynomial; + const value_type initial_remainder; + const value_type final_xor_value; + value_type remainder; + + static constexpr size_t width() { + return Width; + } + + static constexpr value_type top_bit() { + return 1U << (width() - 1); + } + + static constexpr value_type mask() { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshift-count-overflow" - return (~(~(0UL) << width())); + return (~(~(0UL) << width())); #pragma GCC diagnostic pop - } - - static value_type reflect(value_type x) { - value_type reflection = 0; - for(size_t i=0; i>= 1; - } - return reflection; - } - - void process_bits_msb_first(value_type bits, size_t bit_count) { - constexpr auto digits = std::numeric_limits::digits; - constexpr auto mask = static_cast(1) << (digits - 1); - - bits <<= (std::numeric_limits::digits - bit_count); - for(size_t i=bit_count; i>0; --i, bits <<= 1) { - process_bit(static_cast(bits & mask)); - } - } - - void process_bits_lsb_first(value_type bits, size_t bit_count) { - for(size_t i=bit_count; i>0; --i, bits >>= 1) { - process_bit(static_cast(bits & 0x01)); - } - } + } + + static value_type reflect(value_type x) { + value_type reflection = 0; + for (size_t i = 0; i < width(); ++i) { + reflection <<= 1; + reflection |= (x & 1); + x >>= 1; + } + return reflection; + } + + void process_bits_msb_first(value_type bits, size_t bit_count) { + constexpr auto digits = std::numeric_limits::digits; + constexpr auto mask = static_cast(1) << (digits - 1); + + bits <<= (std::numeric_limits::digits - bit_count); + for (size_t i = bit_count; i > 0; --i, bits <<= 1) { + process_bit(static_cast(bits & mask)); + } + } + + void process_bits_lsb_first(value_type bits, size_t bit_count) { + for (size_t i = bit_count; i > 0; --i, bits >>= 1) { + process_bit(static_cast(bits & 0x01)); + } + } }; class Adler32 { -public: - void feed(const uint8_t v) { - feed_one(v); - } - - void feed(const void* const data, const size_t n) { - const uint8_t* const p = reinterpret_cast(data); - for(size_t i=0; i - void feed(const T& a) { - feed(a.data(), sizeof(T)); - } - - std::array bytes() const { - return { - static_cast((b >> 8) & 0xff), - static_cast((b >> 0) & 0xff), - static_cast((a >> 8) & 0xff), - static_cast((a >> 0) & 0xff) - }; - } - -private: - static constexpr uint32_t mod = 65521; - - uint32_t a { 1 }; - uint32_t b { 0 }; - - void feed_one(const uint8_t c) { - a = (a + c) % mod; - b = (b + a) % mod; - } + public: + void feed(const uint8_t v) { + feed_one(v); + } + + void feed(const void* const data, const size_t n) { + const uint8_t* const p = reinterpret_cast(data); + for (size_t i = 0; i < n; i++) { + feed_one(p[i]); + } + } + + template + void feed(const T& a) { + feed(a.data(), sizeof(T)); + } + + std::array bytes() const { + return { + static_cast((b >> 8) & 0xff), + static_cast((b >> 0) & 0xff), + static_cast((a >> 8) & 0xff), + static_cast((a >> 0) & 0xff)}; + } + + private: + static constexpr uint32_t mod = 65521; + + uint32_t a{1}; + uint32_t b{0}; + + void feed_one(const uint8_t c) { + a = (a + c) % mod; + b = (b + a) % mod; + } }; -#endif/*__CRC_H__*/ +#endif /*__CRC_H__*/ diff --git a/firmware/common/dsp_fft.hpp b/firmware/common/dsp_fft.hpp index f830c12ba..2fade8688 100644 --- a/firmware/common/dsp_fft.hpp +++ b/firmware/common/dsp_fft.hpp @@ -36,107 +36,104 @@ #include "sine_table_int8.hpp" namespace std { - /* https://github.com/AE9RB/fftbench/blob/master/cxlr.hpp - * Nice trick from AE9RB (David Turnbull) to get compiler to produce simpler - * fma (fused multiply-accumulate) instead of worrying about NaN handling - */ - inline complex - operator*(const complex& v1, const complex& v2) { - return complex { - v1.real() * v2.real() - v1.imag() * v2.imag(), - v1.real() * v2.imag() + v1.imag() * v2.real() - }; - } +/* https://github.com/AE9RB/fftbench/blob/master/cxlr.hpp + * Nice trick from AE9RB (David Turnbull) to get compiler to produce simpler + * fma (fused multiply-accumulate) instead of worrying about NaN handling + */ +inline complex +operator*(const complex& v1, const complex& v2) { + return complex{ + v1.real() * v2.real() - v1.imag() * v2.imag(), + v1.real() * v2.imag() + v1.imag() * v2.real()}; +} } /* namespace std */ -template +template void fft_swap(const buffer_c16_t src, std::array& dst) { - static_assert(power_of_two(N), "only defined for N == power of two"); - - for(size_t i=0; i> (32 - log_2(N)); - const auto s = src.p[i]; - dst[i_rev] = { - static_cast(s.real()), - static_cast(s.imag()) - }; - } + static_assert(power_of_two(N), "only defined for N == power of two"); + + for (size_t i = 0; i < N; i++) { + const size_t i_rev = __RBIT(i) >> (32 - log_2(N)); + const auto s = src.p[i]; + dst[i_rev] = { + static_cast(s.real()), + static_cast(s.imag())}; + } } -template +template void fft_swap(const std::array& src, std::array& dst) { - static_assert(power_of_two(N), "only defined for N == power of two"); - - for(size_t i=0; i> (32 - log_2(N)); - const auto s = src[i]; - dst[i_rev] = { - static_cast(s.real()), - static_cast(s.imag()) - }; - } + static_assert(power_of_two(N), "only defined for N == power of two"); + + for (size_t i = 0; i < N; i++) { + const size_t i_rev = __RBIT(i) >> (32 - log_2(N)); + const auto s = src[i]; + dst[i_rev] = { + static_cast(s.real()), + static_cast(s.imag())}; + } } -template +template void fft_swap(const std::array& src, std::array& dst) { - static_assert(power_of_two(N), "only defined for N == power of two"); + static_assert(power_of_two(N), "only defined for N == power of two"); - for(size_t i=0; i> (32 - log_2(N)); - dst[i_rev] = src[i]; - } + for (size_t i = 0; i < N; i++) { + const size_t i_rev = __RBIT(i) >> (32 - log_2(N)); + dst[i_rev] = src[i]; + } } -template +template void fft_swap_in_place(std::array& data) { - static_assert(power_of_two(N), "only defined for N == power of two"); + static_assert(power_of_two(N), "only defined for N == power of two"); - for(size_t i=0; i> (32 - log_2(N)); - std::swap(data[i], data[i_rev]); - } + for (size_t i = 0; i < N / 2; i++) { + const size_t i_rev = __RBIT(i) >> (32 - log_2(N)); + std::swap(data[i], data[i_rev]); + } } /* http://beige.ucs.indiana.edu/B673/node14.html */ /* http://www.drdobbs.com/cpp/a-simple-and-efficient-fft-implementatio/199500857?pgno=3 */ -template +template void fft_c_preswapped(std::array& data, const size_t from, const size_t to) { - static_assert(power_of_two(N), "only defined for N == power of two"); - constexpr auto K = log_2(N); - if ((to > K) || (from > K)) return; - - constexpr size_t K_max = 8; - static_assert(K <= K_max, "No FFT twiddle factors for K > 8"); - static constexpr std::array, K_max> wp_table { { - { -2.0f, 0.0f }, // 2 - { -1.0f, -1.0f }, // 4 - { -0.2928932188134524756f, -0.7071067811865475244f }, // 8 - { -0.076120467488713243872f, -0.38268343236508977173f }, // 16 - { -0.019214719596769550874f, -0.19509032201612826785f }, // 32 - { -0.0048152733278031137552f, -0.098017140329560601994f }, // 64 - { -0.0012045437948276072852f, -0.049067674327418014255f }, // 128 - { -0.00030118130379577988423f, -0.024541228522912288032f }, // 256 - } }; - - /* Provide data to this function, pre-swapped. */ - for(size_t k = from; k < to; k++) { - const size_t mmax = 1 << k; - const auto wp = wp_table[k]; - T w { 1.0f, 0.0f }; - for(size_t m = 0; m < mmax; ++m) { - for(size_t i = m; i < N; i += mmax * 2) { - const size_t j = i + mmax; - const T temp = w * data[j]; - data[j] = data[i] - temp; - data[i] += temp; - } - w += w * wp; - } - } + static_assert(power_of_two(N), "only defined for N == power of two"); + constexpr auto K = log_2(N); + if ((to > K) || (from > K)) return; + + constexpr size_t K_max = 8; + static_assert(K <= K_max, "No FFT twiddle factors for K > 8"); + static constexpr std::array, K_max> wp_table{{ + {-2.0f, 0.0f}, // 2 + {-1.0f, -1.0f}, // 4 + {-0.2928932188134524756f, -0.7071067811865475244f}, // 8 + {-0.076120467488713243872f, -0.38268343236508977173f}, // 16 + {-0.019214719596769550874f, -0.19509032201612826785f}, // 32 + {-0.0048152733278031137552f, -0.098017140329560601994f}, // 64 + {-0.0012045437948276072852f, -0.049067674327418014255f}, // 128 + {-0.00030118130379577988423f, -0.024541228522912288032f}, // 256 + }}; + + /* Provide data to this function, pre-swapped. */ + for (size_t k = from; k < to; k++) { + const size_t mmax = 1 << k; + const auto wp = wp_table[k]; + T w{1.0f, 0.0f}; + for (size_t m = 0; m < mmax; ++m) { + for (size_t i = m; i < N; i += mmax * 2) { + const size_t j = i + mmax; + const T temp = w * data[j]; + data[j] = data[i] - temp; + data[i] += temp; + } + w += w * wp; + } + } } -/* +/* ifft(v,N): [0] If N==1 then return. [1] For k = 0 to N/2-1, let ve[k] = v[2*k] @@ -149,32 +146,32 @@ void fft_c_preswapped(std::array& data, const size_t from, const size_t to [8] Let v[m] = ve[m] + w*vo[m] [9] Let v[m+N/2] = ve[m] - w*vo[m] */ -template -void ifft( T *v, int n, T *tmp ) -{ - if(n>1) { - int k,m; - T z, w, *vo, *ve; - ve = tmp; vo = tmp+n/2; - for(k=0; k +void ifft(T* v, int n, T* tmp) { + if (n > 1) { + int k, m; + T z, w, *vo, *ve; + ve = tmp; + vo = tmp + n / 2; + for (k = 0; k < n / 2; k++) { + ve[k] = v[2 * k]; + vo[k] = v[2 * k + 1]; } - ifft( ve, n/2, v ); /* FFT on even-indexed elements of v[] */ - ifft( vo, n/2, v ); /* FFT on odd-indexed elements of v[] */ - for(m=0; m +template struct fir_taps_real { - float low_frequency_normalized; - float high_frequency_normalized; - float transition_normalized; - std::array taps; + float low_frequency_normalized; + float high_frequency_normalized; + float transition_normalized; + std::array taps; }; -template +template struct fir_taps_complex { - float low_frequency_normalized; - float high_frequency_normalized; - float transition_normalized; - std::array taps; + float low_frequency_normalized; + float high_frequency_normalized; + float transition_normalized; + std::array taps; }; // NBFM 16K0F3E emission type ///////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000 -constexpr fir_taps_real<24> taps_16k0_decim_0 { - .low_frequency_normalized = -8000.0f / 3072000.0f, - .high_frequency_normalized = 8000.0f / 3072000.0f, - .transition_normalized = 336000.0f / 3072000.0f, - .taps = { { - 1, 67, 165, 340, 599, 944, 1361, 1820, - 2278, 2684, 2988, 3152, 3152, 2988, 2684, 2278, - 1820, 1361, 944, 599, 340, 165, 67, 1, - } }, +constexpr fir_taps_real<24> taps_16k0_decim_0{ + .low_frequency_normalized = -8000.0f / 3072000.0f, + .high_frequency_normalized = 8000.0f / 3072000.0f, + .transition_normalized = 336000.0f / 3072000.0f, + .taps = {{ + 1, + 67, + 165, + 340, + 599, + 944, + 1361, + 1820, + 2278, + 2684, + 2988, + 3152, + 3152, + 2988, + 2684, + 2278, + 1820, + 1361, + 944, + 599, + 340, + 165, + 67, + 1, + }}, }; // IFIR prototype filter: fs=384000, pass=8000, stop=40000, decim=8, fout=48000 -constexpr fir_taps_real<32> taps_16k0_decim_1 { - .low_frequency_normalized = -8000.0f / 384000.0f, - .high_frequency_normalized = 8000.0f / 384000.0f, - .transition_normalized = 32000.0f / 384000.0f, - .taps = { { - -26, -125, -180, -275, -342, -359, -286, -90, - 250, 733, 1337, 2011, 2688, 3289, 3740, 3982, - 3982, 3740, 3289, 2688, 2011, 1337, 733, 250, - -90, -286, -359, -342, -275, -180, -125, -26, - } }, +constexpr fir_taps_real<32> taps_16k0_decim_1{ + .low_frequency_normalized = -8000.0f / 384000.0f, + .high_frequency_normalized = 8000.0f / 384000.0f, + .transition_normalized = 32000.0f / 384000.0f, + .taps = {{ + -26, + -125, + -180, + -275, + -342, + -359, + -286, + -90, + 250, + 733, + 1337, + 2011, + 2688, + 3289, + 3740, + 3982, + 3982, + 3740, + 3289, + 2688, + 2011, + 1337, + 733, + 250, + -90, + -286, + -359, + -342, + -275, + -180, + -125, + -26, + }}, }; // Channel filter: fs=48000, pass=8000, stop=12400, decim=1, fout=48000 -constexpr fir_taps_real<32> taps_16k0_channel { - .low_frequency_normalized = -8000.0f / 48000.0f, - .high_frequency_normalized = 8000.0f / 48000.0f, - .transition_normalized = 4400.0f / 48000.0f, - .taps = { { - -73, -285, -376, -8, 609, 538, -584, -1387, - -148, 2173, 1959, -2146, -5267, -297, 12915, 24737, - 24737, 12915, -297, -5267, -2146, 1959, 2173, -148, - -1387, -584, 538, 609, -8, -376, -285, -73, - } }, +constexpr fir_taps_real<32> taps_16k0_channel{ + .low_frequency_normalized = -8000.0f / 48000.0f, + .high_frequency_normalized = 8000.0f / 48000.0f, + .transition_normalized = 4400.0f / 48000.0f, + .taps = {{ + -73, + -285, + -376, + -8, + 609, + 538, + -584, + -1387, + -148, + 2173, + 1959, + -2146, + -5267, + -297, + 12915, + 24737, + 24737, + 12915, + -297, + -5267, + -2146, + 1959, + 2173, + -148, + -1387, + -584, + 538, + 609, + -8, + -376, + -285, + -73, + }}, }; // NBFM 11K0F3E emission type ///////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=5500, stop=341500, decim=8, fout=384000 -constexpr fir_taps_real<24> taps_11k0_decim_0 { - .low_frequency_normalized = -5500.0f / 3072000.0f, - .high_frequency_normalized = 5500.0f / 3072000.0f, - .transition_normalized = 336000.0f / 3072000.0f, - .taps = { { - 38, 102, 220, 406, 668, 1004, 1397, 1822, - 2238, 2603, 2875, 3020, 3020, 2875, 2603, 2238, - 1822, 1397, 1004, 668, 406, 220, 102, 38, - } }, +constexpr fir_taps_real<24> taps_11k0_decim_0{ + .low_frequency_normalized = -5500.0f / 3072000.0f, + .high_frequency_normalized = 5500.0f / 3072000.0f, + .transition_normalized = 336000.0f / 3072000.0f, + .taps = {{ + 38, + 102, + 220, + 406, + 668, + 1004, + 1397, + 1822, + 2238, + 2603, + 2875, + 3020, + 3020, + 2875, + 2603, + 2238, + 1822, + 1397, + 1004, + 668, + 406, + 220, + 102, + 38, + }}, }; // IFIR prototype filter: fs=384000, pass=5500, stop=42500, decim=8, fout=48000 -constexpr fir_taps_real<32> taps_11k0_decim_1 { - .low_frequency_normalized = -5500.0f / 384000.0f, - .high_frequency_normalized = 5500.0f / 384000.0f, - .transition_normalized = 37000.0f / 384000.0f, - .taps = { { - -42, -87, -157, -234, -298, -318, -255, -75, - 246, 713, 1306, 1976, 2656, 3265, 3724, 3971, - 3971, 3724, 3265, 2656, 1976, 1306, 713, 246, - -75, -255, -318, -298, -234, -157, -87, -42, - } }, +constexpr fir_taps_real<32> taps_11k0_decim_1{ + .low_frequency_normalized = -5500.0f / 384000.0f, + .high_frequency_normalized = 5500.0f / 384000.0f, + .transition_normalized = 37000.0f / 384000.0f, + .taps = {{ + -42, + -87, + -157, + -234, + -298, + -318, + -255, + -75, + 246, + 713, + 1306, + 1976, + 2656, + 3265, + 3724, + 3971, + 3971, + 3724, + 3265, + 2656, + 1976, + 1306, + 713, + 246, + -75, + -255, + -318, + -298, + -234, + -157, + -87, + -42, + }}, }; // Channel filter: fs=48000, pass=5500, stop=8900, decim=1, fout=48000 -constexpr fir_taps_real<32> taps_11k0_channel { - .low_frequency_normalized = -5500.0f / 48000.0f, - .high_frequency_normalized = 5500.0f / 48000.0f, - .transition_normalized = 3400.0f / 48000.0f, - .taps = { { - -68, -345, -675, -867, -582, 247, 1222, 1562, - 634, -1379, -3219, -3068, 310, 6510, 13331, 17795, - 17795, 13331, 6510, 310, -3068, -3219, -1379, 634, - 1562, 1222, 247, -582, -867, -675, -345, -68, - } }, +constexpr fir_taps_real<32> taps_11k0_channel{ + .low_frequency_normalized = -5500.0f / 48000.0f, + .high_frequency_normalized = 5500.0f / 48000.0f, + .transition_normalized = 3400.0f / 48000.0f, + .taps = {{ + -68, + -345, + -675, + -867, + -582, + 247, + 1222, + 1562, + 634, + -1379, + -3219, + -3068, + 310, + 6510, + 13331, + 17795, + 17795, + 13331, + 6510, + 310, + -3068, + -3219, + -1379, + 634, + 1562, + 1222, + 247, + -582, + -867, + -675, + -345, + -68, + }}, }; // NBFM 8K50F3E emission type ///////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=4250, stop=340250, decim=8, fout=384000 -constexpr fir_taps_real<24> taps_4k25_decim_0 { - .low_frequency_normalized = -4250.0f / 3072000.0f, - .high_frequency_normalized = 4250.0f / 3072000.0f, - .transition_normalized = 33600.0f / 3072000.0f, - .taps = { { - 38, 103, 222, 409, 671, 1006, 1399, 1821, - 2236, 2599, 2868, 3012, 3012, 2868, 2599, 2236, - 1821, 1399, 1006, 671, 409, 222, 103, 38, - } }, +constexpr fir_taps_real<24> taps_4k25_decim_0{ + .low_frequency_normalized = -4250.0f / 3072000.0f, + .high_frequency_normalized = 4250.0f / 3072000.0f, + .transition_normalized = 33600.0f / 3072000.0f, + .taps = {{ + 38, + 103, + 222, + 409, + 671, + 1006, + 1399, + 1821, + 2236, + 2599, + 2868, + 3012, + 3012, + 2868, + 2599, + 2236, + 1821, + 1399, + 1006, + 671, + 409, + 222, + 103, + 38, + }}, }; // IFIR prototype filter: fs=384000, pass=4250, stop=43750, decim=8, fout=48000 -constexpr fir_taps_real<32> taps_4k25_decim_1 { - .low_frequency_normalized = -4250.0f / 384000.0f, - .high_frequency_normalized = 4250.0f / 384000.0f, - .transition_normalized = 39500.0f / 384000.0f, - .taps = { { - -33, -74, -139, -214, -280, -306, -254, -87, - 222, 682, 1274, 1951, 2644, 3268, 3741, 3996, - 3996, 3741, 3268, 2644, 1951, 1274, 682, 222, - -87, -254, -306, -280, -214, -139, -74, -33, - } }, +constexpr fir_taps_real<32> taps_4k25_decim_1{ + .low_frequency_normalized = -4250.0f / 384000.0f, + .high_frequency_normalized = 4250.0f / 384000.0f, + .transition_normalized = 39500.0f / 384000.0f, + .taps = {{ + -33, + -74, + -139, + -214, + -280, + -306, + -254, + -87, + 222, + 682, + 1274, + 1951, + 2644, + 3268, + 3741, + 3996, + 3996, + 3741, + 3268, + 2644, + 1951, + 1274, + 682, + 222, + -87, + -254, + -306, + -280, + -214, + -139, + -74, + -33, + }}, }; // Channel filter: fs=48000, pass=4250, stop=7900, decim=1, fout=48000 -constexpr fir_taps_real<32> taps_4k25_channel { - .low_frequency_normalized = -4250.0f / 48000.0f, - .high_frequency_normalized = 4250.0f / 48000.0f, - .transition_normalized = 3650.0f / 48000.0f, - .taps = { { - -58, -14, 153, 484, 871, 1063, 770, -141, - -1440, -2488, -2435, -614, 3035, 7771, 12226, 14927, - 14927, 12226, 7771, 3035, -614, -2435, -2488, -1440, - -141, 770, 1063, 871, 484, 153, -14, -58, - } }, +constexpr fir_taps_real<32> taps_4k25_channel{ + .low_frequency_normalized = -4250.0f / 48000.0f, + .high_frequency_normalized = 4250.0f / 48000.0f, + .transition_normalized = 3650.0f / 48000.0f, + .taps = {{ + -58, + -14, + 153, + 484, + 871, + 1063, + 770, + -141, + -1440, + -2488, + -2435, + -614, + 3035, + 7771, + 12226, + 14927, + 14927, + 12226, + 7771, + 3035, + -614, + -2435, + -2488, + -1440, + -141, + 770, + 1063, + 871, + 484, + 153, + -14, + -58, + }}, }; /* CTCSS audio filter */ @@ -172,16 +403,16 @@ constexpr fir_taps_real<32> taps_4k25_channel { * sum(abs(taps)): 125270 */ /*constexpr fir_taps_real<64> taps_64_lp_025_025 { - .taps = { { - 0, 0, -3, -7, -13, -20, -27, -32, - -34, -33, -25, -10, 13, 47, 94, 152, - 223, 307, 402, 508, 622, 742, 866, 991, - 1113, 1229, 1336, 1430, 1510, 1571, 1614, 1635, - 1635, 1614, 1571, 1510, 1430, 1336, 1229, 1113, - 991, 866, 742, 622, 508, 402, 307, 223, - 152, 94, 47, 13, -10, -25, -33, -34, - -32, -27, -20, -13, -7, -3, 0, 0 - } }, + .taps = { { + 0, 0, -3, -7, -13, -20, -27, -32, + -34, -33, -25, -10, 13, 47, 94, 152, + 223, 307, 402, 508, 622, 742, 866, 991, + 1113, 1229, 1336, 1430, 1510, 1571, 1614, 1635, + 1635, 1614, 1571, 1510, 1430, 1336, 1229, 1113, + 991, 866, 742, 622, 508, 402, 307, 223, + 152, 94, 47, 13, -10, -25, -33, -34, + -32, -27, -20, -13, -7, -3, 0, 0 + } }, };*/ /* CTCSS audio filter */ @@ -191,240 +422,587 @@ constexpr fir_taps_real<32> taps_4k25_channel { * Padded to multiple of four taps for unrolled FIR code. * sum(abs(taps)): 125270 */ -constexpr fir_taps_real<64> taps_64_lp_025_025 { - .low_frequency_normalized = 0, - .high_frequency_normalized = 0, - .transition_normalized = 0, - .taps = { { - 0, 0, 2, 6, 12, 20, 32, 46, - 64, 85, 110, 138, 169, 204, 241, 281, - 323, 367, 412, 457, 502, 547, 590, 631, - 669, 704, 735, 762, 784, 801, 812, 818, - 818, 812, 801, 784, 762, 735, 704, 669, - 631, 590, 547, 502, 457, 412, 367, 323, - 281, 241, 204, 169, 138, 110, 85, 64, - 46, 32, 20, 12, 6, 2, 0, 0 - } }, +constexpr fir_taps_real<64> taps_64_lp_025_025{ + .low_frequency_normalized = 0, + .high_frequency_normalized = 0, + .transition_normalized = 0, + .taps = {{0, 0, 2, 6, 12, 20, 32, 46, + 64, 85, 110, 138, 169, 204, 241, 281, + 323, 367, 412, 457, 502, 547, 590, 631, + 669, 704, 735, 762, 784, 801, 812, 818, + 818, 812, 801, 784, 762, 735, 704, 669, + 631, 590, 547, 502, 457, 412, 367, 323, + 281, 241, 204, 169, 138, 110, 85, 64, + 46, 32, 20, 12, 6, 2, 0, 0}}, }; // DSB AM 6K00A3E emission type /////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=3000, stop=339000, decim=8, fout=384000 -constexpr fir_taps_real<24> taps_6k0_decim_0 { - .low_frequency_normalized = -3000.0f / 3072000.0f, - .high_frequency_normalized = 3000.0f / 3072000.0f, - .transition_normalized = 336000.0f / 3072000.0f, - .taps = { { - 39, 104, 224, 412, 674, 1008, 1400, 1821, - 2234, 2594, 2863, 3006, 3006, 2863, 2594, 2234, - 1821, 1400, 1008, 674, 412, 224, 104, 39, - } }, +constexpr fir_taps_real<24> taps_6k0_decim_0{ + .low_frequency_normalized = -3000.0f / 3072000.0f, + .high_frequency_normalized = 3000.0f / 3072000.0f, + .transition_normalized = 336000.0f / 3072000.0f, + .taps = {{ + 39, + 104, + 224, + 412, + 674, + 1008, + 1400, + 1821, + 2234, + 2594, + 2863, + 3006, + 3006, + 2863, + 2594, + 2234, + 1821, + 1400, + 1008, + 674, + 412, + 224, + 104, + 39, + }}, }; // IFIR prototype filter: fs=384000, pass=3000, stop=45000, decim=8, fout=48000 -constexpr fir_taps_real<32> taps_6k0_decim_1 { - .low_frequency_normalized = -3000.0f / 384000.0f, - .high_frequency_normalized = 3000.0f / 384000.0f, - .transition_normalized = 43000.0f / 384000.0f, - .taps = { { - -26, -63, -123, -195, -263, -295, -253, -99, - 199, 651, 1242, 1927, 2633, 3273, 3760, 4023, - 4023, 3760, 3273, 2633, 1927, 1242, 651, 199, - -99, -253, -295, -263, -195, -123, -63, -26, - } }, +constexpr fir_taps_real<32> taps_6k0_decim_1{ + .low_frequency_normalized = -3000.0f / 384000.0f, + .high_frequency_normalized = 3000.0f / 384000.0f, + .transition_normalized = 43000.0f / 384000.0f, + .taps = {{ + -26, + -63, + -123, + -195, + -263, + -295, + -253, + -99, + 199, + 651, + 1242, + 1927, + 2633, + 3273, + 3760, + 4023, + 4023, + 3760, + 3273, + 2633, + 1927, + 1242, + 651, + 199, + -99, + -253, + -295, + -263, + -195, + -123, + -63, + -26, + }}, }; // IFIR prototype filter: fs=48000, pass=3000, stop=6700, decim=4, fout=12000 -constexpr fir_taps_real<32> taps_6k0_decim_2 { - .low_frequency_normalized = -3000.0f / 48000.0f, - .high_frequency_normalized = 3000.0f / 48000.0f, - .transition_normalized = 3700.0f / 48000.0f, - .taps = { { - 95, 178, 247, 208, -21, -474, -1080, -1640, - -1857, -1411, -83, 2134, 4978, 7946, 10413, 11815, - 11815, 10413, 7946, 4978, 2134, -83, -1411, -1857, - -1640, -1080, -474, -21, 208, 247, 178, 95, - } }, -}; +constexpr fir_taps_real<32> taps_6k0_decim_2{ + .low_frequency_normalized = -3000.0f / 48000.0f, + .high_frequency_normalized = 3000.0f / 48000.0f, + .transition_normalized = 3700.0f / 48000.0f, + .taps = {{ + 95, + 178, + 247, + 208, + -21, + -474, + -1080, + -1640, + -1857, + -1411, + -83, + 2134, + 4978, + 7946, + 10413, + 11815, + 11815, + 10413, + 7946, + 4978, + 2134, + -83, + -1411, + -1857, + -1640, + -1080, + -474, + -21, + 208, + 247, + 178, + 95, + }}, +}; // IFIR prototype filter fs=48000 ; pass=4500 (cutt off -3dBs) , stop=8000 (<-60dBs), decim=4, fout=12000 -// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivated from taps_6k0_decim_2 ) -// Pre-decimate LPF FIR filter design Created with SciPy Python with the "window method", num_taps = 32, cut_off = 5150. sample_rate = 48000 # Hz, +// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivated from taps_6k0_decim_2 ) +// Pre-decimate LPF FIR filter design Created with SciPy Python with the "window method", num_taps = 32, cut_off = 5150. sample_rate = 48000 # Hz, // Created with h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2, window=('chebwin',50)) , achieving good STOP band plot < -60 dB's with some ripple. // post-scaled h taps to avoid decimals , targeting <= similar int values as previous taps_6k0_dsb_channel peak < 32.767 (2 exp 15) and similar H(f)gain -constexpr fir_taps_real<32> taps_9k0_decim_2 { - .low_frequency_normalized = -4500.0f / 48000.0f, // Negative -cutt off freq -3dB (real achieved data ,in the plot and measurements) - .high_frequency_normalized = 4500.0f / 48000.0f, // Positive +cutt off freq -3dB (idem) - .transition_normalized = 3500.0f / 48000.0f, // 3500 Hz = (8000 Hz - 4500 Hz) (both from plot H(f) curve plot) - .taps = { { - -53, -30, 47, 198, 355, 372, 89, -535, - -1307, -1771, -1353, 370, 3384, 7109, 10535, 12591, - 12591, 10535, 7109, 3384, 370, -1353, -1771, -1307, - -535, 89, 372, 355, 198, 47, -30, -53 - } }, +constexpr fir_taps_real<32> taps_9k0_decim_2{ + .low_frequency_normalized = -4500.0f / 48000.0f, // Negative -cutt off freq -3dB (real achieved data ,in the plot and measurements) + .high_frequency_normalized = 4500.0f / 48000.0f, // Positive +cutt off freq -3dB (idem) + .transition_normalized = 3500.0f / 48000.0f, // 3500 Hz = (8000 Hz - 4500 Hz) (both from plot H(f) curve plot) + .taps = {{-53, -30, 47, 198, 355, 372, 89, -535, + -1307, -1771, -1353, 370, 3384, 7109, 10535, 12591, + 12591, 10535, 7109, 3384, 370, -1353, -1771, -1307, + -535, 89, 372, 355, 198, 47, -30, -53}}, }; // Channel filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000 /* NOTE: Slightly less than 1.0 gain (normalized to 65536) due to max(taps) being * slightly larger than 32767 (33312). -*/ -constexpr fir_taps_complex<64> taps_6k0_dsb_channel { - .low_frequency_normalized = -3000.0f / 12000.0f, - .high_frequency_normalized = 3000.0f / 12000.0f, - .transition_normalized = 300.0f / 12000.0f, - .taps = { { - { -69, 0 }, { -140, 0 }, { 119, 0 }, { 89, 0 }, - { -132, 0 }, { -134, 0 }, { 197, 0 }, { 167, 0 }, - { -273, 0 }, { -206, 0 }, { 372, 0 }, { 247, 0 }, - { -497, 0 }, { -289, 0 }, { 654, 0 }, { 331, 0 }, - { -854, 0 }, { -372, 0 }, { 1112, 0 }, { 411, 0 }, - { -1455, 0 }, { -446, 0 }, { 1933, 0 }, { 476, 0 }, - { -2654, 0 }, { -501, 0 }, { 3902, 0 }, { 520, 0 }, - { -6717, 0 }, { -531, 0 }, { 20478, 0 }, { 32767, 0 }, - { 20478, 0 }, { -531, 0 }, { -6717, 0 }, { 520, 0 }, - { 3902, 0 }, { -501, 0 }, { -2654, 0 }, { 476, 0 }, - { 1933, 0 }, { -446, 0 }, { -1455, 0 }, { 411, 0 }, - { 1112, 0 }, { -372, 0 }, { -854, 0 }, { 331, 0 }, - { 654, 0 }, { -289, 0 }, { -497, 0 }, { 247, 0 }, - { 372, 0 }, { -206, 0 }, { -273, 0 }, { 167, 0 }, - { 197, 0 }, { -134, 0 }, { -132, 0 }, { 89, 0 }, - { 119, 0 }, { -140, 0 }, { -69, 0 }, { 0, 0 }, - } }, + */ +constexpr fir_taps_complex<64> taps_6k0_dsb_channel{ + .low_frequency_normalized = -3000.0f / 12000.0f, + .high_frequency_normalized = 3000.0f / 12000.0f, + .transition_normalized = 300.0f / 12000.0f, + .taps = {{ + {-69, 0}, + {-140, 0}, + {119, 0}, + {89, 0}, + {-132, 0}, + {-134, 0}, + {197, 0}, + {167, 0}, + {-273, 0}, + {-206, 0}, + {372, 0}, + {247, 0}, + {-497, 0}, + {-289, 0}, + {654, 0}, + {331, 0}, + {-854, 0}, + {-372, 0}, + {1112, 0}, + {411, 0}, + {-1455, 0}, + {-446, 0}, + {1933, 0}, + {476, 0}, + {-2654, 0}, + {-501, 0}, + {3902, 0}, + {520, 0}, + {-6717, 0}, + {-531, 0}, + {20478, 0}, + {32767, 0}, + {20478, 0}, + {-531, 0}, + {-6717, 0}, + {520, 0}, + {3902, 0}, + {-501, 0}, + {-2654, 0}, + {476, 0}, + {1933, 0}, + {-446, 0}, + {-1455, 0}, + {411, 0}, + {1112, 0}, + {-372, 0}, + {-854, 0}, + {331, 0}, + {654, 0}, + {-289, 0}, + {-497, 0}, + {247, 0}, + {372, 0}, + {-206, 0}, + {-273, 0}, + {167, 0}, + {197, 0}, + {-134, 0}, + {-132, 0}, + {89, 0}, + {119, 0}, + {-140, 0}, + {-69, 0}, + {0, 0}, + }}, }; // Channel filter: fs=12000, pass=4500 (cutt off -3dBs), stop=4940 (<-60dBs), decim=1, fout=12000 (*1) real frec pass / stop , based on plotted H(f) curve) -// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivative from taps_6k0_dsb_channel) -// FIR filter design created with SciPy Python using "window method"; selected design parameters: num_taps = 64, cut_off = 4575. sample_rate = 12000 # Hz, +// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivative from taps_6k0_dsb_channel) +// FIR filter design created with SciPy Python using "window method"; selected design parameters: num_taps = 64, cut_off = 4575. sample_rate = 12000 # Hz, // Created with : h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2, window=('chebwin',50)) , achieving real plot curve (*1) with peak stop band ripple -60dBs. // post-scaled h taps to avoid decimals , targeting <= similar int values as previous taps_6k0_dsb_channel peak < 32.767 (2 exp 15), (29625) and similar H(f)gain -constexpr fir_taps_complex<64> taps_9k0_dsb_channel { - .low_frequency_normalized = -4500.0f / 12000.0f, // Negative -cutt off freq -3dB (in the H(f) curve plot) - .high_frequency_normalized = 4500.0f / 12000.0f, // Positive +cutt off freq -3dB (in the H(f) curve plot) - .transition_normalized = 440.0f / 12000.0f, // 440Hz = (4940 Hz -4500 Hz) cut-3dB's (both data comes from H(f) curve plot and confirmed by measurements ) - .taps = { { - { 2, 0 }, { -18, 0 }, { 34, 0 }, { -33, 0 }, - { 6, 0 }, { 44, 0 }, { -91, 0 }, { 96, 0 }, - { -35, 0 }, { -80, 0 }, { 193, 0 }, { -223, 0 }, - { 116, 0 }, { 112, 0 }, { -353, 0 }, { 452, 0 }, - { -293, 0 }, { -111, 0 }, { 584, 0 }, { -844, 0 }, - { 653, 0 }, { 22, 0 }, { -921, 0 }, { 1554, 0 }, - { -1422, 0 }, { 301, 0 }, { 1533, 0 }, { -3282, 0 }, - { 3804, 0 }, { -1819, 0 }, { -4605, 0 }, { 29625, 0 }, - { 29625, 0 }, { -4605, 0 }, { -1819, 0 }, { 3804, 0 }, - { -3282, 0 }, { 1533, 0 }, { 301, 0 }, { -1422, 0 }, - { 1554, 0 }, { -921, 0 }, { 22, 0 }, { 653, 0 }, - { -844, 0 }, { 584, 0 }, { -111, 0 }, { -293, 0 }, - { 452, 0 }, { -353, 0 }, { 112, 0 }, { 116, 0 }, - { -223, 0 }, { 193, 0 }, { -80, 0 }, { -35, 0 }, - { 96, 0 }, { -91, 0 }, { 44, 0 }, { 6, 0 }, - { -33, 0 }, { 34, 0 }, { -18, 0 }, { 2, 0 }, - } }, +constexpr fir_taps_complex<64> taps_9k0_dsb_channel{ + .low_frequency_normalized = -4500.0f / 12000.0f, // Negative -cutt off freq -3dB (in the H(f) curve plot) + .high_frequency_normalized = 4500.0f / 12000.0f, // Positive +cutt off freq -3dB (in the H(f) curve plot) + .transition_normalized = 440.0f / 12000.0f, // 440Hz = (4940 Hz -4500 Hz) cut-3dB's (both data comes from H(f) curve plot and confirmed by measurements ) + .taps = {{ + {2, 0}, + {-18, 0}, + {34, 0}, + {-33, 0}, + {6, 0}, + {44, 0}, + {-91, 0}, + {96, 0}, + {-35, 0}, + {-80, 0}, + {193, 0}, + {-223, 0}, + {116, 0}, + {112, 0}, + {-353, 0}, + {452, 0}, + {-293, 0}, + {-111, 0}, + {584, 0}, + {-844, 0}, + {653, 0}, + {22, 0}, + {-921, 0}, + {1554, 0}, + {-1422, 0}, + {301, 0}, + {1533, 0}, + {-3282, 0}, + {3804, 0}, + {-1819, 0}, + {-4605, 0}, + {29625, 0}, + {29625, 0}, + {-4605, 0}, + {-1819, 0}, + {3804, 0}, + {-3282, 0}, + {1533, 0}, + {301, 0}, + {-1422, 0}, + {1554, 0}, + {-921, 0}, + {22, 0}, + {653, 0}, + {-844, 0}, + {584, 0}, + {-111, 0}, + {-293, 0}, + {452, 0}, + {-353, 0}, + {112, 0}, + {116, 0}, + {-223, 0}, + {193, 0}, + {-80, 0}, + {-35, 0}, + {96, 0}, + {-91, 0}, + {44, 0}, + {6, 0}, + {-33, 0}, + {34, 0}, + {-18, 0}, + {2, 0}, + }}, }; - // USB AM 2K80J3E emission type /////////////////////////////////////////// // IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000 -constexpr fir_taps_complex<64> taps_2k8_usb_channel { - .low_frequency_normalized = 0, - .high_frequency_normalized = 3000.0f / 12000.0f, - .transition_normalized = 300.0f / 12000.0f, - .taps = { { - { -146, 0 }, { -41, -45 }, { -1, 10 }, { -95, 69 }, - { -194, -41 }, { -91, -158 }, { 14, -43 }, { -150, 67 }, - { -299, -133 }, { -100, -307 }, { 50, -86 }, { -254, 54 }, - { -453, -329 }, { -62, -587 }, { 170, -189 }, { -334, 0 }, - { -580, -645 }, { 104, -986 }, { 418, -304 }, { -412, -88 }, - { -680, -1178 }, { 527, -1623 }, { 970, -432 }, { -441, -196 }, - { -698, -2149 }, { 1617, -2800 }, { 2384, -507 }, { -429, -311 }, - { -545, -5181 }, { 6925, -7691 }, { 14340, 0 }, { 10601, 11773 }, - { -1499, 14261 }, { -8373, 6083 }, { -5095, -1083 }, { -265, -459 }, - { -753, 2318 }, { -2954, 1315 }, { -2064, -919 }, { -149, -459 }, - { -531, 920 }, { -1669, 355 }, { -1100, -800 }, { -44, -419 }, - { -346, 384 }, { -992, 0 }, { -580, -645 }, { 35, -332 }, - { -205, 149 }, { -577, -123 }, { -280, -485 }, { 80, -247 }, - { -91, 40 }, { -294, -131 }, { -101, -312 }, { 82, -142 }, - { -44, 9 }, { -147, -107 }, { -21, -197 }, { 79, -88 }, - { 10, 0 }, { -41, -45 }, { 15, -145 }, { 0, 0 }, - } }, +constexpr fir_taps_complex<64> taps_2k8_usb_channel{ + .low_frequency_normalized = 0, + .high_frequency_normalized = 3000.0f / 12000.0f, + .transition_normalized = 300.0f / 12000.0f, + .taps = {{ + {-146, 0}, + {-41, -45}, + {-1, 10}, + {-95, 69}, + {-194, -41}, + {-91, -158}, + {14, -43}, + {-150, 67}, + {-299, -133}, + {-100, -307}, + {50, -86}, + {-254, 54}, + {-453, -329}, + {-62, -587}, + {170, -189}, + {-334, 0}, + {-580, -645}, + {104, -986}, + {418, -304}, + {-412, -88}, + {-680, -1178}, + {527, -1623}, + {970, -432}, + {-441, -196}, + {-698, -2149}, + {1617, -2800}, + {2384, -507}, + {-429, -311}, + {-545, -5181}, + {6925, -7691}, + {14340, 0}, + {10601, 11773}, + {-1499, 14261}, + {-8373, 6083}, + {-5095, -1083}, + {-265, -459}, + {-753, 2318}, + {-2954, 1315}, + {-2064, -919}, + {-149, -459}, + {-531, 920}, + {-1669, 355}, + {-1100, -800}, + {-44, -419}, + {-346, 384}, + {-992, 0}, + {-580, -645}, + {35, -332}, + {-205, 149}, + {-577, -123}, + {-280, -485}, + {80, -247}, + {-91, 40}, + {-294, -131}, + {-101, -312}, + {82, -142}, + {-44, 9}, + {-147, -107}, + {-21, -197}, + {79, -88}, + {10, 0}, + {-41, -45}, + {15, -145}, + {0, 0}, + }}, }; // LSB AM 2K80J3E emission type /////////////////////////////////////////// // IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000 -constexpr fir_taps_complex<64> taps_2k8_lsb_channel { - .low_frequency_normalized = -3000.0f / 12000.0f, - .high_frequency_normalized = 0, - .transition_normalized = 300.0f / 12000.0f, - .taps = { { - { -146, 0 }, { -41, 45 }, { -1, -10 }, { -95, -69 }, - { -194, 41 }, { -91, 158 }, { 14, 43 }, { -150, -67 }, - { -299, 133 }, { -100, 307 }, { 50, 86 }, { -254, -54 }, - { -453, 329 }, { -62, 587 }, { 170, 189 }, { -334, 0 }, - { -580, 645 }, { 104, 986 }, { 418, 304 }, { -412, 88 }, - { -680, 1178 }, { 527, 1623 }, { 970, 432 }, { -441, 196 }, - { -698, 2149 }, { 1617, 2800 }, { 2384, 507 }, { -429, 311 }, - { -545, 5181 }, { 6925, 7691 }, { 14340, 0 }, { 10601, -11773 }, - { -1499, -14261 }, { -8373, -6083 }, { -5095, 1083 }, { -265, 459 }, - { -753, -2318 }, { -2954, -1315 }, { -2064, 919 }, { -149, 459 }, - { -531, -920 }, { -1669, -355 }, { -1100, 800 }, { -44, 419 }, - { -346, -384 }, { -992, 0 }, { -580, 645 }, { 35, 332 }, - { -205, -149 }, { -577, 123 }, { -280, 485 }, { 80, 247 }, - { -91, -40 }, { -294, 131 }, { -101, 312 }, { 82, 142 }, - { -44, -9 }, { -147, 107 }, { -21, 197 }, { 79, 88 }, - { 10, 0 }, { -41, 45 }, { 15, 145 }, { 0, 0 }, - } }, +constexpr fir_taps_complex<64> taps_2k8_lsb_channel{ + .low_frequency_normalized = -3000.0f / 12000.0f, + .high_frequency_normalized = 0, + .transition_normalized = 300.0f / 12000.0f, + .taps = {{ + {-146, 0}, + {-41, 45}, + {-1, -10}, + {-95, -69}, + {-194, 41}, + {-91, 158}, + {14, 43}, + {-150, -67}, + {-299, 133}, + {-100, 307}, + {50, 86}, + {-254, -54}, + {-453, 329}, + {-62, 587}, + {170, 189}, + {-334, 0}, + {-580, 645}, + {104, 986}, + {418, 304}, + {-412, 88}, + {-680, 1178}, + {527, 1623}, + {970, 432}, + {-441, 196}, + {-698, 2149}, + {1617, 2800}, + {2384, 507}, + {-429, 311}, + {-545, 5181}, + {6925, 7691}, + {14340, 0}, + {10601, -11773}, + {-1499, -14261}, + {-8373, -6083}, + {-5095, 1083}, + {-265, 459}, + {-753, -2318}, + {-2954, -1315}, + {-2064, 919}, + {-149, 459}, + {-531, -920}, + {-1669, -355}, + {-1100, 800}, + {-44, 419}, + {-346, -384}, + {-992, 0}, + {-580, 645}, + {35, 332}, + {-205, -149}, + {-577, 123}, + {-280, 485}, + {80, 247}, + {-91, -40}, + {-294, 131}, + {-101, 312}, + {82, 142}, + {-44, -9}, + {-147, 107}, + {-21, 197}, + {79, 88}, + {10, 0}, + {-41, 45}, + {15, 145}, + {0, 0}, + }}, }; // USB AM 700Hz filter: fs=12000, start=600, end=800, width=200, stop=40db, decim=1, fout=12000 -constexpr fir_taps_complex<64> taps_0k7_usb_channel { - .low_frequency_normalized = 600.0f / 12000.0f, - .high_frequency_normalized = 800.0f / 12000.0f, - .transition_normalized = 200.0f / 12000.0f, - .taps = { { - { 531, 0 }, { 192, 73 }, { 181, 163 }, { 129, 254 }, - { 34, 328 }, { -97, 364 }, { -251, 345 }, { -403, 261 }, - { -524, 111 }, { -585, -92 }, { -564, -326 }, { -448, -554 }, - { -239, -737 }, { 43, -836 }, { 366, -822 }, { 681, -681 }, - { 936, -417 }, { 1085, -56 }, { 1090, 354 }, { 935, 757 }, - { 629, 1090 }, { 205, 1296 }, { -283, 1331 }, { -766, 1180 }, - { -1172, 851 }, { -1435, 384 }, { -1510, -158 }, { -1377, -702 }, - { -1049, -1165 }, { -568, -1480 }, { 0, -1596 }, { 574, -1496 }, - { 1072, -1191 }, { 1422, -724 }, { 1576, -165 }, { 1515, 406 }, - { 1251, 908 }, { 827, 1273 }, { 309, 1453 }, { -226, 1431 }, - { -703, 1218 }, { -1058, 856 }, { -1248, 405 }, { -1257, -65 }, - { -1100, -489 }, { -810, -810 }, { -441, -992 }, { -53, -1024 }, - { 297, -916 }, { 566, -699 }, { 725, -418 }, { 765, -121 }, - { 697, 148 }, { 546, 355 }, { 348, 479 }, { 138, 517 }, - { -50, 477 }, { -194, 381 }, { -280, 252 }, { -308, 118 }, - { -285, 0 }, { -228, -87 }, { -153, -138 }, { -241, -473 }, - } }, +constexpr fir_taps_complex<64> taps_0k7_usb_channel{ + .low_frequency_normalized = 600.0f / 12000.0f, + .high_frequency_normalized = 800.0f / 12000.0f, + .transition_normalized = 200.0f / 12000.0f, + .taps = {{ + {531, 0}, + {192, 73}, + {181, 163}, + {129, 254}, + {34, 328}, + {-97, 364}, + {-251, 345}, + {-403, 261}, + {-524, 111}, + {-585, -92}, + {-564, -326}, + {-448, -554}, + {-239, -737}, + {43, -836}, + {366, -822}, + {681, -681}, + {936, -417}, + {1085, -56}, + {1090, 354}, + {935, 757}, + {629, 1090}, + {205, 1296}, + {-283, 1331}, + {-766, 1180}, + {-1172, 851}, + {-1435, 384}, + {-1510, -158}, + {-1377, -702}, + {-1049, -1165}, + {-568, -1480}, + {0, -1596}, + {574, -1496}, + {1072, -1191}, + {1422, -724}, + {1576, -165}, + {1515, 406}, + {1251, 908}, + {827, 1273}, + {309, 1453}, + {-226, 1431}, + {-703, 1218}, + {-1058, 856}, + {-1248, 405}, + {-1257, -65}, + {-1100, -489}, + {-810, -810}, + {-441, -992}, + {-53, -1024}, + {297, -916}, + {566, -699}, + {725, -418}, + {765, -121}, + {697, 148}, + {546, 355}, + {348, 479}, + {138, 517}, + {-50, 477}, + {-194, 381}, + {-280, 252}, + {-308, 118}, + {-285, 0}, + {-228, -87}, + {-153, -138}, + {-241, -473}, + }}, }; // WFM 200KF8E emission type ////////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=100000, stop=484000, decim=4, fout=768000 constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = { - .low_frequency_normalized = -100000.0f / 3072000.0f, - .high_frequency_normalized = 100000.0f / 3072000.0f, - .transition_normalized = 384000.0f / 3072000.0f, - .taps = { { - 48, -18, -151, -364, -557, -548, -139, 789, - 2187, 3800, 5230, 6071, 6071, 5230, 3800, 2187, - 789, -139, -548, -557, -364, -151, -18, 48, - } }, + .low_frequency_normalized = -100000.0f / 3072000.0f, + .high_frequency_normalized = 100000.0f / 3072000.0f, + .transition_normalized = 384000.0f / 3072000.0f, + .taps = {{ + 48, + -18, + -151, + -364, + -557, + -548, + -139, + 789, + 2187, + 3800, + 5230, + 6071, + 6071, + 5230, + 3800, + 2187, + 789, + -139, + -548, + -557, + -364, + -151, + -18, + 48, + }}, }; // IFIR prototype filter: fs=768000, pass=100000, stop=284000, decim=2, fout=384000 constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = { - .low_frequency_normalized = -100000.0f / 768000.0f, - .high_frequency_normalized = 100000.0f / 768000.0f, - .transition_normalized = 184000.0f / 768000.0f, - .taps = { { - -67, -123, 388, 622, -1342, -2185, 4599, 14486, - 14486, 4599, -2185, -1342, 622, 388, -123, -67, - } }, + .low_frequency_normalized = -100000.0f / 768000.0f, + .high_frequency_normalized = 100000.0f / 768000.0f, + .transition_normalized = 184000.0f / 768000.0f, + .taps = {{ + -67, + -123, + 388, + 622, + -1342, + -2185, + 4599, + 14486, + 14486, + 4599, + -2185, + -1342, + 622, + 388, + -123, + -67, + }}, }; /* Wideband audio filter */ @@ -434,98 +1012,256 @@ constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = { * Padded to multiple of four taps for unrolled FIR code. * sum(abs(taps)): 125270 */ -constexpr fir_taps_real<64> taps_64_lp_156_198 { - .low_frequency_normalized = -0.156f, - .high_frequency_normalized = 0.156f, - .transition_normalized = 0.04f, - .taps = { { - -27, 166, 104, -36, -174, -129, 109, 287, - 148, -232, -430, -130, 427, 597, 49, -716, - -778, 137, 1131, 957, -493, -1740, -1121, 1167, - 2733, 1252, -2633, -4899, -1336, 8210, 18660, 23254, - 18660, 8210, -1336, -4899, -2633, 1252, 2733, 1167, - -1121, -1740, -493, 957, 1131, 137, -778, -716, - 49, 597, 427, -130, -430, -232, 148, 287, - 109, -129, -174, -36, 104, 166, -27, 0, - } }, +constexpr fir_taps_real<64> taps_64_lp_156_198{ + .low_frequency_normalized = -0.156f, + .high_frequency_normalized = 0.156f, + .transition_normalized = 0.04f, + .taps = {{ + -27, + 166, + 104, + -36, + -174, + -129, + 109, + 287, + 148, + -232, + -430, + -130, + 427, + 597, + 49, + -716, + -778, + 137, + 1131, + 957, + -493, + -1740, + -1121, + 1167, + 2733, + 1252, + -2633, + -4899, + -1336, + 8210, + 18660, + 23254, + 18660, + 8210, + -1336, + -4899, + -2633, + 1252, + 2733, + 1167, + -1121, + -1740, + -493, + 957, + 1131, + 137, + -778, + -716, + 49, + 597, + 427, + -130, + -430, + -232, + 148, + 287, + 109, + -129, + -174, + -36, + 104, + 166, + -27, + 0, + }}, }; // WFM 180kHZ General purpose filter with sharp transition , it improves Commercial WFM S/N in weak signals ////////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=90000, stop=250000, decim=4, fout=768000 constexpr fir_taps_real<24> taps_180k_wfm_decim_0 = { - .low_frequency_normalized = -90000.0f / 3072000.0f, - .high_frequency_normalized = 90000.0f / 3072000.0f, - .transition_normalized = 160000.0f / 3072000.0f, - .taps = { { - 55, 122, 244, 424, 666, 965, 1308, 1669, - 2019, 2321, 2544, 2663, 2663, 2544, 2321, 2019, - 1669, 1308, 965, 666, 424, 244, 122, 55, - } }, + .low_frequency_normalized = -90000.0f / 3072000.0f, + .high_frequency_normalized = 90000.0f / 3072000.0f, + .transition_normalized = 160000.0f / 3072000.0f, + .taps = {{ + 55, + 122, + 244, + 424, + 666, + 965, + 1308, + 1669, + 2019, + 2321, + 2544, + 2663, + 2663, + 2544, + 2321, + 2019, + 1669, + 1308, + 965, + 666, + 424, + 244, + 122, + 55, + }}, }; // IFIR prototype filter: fs=768000, pass=90000, stop=110000, decim=2, fout=384000 constexpr fir_taps_real<16> taps_180k_wfm_decim_1 = { - .low_frequency_normalized = -90000.0f / 768000.0f, - .high_frequency_normalized = 90000.0f / 768000.0f, - .transition_normalized = 20000.0f / 768000.0f, - .taps = { { - 55, 19, -356, -916, -529, 2139, 6695, 10392, - 10392, 6695, 2139, -529, -916, -356, 19, 55, - } }, + .low_frequency_normalized = -90000.0f / 768000.0f, + .high_frequency_normalized = 90000.0f / 768000.0f, + .transition_normalized = 20000.0f / 768000.0f, + .taps = {{ + 55, + 19, + -356, + -916, + -529, + 2139, + 6695, + 10392, + 10392, + 6695, + 2139, + -529, + -916, + -356, + 19, + 55, + }}, }; - - // WFM 40kHZ filter for NOAA APT reception in 137Mhz band with sharp transition ////////////////////////////////////////////// // IFIR image-reject filter: fs=3072000, pass=20000, stop=97000, decim=4, fout=768000 constexpr fir_taps_real<24> taps_40k_wfm_decim_0 = { - .low_frequency_normalized = -20000.0f / 3072000.0f, - .high_frequency_normalized = 20000.0f / 3072000.0f, - .transition_normalized = 67000.0f / 3072000.0f, - .taps = { { - 46, 112, 230, 408, 650, 953, 1301, 1671, - 2029, 2340, 2570, 2692, 2692, 2570, 2340, 2029, - 1671, 1301, 953, 650, 408, 230, 112, 46, - } }, + .low_frequency_normalized = -20000.0f / 3072000.0f, + .high_frequency_normalized = 20000.0f / 3072000.0f, + .transition_normalized = 67000.0f / 3072000.0f, + .taps = {{ + 46, + 112, + 230, + 408, + 650, + 953, + 1301, + 1671, + 2029, + 2340, + 2570, + 2692, + 2692, + 2570, + 2340, + 2029, + 1671, + 1301, + 953, + 650, + 408, + 230, + 112, + 46, + }}, }; // IFIR prototype filter: fs=768000, pass=20000, stop=55000, decim=2, fout=384000 constexpr fir_taps_real<16> taps_40k_wfm_decim_1 = { - .low_frequency_normalized = -20000.0f / 768000.0f, - .high_frequency_normalized = 20000.0f / 768000.0f, - .transition_normalized = 35000.0f / 768000.0f, - .taps = { { - 83, 299, 743, 1456, 2396, 3418, 4297, 4808, - 4808, 4297, 3418, 2396, 1456, 743, 299, 83, - } }, + .low_frequency_normalized = -20000.0f / 768000.0f, + .high_frequency_normalized = 20000.0f / 768000.0f, + .transition_normalized = 35000.0f / 768000.0f, + .taps = {{ + 83, + 299, + 743, + 1456, + 2396, + 3418, + 4297, + 4808, + 4808, + 4297, + 3418, + 2396, + 1456, + 743, + 299, + 83, + }}, }; - // TPMS decimation filters //////////////////////////////////////////////// // IFIR image-reject filter: fs=2457600, pass=100000, stop=407200, decim=4, fout=614400 static constexpr fir_taps_real<24> taps_200k_decim_0 = { - .low_frequency_normalized = -100000.0f / 2457600.0f, - .high_frequency_normalized = 100000.0f / 2457600.0f, - .transition_normalized = 307200.0f / 2457600.0f, - .taps = { { - 90, 94, 4, -240, -570, -776, -563, 309, - 1861, 3808, 5618, 6710, 6710, 5618, 3808, 1861, - 309, -563, -776, -570, -240, 4, 94, 90, - } }, + .low_frequency_normalized = -100000.0f / 2457600.0f, + .high_frequency_normalized = 100000.0f / 2457600.0f, + .transition_normalized = 307200.0f / 2457600.0f, + .taps = {{ + 90, + 94, + 4, + -240, + -570, + -776, + -563, + 309, + 1861, + 3808, + 5618, + 6710, + 6710, + 5618, + 3808, + 1861, + 309, + -563, + -776, + -570, + -240, + 4, + 94, + 90, + }}, }; // IFIR prototype filter: fs=614400, pass=100000, stop=207200, decim=2, fout=307200 static constexpr fir_taps_real<16> taps_200k_decim_1 = { - .low_frequency_normalized = -100000.0f / 614400.0f, - .high_frequency_normalized = 100000.0f / 614400.0f, - .transition_normalized = 107200.0f / 614400.0f, - .taps = { { - -132, -256, 545, 834, -1507, -2401, 4666, 14583, - 14583, 4666, -2401, -1507, 834, 545, -256, -132, - } }, + .low_frequency_normalized = -100000.0f / 614400.0f, + .high_frequency_normalized = 100000.0f / 614400.0f, + .transition_normalized = 107200.0f / 614400.0f, + .taps = {{ + -132, + -256, + 545, + 834, + -1507, + -2401, + 4666, + 14583, + 14583, + 4666, + -2401, + -1507, + 834, + 545, + -256, + -132, + }}, }; -#endif/*__DSP_FIR_TAPS_H__*/ +#endif /*__DSP_FIR_TAPS_H__*/ diff --git a/firmware/common/dsp_iir.cpp b/firmware/common/dsp_iir.cpp index 98e627ff2..b618a3966 100644 --- a/firmware/common/dsp_iir.cpp +++ b/firmware/common/dsp_iir.cpp @@ -24,60 +24,59 @@ #include void IIRBiquadFilter::configure(const iir_biquad_config_t& new_config) { - config = new_config; + config = new_config; } void IIRBiquadFilter::execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out) { - const auto a_ = config.a; - const auto b_ = config.b; - - auto x_ = x; - auto y_ = y; + const auto a_ = config.a; + const auto b_ = config.b; - // TODO: Assert that buffer_out.count == buffer_in.count. - for(size_t i=0; i b; - std::array a; + std::array b; + std::array a; }; // 0..2 - b, 3..5 - a typedef std::array iir_biquad_df2_config_t; -constexpr iir_biquad_config_t iir_config_passthrough { - { { 1.0f, 0.0f, 0.0f } }, - { { 0.0f, 0.0f, 0.0f } }, +constexpr iir_biquad_config_t iir_config_passthrough{ + {{1.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f}}, }; -constexpr iir_biquad_config_t iir_config_no_pass { - { { 0.0f, 0.0f, 0.0f } }, - { { 0.0f, 0.0f, 0.0f } }, +constexpr iir_biquad_config_t iir_config_no_pass{ + {{0.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f}}, }; class IIRBiquadFilter { -public: - // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - constexpr IIRBiquadFilter( - ) : IIRBiquadFilter(iir_config_no_pass) - { - } - - // Assume all coefficients are normalized so that a0=1.0 - constexpr IIRBiquadFilter( - const iir_biquad_config_t& config - ) : config(config) - { - } - - void configure(const iir_biquad_config_t& new_config); - - void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out); - void execute_in_place(const buffer_f32_t& buffer); - -private: - iir_biquad_config_t config; - std::array x { { 0.0f, 0.0f, 0.0f } }; - std::array y { { 0.0f, 0.0f, 0.0f } }; + public: + // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + constexpr IIRBiquadFilter() + : IIRBiquadFilter(iir_config_no_pass) { + } + + // Assume all coefficients are normalized so that a0=1.0 + constexpr IIRBiquadFilter( + const iir_biquad_config_t& config) + : config(config) { + } + + void configure(const iir_biquad_config_t& new_config); + + void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out); + void execute_in_place(const buffer_f32_t& buffer); + + private: + iir_biquad_config_t config; + std::array x{{0.0f, 0.0f, 0.0f}}; + std::array y{{0.0f, 0.0f, 0.0f}}; }; class IIRBiquadDF2Filter { -public: - - void configure(const iir_biquad_df2_config_t& config); - float execute(float z); - -private: - float b0 = 0; - float b1 = 0; - float b2 = 0; - float a1 = 0; - float a2 = 0; - - float z0 = 0; - float z1 = 0; + public: + void configure(const iir_biquad_df2_config_t& config); + float execute(float z); + + private: + float b0 = 0; + float b1 = 0; + float b2 = 0; + float a1 = 0; + float a2 = 0; + + float z0 = 0; + float z1 = 0; }; -#endif/*__DSP_IIR_H__*/ +#endif /*__DSP_IIR_H__*/ diff --git a/firmware/common/dsp_iir_config.hpp b/firmware/common/dsp_iir_config.hpp index 3ccaaa494..83666cce9 100644 --- a/firmware/common/dsp_iir_config.hpp +++ b/firmware/common/dsp_iir_config.hpp @@ -26,95 +26,82 @@ #include "dsp_iir.hpp" // scipy.signal.butter(2, 30 / 24000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_48k_hpf_30hz_config { - { 0.99722705f, -1.99445410f, 0.99722705f }, - { 1.00000000f, -1.99444641f, 0.99446179f } -}; +constexpr iir_biquad_config_t audio_48k_hpf_30hz_config{ + {0.99722705f, -1.99445410f, 0.99722705f}, + {1.00000000f, -1.99444641f, 0.99446179f}}; // scipy.signal.butter(2, 300 / 24000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_48k_hpf_300hz_config { - { 0.97261390f, -1.94522780f, 0.97261390f }, - { 1.00000000f, -1.94447766f, 0.94597794f } -}; +constexpr iir_biquad_config_t audio_48k_hpf_300hz_config{ + {0.97261390f, -1.94522780f, 0.97261390f}, + {1.00000000f, -1.94447766f, 0.94597794f}}; // scipy.signal.butter(2, 300 / 12000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_24k_hpf_300hz_config { - { 0.94597686f, -1.89195371f, 0.94597686f }, - { 1.00000000f, -1.88903308f, 0.89487434f } +constexpr iir_biquad_config_t audio_24k_hpf_300hz_config{ + {0.94597686f, -1.89195371f, 0.94597686f}, + {1.00000000f, -1.88903308f, 0.89487434f} }; // scipy.signal.butter(2, 30 / 12000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_24k_hpf_30hz_config { - { 0.99446179f, -1.98892358f, 0.99446179f }, - { 1.00000000f, -1.98889291f, 0.98895425f } -}; +constexpr iir_biquad_config_t audio_24k_hpf_30hz_config{ + {0.99446179f, -1.98892358f, 0.99446179f}, + {1.00000000f, -1.98889291f, 0.98895425f}}; // scipy.signal.butter(2, 300 / 8000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_16k_hpf_300hz_config { - { 0.92006616f, -1.84013232f, 0.92006616f }, - { 1.00000000f, -1.83373266f, 0.84653197f } -}; +constexpr iir_biquad_config_t audio_16k_hpf_300hz_config{ + {0.92006616f, -1.84013232f, 0.92006616f}, + {1.00000000f, -1.83373266f, 0.84653197f}}; // scipy.signal.butter(2, 300 / 6000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_12k_hpf_300hz_config { - { 0.89485861f, -1.78971721f, 0.89485861f }, - { 1.00000000f, -1.77863178f, 0.80080265f } -}; +constexpr iir_biquad_config_t audio_12k_hpf_300hz_config{ + {0.89485861f, -1.78971721f, 0.89485861f}, + {1.00000000f, -1.77863178f, 0.80080265f}}; // scipy.signal.butter(2, 300 / 4000.0, 'highpass', analog=False) -constexpr iir_biquad_config_t audio_8k_hpf_300hz_config { - { 0.84645925f, -1.69291851f, 0.84645925f }, - { 1.00000000f, -1.66920314f, 0.71663387f } -}; +constexpr iir_biquad_config_t audio_8k_hpf_300hz_config{ + {0.84645925f, -1.69291851f, 0.84645925f}, + {1.00000000f, -1.66920314f, 0.71663387f}}; // scipy.signal.iirdesign(wp=8000 / 24000.0, ws= 4000 / 24000.0, gpass=1, gstop=18, ftype='ellip') -constexpr iir_biquad_config_t non_audio_hpf_config { - { 0.51891061f, -0.95714180f, 0.51891061f }, - { 1.0f , -0.79878302f, 0.43960231f } -}; +constexpr iir_biquad_config_t non_audio_hpf_config{ + {0.51891061f, -0.95714180f, 0.51891061f}, + {1.0f, -0.79878302f, 0.43960231f}}; // scipy.signal.butter(1, 300 / 24000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_48k_deemph_300_6_config { - { 0.01925927f, 0.01925927f, 0.00000000f }, - { 1.00000000f, -0.96148145f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_48k_deemph_300_6_config{ + {0.01925927f, 0.01925927f, 0.00000000f}, + {1.00000000f, -0.96148145f, 0.00000000f}}; // scipy.signal.butter(1, 300 / 12000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_24k_deemph_300_6_config { - { 0.03780475f, 0.03780475f, 0.00000000f }, - { 1.00000000f, -0.92439049f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_24k_deemph_300_6_config{ + {0.03780475f, 0.03780475f, 0.00000000f}, + {1.00000000f, -0.92439049f, 0.00000000f}}; // scipy.signal.butter(1, 300 / 8000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_16k_deemph_300_6_config { - { 0.05568894f, 0.05568894f, 0.00000000f }, - { 1.00000000f, -0.88862213f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_16k_deemph_300_6_config{ + {0.05568894f, 0.05568894f, 0.00000000f}, + {1.00000000f, -0.88862213f, 0.00000000f}}; // scipy.signal.butter(1, 300 / 6000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_12k_deemph_300_6_config { - { 0.07295966f, 0.07295966f, 0.00000000f }, - { 1.00000000f, -0.85408069f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_12k_deemph_300_6_config{ + {0.07295966f, 0.07295966f, 0.00000000f}, + {1.00000000f, -0.85408069f, 0.00000000f}}; // scipy.signal.butter(1, 300 / 4000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_8k_deemph_300_6_config { - { 0.10583178f, 0.10583178f, 0.00000000f }, - { 1.00000000f, -0.78833643f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_8k_deemph_300_6_config{ + {0.10583178f, 0.10583178f, 0.00000000f}, + {1.00000000f, -0.78833643f, 0.00000000f}}; // 75us RC time constant, used in broadcast FM in Americas, South Korea // scipy.signal.butter(1, 2122 / 24000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_48k_deemph_2122_6_config { - { 0.12264116f, 0.12264116f, 0.00000000f }, - { 1.00000000f, -0.75471767f, 0.00000000f } -}; +constexpr iir_biquad_config_t audio_48k_deemph_2122_6_config{ + {0.12264116f, 0.12264116f, 0.00000000f}, + {1.00000000f, -0.75471767f, 0.00000000f}}; -#endif/*__DSP_IIR_CONFIG_H__*/ +#endif /*__DSP_IIR_CONFIG_H__*/ diff --git a/firmware/common/dsp_sos.hpp b/firmware/common/dsp_sos.hpp index b74466369..095a799a9 100644 --- a/firmware/common/dsp_sos.hpp +++ b/firmware/common/dsp_sos.hpp @@ -29,23 +29,20 @@ template class SOSFilter { - -public: - + public: void configure(const iir_biquad_df2_config_t config[N]) { for (size_t i = 0; i < N; i++) filters[i].configure(config[i]); } float execute(float value) { - for (auto &filter : filters) + for (auto& filter : filters) value = filter.execute(value); return value; } -private: - + private: IIRBiquadDF2Filter filters[N]; }; -#endif/*__DSP_SOS_H__*/ +#endif /*__DSP_SOS_H__*/ diff --git a/firmware/common/dsp_sos_config.hpp b/firmware/common/dsp_sos_config.hpp index 23c366223..6a0ecb00f 100644 --- a/firmware/common/dsp_sos_config.hpp +++ b/firmware/common/dsp_sos_config.hpp @@ -27,11 +27,10 @@ // scipy.signal.iirfilter(ftype="ellip", N = 10, rp = 0.5, rs = 60.0, Wn = 0.5, btype = 'lowpass', output="sos") constexpr iir_biquad_df2_config_t half_band_lpf_config[5] = { - { 0.02339042f, 0.0411599f, 0.02339042f, 1.0f, -0.95317621f, 0.33446485f }, - { 1.0f, 0.82196114f, 1.0f, 1.0f, -0.50327735f, 0.63611027f }, - { 1.0f, 0.32515305f, 1.0f, 1.0f, -0.18144446f, 0.85269598f }, - { 1.0f, 0.14394122f, 1.0f, 1.0f, -0.04368236f, 0.94798064f }, - { 1.0f, 0.08720754, 1.0f, 1.0f, 0.00220944f, 0.98743139f } -}; + {0.02339042f, 0.0411599f, 0.02339042f, 1.0f, -0.95317621f, 0.33446485f}, + {1.0f, 0.82196114f, 1.0f, 1.0f, -0.50327735f, 0.63611027f}, + {1.0f, 0.32515305f, 1.0f, 1.0f, -0.18144446f, 0.85269598f}, + {1.0f, 0.14394122f, 1.0f, 1.0f, -0.04368236f, 0.94798064f}, + {1.0f, 0.08720754, 1.0f, 1.0f, 0.00220944f, 0.98743139f}}; -#endif/*__DSP_SOS_CONFIG_H__*/ +#endif /*__DSP_SOS_CONFIG_H__*/ diff --git a/firmware/common/dsp_types.hpp b/firmware/common/dsp_types.hpp index b27e6b408..a4a5208a2 100644 --- a/firmware/common/dsp_types.hpp +++ b/firmware/common/dsp_types.hpp @@ -33,4 +33,4 @@ using buffer_s16_t = buffer_t; using buffer_c32_t = buffer_t; using buffer_f32_t = buffer_t; -#endif/*__DSP_TYPES_H__*/ +#endif /*__DSP_TYPES_H__*/ diff --git a/firmware/common/emu_cc1101.hpp b/firmware/common/emu_cc1101.hpp index 549535fe1..1245a4e8b 100644 --- a/firmware/common/emu_cc1101.hpp +++ b/firmware/common/emu_cc1101.hpp @@ -29,7 +29,7 @@ #include "utility.hpp" namespace cc1101 { - + // Data rate (Bauds) // Whitening: Everything except preamble and sync word, init value = 111111111 // Packet format: preamble, sync word, (opt) length, (opt) address, payload, (opt) CRC @@ -43,70 +43,69 @@ namespace cc1101 { // FEC: ? class CC1101Emu { -public: - //CC1101Emu(); - //~CC1101Emu(); - - enum packet_mode_t { - FIXED_LENGTH, - VARIABLE_LENGTH, - INFINITE_LENGTH - }; - - enum modulation_t { - TWO_FSK, - GFSK, - OOK, - FOUR_FSK, - MSK, - }; - - void set_sync_word(const uint16_t sync_word) { - sync_word_ = sync_word; - }; - void set_address(const uint8_t address) { - address_ = address; - }; - void set_packet_length(const uint8_t packet_length) { - packet_length_ = packet_length; - }; - void set_data_config(const bool CRC, const bool manchester, const bool whitening) { - CRC_ = CRC; - manchester_ = manchester; - whitening_ = whitening; - }; - void set_packet_mode(const packet_mode_t packet_mode) { - packet_mode_ = packet_mode; - }; - void set_modulation(const modulation_t modulation) { - modulation_ = modulation; - } - void set_num_preamble(const uint8_t num_preamble) { // 2, 3, 4, 6, 8, 12, 16, or 24 - num_preamble_ = num_preamble; - }; - void set_deviation(const size_t deviation) { - deviation_ = deviation; - }; + public: + // CC1101Emu(); + //~CC1101Emu(); + + enum packet_mode_t { + FIXED_LENGTH, + VARIABLE_LENGTH, + INFINITE_LENGTH + }; + + enum modulation_t { + TWO_FSK, + GFSK, + OOK, + FOUR_FSK, + MSK, + }; + + void set_sync_word(const uint16_t sync_word) { + sync_word_ = sync_word; + }; + void set_address(const uint8_t address) { + address_ = address; + }; + void set_packet_length(const uint8_t packet_length) { + packet_length_ = packet_length; + }; + void set_data_config(const bool CRC, const bool manchester, const bool whitening) { + CRC_ = CRC; + manchester_ = manchester; + whitening_ = whitening; + }; + void set_packet_mode(const packet_mode_t packet_mode) { + packet_mode_ = packet_mode; + }; + void set_modulation(const modulation_t modulation) { + modulation_ = modulation; + } + void set_num_preamble(const uint8_t num_preamble) { // 2, 3, 4, 6, 8, 12, 16, or 24 + num_preamble_ = num_preamble; + }; + void set_deviation(const size_t deviation) { + deviation_ = deviation; + }; + + private: + uint16_t sync_word_{0xD391}; + uint8_t address_{0x00}; + uint8_t packet_length_{0}; + bool CRC_{false}; + bool manchester_{false}; + bool whitening_{true}; + packet_mode_t packet_mode_{VARIABLE_LENGTH}; + modulation_t modulation_{TWO_FSK}; + uint8_t num_preamble_{4}; + size_t deviation_{4000}; + + uint16_t whitening_pn{0x1FF}; -private: - uint16_t sync_word_ { 0xD391 }; - uint8_t address_ { 0x00 }; - uint8_t packet_length_ { 0 }; - bool CRC_ { false }; - bool manchester_ { false }; - bool whitening_ { true }; - packet_mode_t packet_mode_ { VARIABLE_LENGTH }; - modulation_t modulation_ { TWO_FSK }; - uint8_t num_preamble_ { 4 }; - size_t deviation_ { 4000 }; - - uint16_t whitening_pn { 0x1FF }; - - void whitening_init(); - uint8_t whiten_byte(uint8_t byte); - + void whitening_init(); + uint8_t whiten_byte(uint8_t byte); }; } /* namespace cc1101 */ -#endif/*__EMU_CC1101_H__*/ +#endif /*__EMU_CC1101_H__*/ diff --git a/firmware/common/ert_packet.cpp b/firmware/common/ert_packet.cpp index 5286d751d..79a050221 100644 --- a/firmware/common/ert_packet.cpp +++ b/firmware/common/ert_packet.cpp @@ -26,91 +26,94 @@ namespace ert { size_t Packet::length() const { - return decoder_.symbols_count(); + return decoder_.symbols_count(); } bool Packet::is_valid() const { - return true; + return true; } Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } Packet::Type Packet::type() const { - return type_; + return type_; } ID Packet::id() const { - if( type() == Type::SCM ) { - const auto msb = reader_.read(0, 2); - const auto lsb = reader_.read(35, 24); - return (msb << 24) | lsb; - } - if( type() == Type::SCMPLUS ) { - return reader_.read(2 * 8, 32); - } - if( type() == Type::IDM ) { - return reader_.read(5 * 8, 32); - } - return invalid_id; + if (type() == Type::SCM) { + const auto msb = reader_.read(0, 2); + const auto lsb = reader_.read(35, 24); + return (msb << 24) | lsb; + } + if (type() == Type::SCMPLUS) { + return reader_.read(2 * 8, 32); + } + if (type() == Type::IDM) { + return reader_.read(5 * 8, 32); + } + return invalid_id; } Consumption Packet::consumption() const { - if( type() == Type::SCM ) { - return reader_.read(11, 24); - } - if( type() == Type::SCMPLUS ) { - return reader_.read(6 * 8, 32); - } - if( type() == Type::IDM ) { - return reader_.read(25 * 8, 32); - } - return invalid_consumption; + if (type() == Type::SCM) { + return reader_.read(11, 24); + } + if (type() == Type::SCMPLUS) { + return reader_.read(6 * 8, 32); + } + if (type() == Type::IDM) { + return reader_.read(25 * 8, 32); + } + return invalid_consumption; } CommodityType Packet::commodity_type() const { - if( type() == Type::SCM ) { - return reader_.read(5, 4); - } - if( type() == Type::SCMPLUS ) { - return reader_.read(1 * 8 + 4, 4); - } - if( type() == Type::IDM ) { - return reader_.read(4 * 8 + 4, 4); - } - return invalid_commodity_type; + if (type() == Type::SCM) { + return reader_.read(5, 4); + } + if (type() == Type::SCMPLUS) { + return reader_.read(1 * 8 + 4, 4); + } + if (type() == Type::IDM) { + return reader_.read(4 * 8 + 4, 4); + } + return invalid_commodity_type; } FormattedSymbols Packet::symbols_formatted() const { - return format_symbols(decoder_); + return format_symbols(decoder_); } bool Packet::crc_ok() const { - switch(type()) { - case Type::SCM: return crc_ok_scm(); - case Type::SCMPLUS: - case Type::IDM: return crc_ok_ccitt(); - default: return false; - } + switch (type()) { + case Type::SCM: + return crc_ok_scm(); + case Type::SCMPLUS: + case Type::IDM: + return crc_ok_ccitt(); + default: + return false; + } } bool Packet::crc_ok_scm() const { - CRC<16> ert_bch { 0x6f63 }; - size_t start_bit = 5; - ert_bch.process_byte(reader_.read(0, start_bit)); - for(size_t i=start_bit; i ert_bch{0x6f63}; + size_t start_bit = 5; + ert_bch.process_byte(reader_.read(0, start_bit)); + for (size_t i = start_bit; i < length(); i += 8) { + ert_bch.process_byte(reader_.read(i, 8)); + } + return ert_bch.checksum() == 0x0000; } bool Packet::crc_ok_ccitt() const { - CRC<16> ert_crc_ccitt { 0x1021, 0xffff, 0x1d0f }; - for(size_t i=0; i ert_crc_ccitt{0x1021, 0xffff, 0x1d0f}; + for (size_t i = 0; i < length(); i += 8) { + ert_crc_ccitt.process_byte(reader_.read(i, 8)); + } + return ert_crc_ccitt.checksum() == 0x0000; } } /* namespace ert */ diff --git a/firmware/common/ert_packet.hpp b/firmware/common/ert_packet.hpp index 90818421f..0542642d6 100644 --- a/firmware/common/ert_packet.hpp +++ b/firmware/common/ert_packet.hpp @@ -40,51 +40,50 @@ constexpr CommodityType invalid_commodity_type = -1; constexpr Consumption invalid_consumption = 0; class Packet { -public: - enum class Type : uint32_t { - Unknown = 0, - IDM = 1, - SCM = 2, - SCMPLUS = 3, - }; - - Packet( - const Type type, - const baseband::Packet& packet - ) : packet_ { packet }, - decoder_ { packet_ }, - reader_ { decoder_ }, - type_ { type } - { - } - - size_t length() const; - - bool is_valid() const; - - Timestamp received_at() const; - - Type type() const; - ID id() const; - CommodityType commodity_type() const; - Consumption consumption() const; - - FormattedSymbols symbols_formatted() const; - - bool crc_ok() const; - -private: - using Reader = FieldReader; - - const baseband::Packet packet_; - const ManchesterDecoder decoder_; - const Reader reader_; - const Type type_; - - bool crc_ok_ccitt() const; - bool crc_ok_scm() const; + public: + enum class Type : uint32_t { + Unknown = 0, + IDM = 1, + SCM = 2, + SCMPLUS = 3, + }; + + Packet( + const Type type, + const baseband::Packet& packet) + : packet_{packet}, + decoder_{packet_}, + reader_{decoder_}, + type_{type} { + } + + size_t length() const; + + bool is_valid() const; + + Timestamp received_at() const; + + Type type() const; + ID id() const; + CommodityType commodity_type() const; + Consumption consumption() const; + + FormattedSymbols symbols_formatted() const; + + bool crc_ok() const; + + private: + using Reader = FieldReader; + + const baseband::Packet packet_; + const ManchesterDecoder decoder_; + const Reader reader_; + const Type type_; + + bool crc_ok_ccitt() const; + bool crc_ok_scm() const; }; } /* namespace ert */ -#endif/*__ERT_PACKET_H__*/ +#endif /*__ERT_PACKET_H__*/ diff --git a/firmware/common/event.hpp b/firmware/common/event.hpp index a6432674c..c1b511dab 100644 --- a/firmware/common/event.hpp +++ b/firmware/common/event.hpp @@ -24,4 +24,4 @@ #include "ch.h" -#endif/*__EVENT_H__*/ +#endif /*__EVENT_H__*/ diff --git a/firmware/common/ffconf.h b/firmware/common/ffconf.h index 041cd7cd3..7737c2baa 100644 --- a/firmware/common/ffconf.h +++ b/firmware/common/ffconf.h @@ -5,20 +5,19 @@ / FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ -#define _FFCONF 68300 /* Revision ID */ +#define _FFCONF 68300 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations /---------------------------------------------------------------------------*/ -#define _FS_READONLY 0 +#define _FS_READONLY 0 /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) / Read-only configuration removes writing API functions, f_write(), f_sync(), / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() / and optional writing functions as well. */ - -#define _FS_MINIMIZE 0 +#define _FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. @@ -27,8 +26,7 @@ / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 3: f_lseek() function is removed in addition to 2. */ - -#define _USE_STRFUNC 1 +#define _USE_STRFUNC 1 /* This option switches string functions, f_gets(), f_putc(), f_puts() and / f_printf(). / @@ -36,43 +34,35 @@ / 1: Enable without LF-CRLF conversion. / 2: Enable with LF-CRLF conversion. */ - -#define _USE_FIND 1 +#define _USE_FIND 1 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ - -#define _USE_MKFS 0 +#define _USE_MKFS 0 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ - -#define _USE_FASTSEEK 1 +#define _USE_FASTSEEK 1 /* This option switches fast seek function. (0:Disable or 1:Enable) */ - -#define _USE_EXPAND 0 +#define _USE_EXPAND 0 /* This option switches f_expand function. (0:Disable or 1:Enable) */ - -#define _USE_CHMOD 0 +#define _USE_CHMOD 0 /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ - -#define _USE_LABEL 0 +#define _USE_LABEL 0 /* This option switches volume label functions, f_getlabel() and f_setlabel(). / (0:Disable or 1:Enable) */ - -#define _USE_FORWARD 0 +#define _USE_FORWARD 0 /* This option switches f_forward() function. (0:Disable or 1:Enable) */ - /*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ -#define _CODE_PAGE 437 +#define _CODE_PAGE 437 /* This option specifies the OEM code page to be used on the target system. / Incorrect setting of the code page can cause a file open failure. / @@ -100,9 +90,8 @@ / 950 - Traditional Chinese (DBCS) */ - -#define _USE_LFN 2 -#define _MAX_LFN 255 +#define _USE_LFN 2 +#define _MAX_LFN 255 /* The _USE_LFN switches the support of long file name (LFN). / / 0: Disable support of LFN. _MAX_LFN has no effect. @@ -118,14 +107,12 @@ / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree(), must be added to the project. */ - -#define _LFN_UNICODE 1 +#define _LFN_UNICODE 1 /* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) / To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. / This option also affects behavior of string I/O functions. */ - -#define _STRF_ENCODE 3 +#define _STRF_ENCODE 3 /* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / @@ -136,8 +123,7 @@ / / This option has no effect when _LFN_UNICODE == 0. */ - -#define _FS_RPATH 0 +#define _FS_RPATH 0 /* This option configures support of relative path. / / 0: Disable relative path and remove related functions. @@ -145,25 +131,22 @@ / 2: f_getcwd() function is available in addition to 1. */ - /*---------------------------------------------------------------------------/ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define _VOLUMES 1 +#define _VOLUMES 1 /* Number of volumes (logical drives) to be used. (1-10) */ - -#define _STR_VOLUME_ID 0 -#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +#define _STR_VOLUME_ID 0 +#define _VOLUME_STRS "RAM", "NAND", "CF", "SD", "SD2", "USB", "USB2", "USB3" /* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each / logical drives. Number of items must be equal to _VOLUMES. Valid characters for / the drive ID strings are: A-Z and 0-9. */ - -#define _MULTI_PARTITION 0 +#define _MULTI_PARTITION 0 /* This option switches support of multi-partition on a physical drive. / By default (0), each logical drive number is bound to the same physical drive / number and only an FAT volume found on the physical drive will be mounted. @@ -171,9 +154,8 @@ / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / funciton will be available. */ - -#define _MIN_SS 512 -#define _MAX_SS 512 +#define _MIN_SS 512 +#define _MAX_SS 512 /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, generic memory card and / harddisk. But a larger value may be required for on-board flash memory and some @@ -181,14 +163,12 @@ / to variable sector size and GET_SECTOR_SIZE command needs to be implemented to / the disk_ioctl() function. */ - -#define _USE_TRIM 0 +#define _USE_TRIM 0 /* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) / To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ - -#define _FS_NOFSINFO 0 +#define _FS_NOFSINFO 0 /* If you need to know correct free space on the FAT32 volume, set bit 0 of this / option, and f_getfree() function at first time after volume mount will force / a full FAT scan. Bit 1 controls the use of last allocated cluster number. @@ -199,29 +179,25 @@ / bit1=1: Do not trust last allocated cluster number in the FSINFO. */ - - /*---------------------------------------------------------------------------/ / System Configurations /---------------------------------------------------------------------------*/ -#define _FS_TINY 0 +#define _FS_TINY 0 /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) / At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ - -#define _FS_EXFAT 0 +#define _FS_EXFAT 0 /* This option switches support of exFAT file system. (0:Disable or 1:Enable) / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards ANSI C (C89) compatibility. */ - -#define _FS_NORTC 0 -#define _NORTC_MON 1 -#define _NORTC_MDAY 1 -#define _NORTC_YEAR 2016 +#define _FS_NORTC 0 +#define _NORTC_MON 1 +#define _NORTC_MDAY 1 +#define _NORTC_YEAR 2016 /* The option _FS_NORTC switches timestamp functiton. If the system does not have / any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable / the timestamp function. All objects modified by FatFs will have a fixed timestamp @@ -231,8 +207,7 @@ / _NORTC_MDAY and _NORTC_YEAR have no effect. / These options have no effect at read-only configuration (_FS_READONLY = 1). */ - -#define _FS_LOCK 0 +#define _FS_LOCK 0 /* The option _FS_LOCK switches file lock function to control duplicated file open / and illegal operation to open objects. This option must be 0 when _FS_READONLY / is 1. @@ -243,10 +218,9 @@ / can be opened simultaneously under file lock control. Note that the file / lock control is independent of re-entrancy. */ - -#define _FS_REENTRANT 1 -#define _FS_TIMEOUT 1000 -#define _SYNC_t Semaphore * +#define _FS_REENTRANT 1 +#define _FS_TIMEOUT 1000 +#define _SYNC_t Semaphore* /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() @@ -266,6 +240,4 @@ /* #include // O/S definitions */ - - /*--- End of configuration options ---*/ diff --git a/firmware/common/field_reader.hpp b/firmware/common/field_reader.hpp index b325546d6..6829fd996 100644 --- a/firmware/common/field_reader.hpp +++ b/firmware/common/field_reader.hpp @@ -26,40 +26,39 @@ #include struct BitRemapNone { - constexpr size_t operator()(const size_t& bit_index) const { - return bit_index; - } + constexpr size_t operator()(const size_t& bit_index) const { + return bit_index; + } }; struct BitRemapByteReverse { - constexpr size_t operator()(const size_t bit_index) const { - return bit_index ^ 7; - } + constexpr size_t operator()(const size_t bit_index) const { + return bit_index ^ 7; + } }; -template +template class FieldReader { -public: - constexpr FieldReader( - const T& data - ) : data { data } - { - } + public: + constexpr FieldReader( + const T& data) + : data{data} { + } - /* The "start_bit" winds up being the MSB of the returned field value. */ - /* The BitRemap functor determines which bits are read from the source - * packet. */ - int32_t read(const size_t start_bit, const size_t length) const { //Euquiq: was uint32_t, used for calculating lat / lon in radiosondes, can be negative too - uint32_t value = 0; - for(size_t i=start_bit; i<(start_bit + length); i++) { - value = (value << 1) | data[bit_remap(i)]; - } - return value; - } + /* The "start_bit" winds up being the MSB of the returned field value. */ + /* The BitRemap functor determines which bits are read from the source + * packet. */ + int32_t read(const size_t start_bit, const size_t length) const { // Euquiq: was uint32_t, used for calculating lat / lon in radiosondes, can be negative too + uint32_t value = 0; + for (size_t i = start_bit; i < (start_bit + length); i++) { + value = (value << 1) | data[bit_remap(i)]; + } + return value; + } -private: - const T& data; - const BitRemap bit_remap { }; + private: + const T& data; + const BitRemap bit_remap{}; }; -#endif/*__FIELD_READER_H__*/ +#endif /*__FIELD_READER_H__*/ diff --git a/firmware/common/fifo.hpp b/firmware/common/fifo.hpp index 0cf1f6d91..f8efc0178 100644 --- a/firmware/common/fifo.hpp +++ b/firmware/common/fifo.hpp @@ -31,209 +31,208 @@ /* FIFO implementation inspired by Linux kfifo. */ -template +template class FIFO { -public: - constexpr FIFO( - T* data, - size_t k - ) : _data { data }, - _size { 1U << k }, - _in { 0 }, - _out { 0 } - { - } - - void reset() { - _in = _out = 0; - } - - void reset_in() { - _in = _out; - } - - void reset_out() { - _out = _in; - } - - size_t len() const { - return _in - _out; - } - - size_t unused() const { - return size() - (_in - _out); - } - - bool is_empty() const { - return _in == _out; - } - - bool is_full() const { - return unused() == 0; - } - - bool in(const T& val) { - if( is_full() ) { - return false; - } - - _data[_in & mask()] = val; - smp_wmb(); - _in += 1; - - return true; - } - - size_t in(const T* const buf, size_t len) { - const size_t l = unused(); - if( len > l ) { - len = l; - } - - copy_in(buf, len, _in); - _in += len; - return len; - } - - size_t in_r(const void* const buf, const size_t len) { - if( (len + recsize()) > unused() ) { - return 0; - } - - poke_n(len); - copy_in((const T*)buf, len, _in + recsize()); - _in += len + recsize(); - return len; - } - - bool out(T& val) { - if( is_empty() ) { - return false; - } - - val = _data[_out & mask()]; // Crashes - smp_wmb(); // Ok - _out += 1; // Crashes - - return true; - } - - size_t out(T* const buf, size_t len) { - len = out_peek(buf, len); - _out += len; - return len; - } - - bool skip() { - if( is_empty() ) { - return false; - } - - size_t len = peek_n(); - _out += len + recsize(); - return true; - } - - size_t peek_r(void* const buf, size_t len) { - if( is_empty() ) { - return 0; - } - - size_t n; - len = out_copy_r((T*)buf, len, &n); - return len; - } - - size_t out_r(void* const buf, size_t len) { - if( is_empty() ) { - return 0; - } - - size_t n; - len = out_copy_r((T*)buf, len, &n); - _out += n + recsize(); - return len; - } - -private: - size_t size() const { - return _size; - } - - static constexpr size_t esize() { - return sizeof(T); - } - - size_t mask() const { - return size() - 1; - } - - static constexpr size_t recsize() { - return 2; - } - - void smp_wmb() { - __DMB(); - } - - size_t peek_n() { - size_t l = _data[_out & mask()]; - if( recsize() > 1 ) { - l |= _data[(_out + 1) & mask()] << 8; - } - return l; - } - - void poke_n(const size_t n) { - _data[_in & mask()] = n & 0xff; - if( recsize() > 1 ) { - _data[(_in + 1) & mask()] = (n >> 8) & 0xff; - } - } - - void copy_in(const T* const src, const size_t len, size_t off) { - off &= mask(); - const size_t l = std::min(len, size() - off); - - memcpy(&_data[off], &src[0], l * esize()); - memcpy(&_data[0], &src[l], (len - l) * esize()); - smp_wmb(); - } - - void copy_out(T* const dst, const size_t len, size_t off) { - off &= mask(); - const size_t l = std::min(len, size() - off); - - memcpy(&dst[0], &_data[off], l * esize()); - memcpy(&dst[l], &_data[0], (len - l) * esize()); - smp_wmb(); - } - - size_t out_copy_r(void *buf, size_t len, size_t* const n) { - *n = peek_n(); - - if( len > *n ) { - len = *n; - } - - copy_out((T*)buf, len, _out + recsize()); - return len; - } - - size_t out_peek(T* const buf, size_t buf_len) { - const size_t l = len(); - if( buf_len > l ) { - buf_len = l; - } - - copy_out(buf, buf_len, _out); - return buf_len; - } - - T* const _data; - const size_t _size; - volatile size_t _in; - volatile size_t _out; + public: + constexpr FIFO( + T* data, + size_t k) + : _data{data}, + _size{1U << k}, + _in{0}, + _out{0} { + } + + void reset() { + _in = _out = 0; + } + + void reset_in() { + _in = _out; + } + + void reset_out() { + _out = _in; + } + + size_t len() const { + return _in - _out; + } + + size_t unused() const { + return size() - (_in - _out); + } + + bool is_empty() const { + return _in == _out; + } + + bool is_full() const { + return unused() == 0; + } + + bool in(const T& val) { + if (is_full()) { + return false; + } + + _data[_in & mask()] = val; + smp_wmb(); + _in += 1; + + return true; + } + + size_t in(const T* const buf, size_t len) { + const size_t l = unused(); + if (len > l) { + len = l; + } + + copy_in(buf, len, _in); + _in += len; + return len; + } + + size_t in_r(const void* const buf, const size_t len) { + if ((len + recsize()) > unused()) { + return 0; + } + + poke_n(len); + copy_in((const T*)buf, len, _in + recsize()); + _in += len + recsize(); + return len; + } + + bool out(T& val) { + if (is_empty()) { + return false; + } + + val = _data[_out & mask()]; // Crashes + smp_wmb(); // Ok + _out += 1; // Crashes + + return true; + } + + size_t out(T* const buf, size_t len) { + len = out_peek(buf, len); + _out += len; + return len; + } + + bool skip() { + if (is_empty()) { + return false; + } + + size_t len = peek_n(); + _out += len + recsize(); + return true; + } + + size_t peek_r(void* const buf, size_t len) { + if (is_empty()) { + return 0; + } + + size_t n; + len = out_copy_r((T*)buf, len, &n); + return len; + } + + size_t out_r(void* const buf, size_t len) { + if (is_empty()) { + return 0; + } + + size_t n; + len = out_copy_r((T*)buf, len, &n); + _out += n + recsize(); + return len; + } + + private: + size_t size() const { + return _size; + } + + static constexpr size_t esize() { + return sizeof(T); + } + + size_t mask() const { + return size() - 1; + } + + static constexpr size_t recsize() { + return 2; + } + + void smp_wmb() { + __DMB(); + } + + size_t peek_n() { + size_t l = _data[_out & mask()]; + if (recsize() > 1) { + l |= _data[(_out + 1) & mask()] << 8; + } + return l; + } + + void poke_n(const size_t n) { + _data[_in & mask()] = n & 0xff; + if (recsize() > 1) { + _data[(_in + 1) & mask()] = (n >> 8) & 0xff; + } + } + + void copy_in(const T* const src, const size_t len, size_t off) { + off &= mask(); + const size_t l = std::min(len, size() - off); + + memcpy(&_data[off], &src[0], l * esize()); + memcpy(&_data[0], &src[l], (len - l) * esize()); + smp_wmb(); + } + + void copy_out(T* const dst, const size_t len, size_t off) { + off &= mask(); + const size_t l = std::min(len, size() - off); + + memcpy(&dst[0], &_data[off], l * esize()); + memcpy(&dst[l], &_data[0], (len - l) * esize()); + smp_wmb(); + } + + size_t out_copy_r(void* buf, size_t len, size_t* const n) { + *n = peek_n(); + + if (len > *n) { + len = *n; + } + + copy_out((T*)buf, len, _out + recsize()); + return len; + } + + size_t out_peek(T* const buf, size_t buf_len) { + const size_t l = len(); + if (buf_len > l) { + buf_len = l; + } + + copy_out(buf, buf_len, _out); + return buf_len; + } + + T* const _data; + const size_t _size; + volatile size_t _in; + volatile size_t _out; }; -#endif/*__FIFO_H__*/ +#endif /*__FIFO_H__*/ diff --git a/firmware/common/gcc.cpp b/firmware/common/gcc.cpp index 7c98842ba..a367eb92e 100644 --- a/firmware/common/gcc.cpp +++ b/firmware/common/gcc.cpp @@ -29,18 +29,18 @@ * undefined reference to `__dso_handle'. * Comes up when using random C++ features, e.g. lambdas or std::function? */ -void *__dso_handle; +void* __dso_handle; /* prevents the exception handling name demangling code getting pulled in */ namespace __gnu_cxx { - void __verbose_terminate_handler() { - } +void __verbose_terminate_handler() { } +} // namespace __gnu_cxx /* NOTE: Hack to address bloat when using C++ class virtual destructors. */ extern "C" __attribute__((weak)) void __cxa_pure_virtual(void) { - chSysHalt(); + chSysHalt(); } #endif @@ -49,6 +49,8 @@ extern "C" __attribute__((weak)) void __cxa_pure_virtual(void) { * _exit(). */ extern "C" void abort() { - /* while() loop to avoid noreturn-is-returning warning. */ - while(1) { chSysHalt(); } + /* while() loop to avoid noreturn-is-returning warning. */ + while (1) { + chSysHalt(); + } } diff --git a/firmware/common/gcc.hpp b/firmware/common/gcc.hpp index 065b76c89..82946364d 100644 --- a/firmware/common/gcc.hpp +++ b/firmware/common/gcc.hpp @@ -22,4 +22,4 @@ #ifndef __GCC_H__ #define __GCC_H__ -#endif/*__GCC_H__*/ +#endif /*__GCC_H__*/ diff --git a/firmware/common/gpdma.cpp b/firmware/common/gpdma.cpp index 034a62481..9b4ccc7c3 100644 --- a/firmware/common/gpdma.cpp +++ b/firmware/common/gpdma.cpp @@ -29,40 +29,38 @@ namespace gpdma { namespace { struct ChannelHandlers { - TCHandler tc; - ErrHandler err; - - constexpr ChannelHandlers( - ) : tc(nullptr), - err(nullptr) - { - } + TCHandler tc; + ErrHandler err; + + constexpr ChannelHandlers() + : tc(nullptr), + err(nullptr) { + } }; -} +} // namespace -static std::array handlers_table { {} }; +static std::array handlers_table{{}}; namespace channel { void Channel::set_handlers(const TCHandler tc_handler, const ErrHandler err_handler) const { - handlers_table[number].tc = tc_handler; - handlers_table[number].err = err_handler; + handlers_table[number].tc = tc_handler; + handlers_table[number].err = err_handler; } void Channel::configure( - const LLI& first_lli, - const uint32_t config -) const { - disable(); - clear_interrupts(); - - LPC_GPDMA_Channel_Type* const channel = &LPC_GPDMA->CH[number]; - channel->SRCADDR = first_lli.srcaddr; - channel->DESTADDR = first_lli.destaddr; - channel->LLI = first_lli.lli; - channel->CONTROL = first_lli.control; - channel->CONFIG = config; + const LLI& first_lli, + const uint32_t config) const { + disable(); + clear_interrupts(); + + LPC_GPDMA_Channel_Type* const channel = &LPC_GPDMA->CH[number]; + channel->SRCADDR = first_lli.srcaddr; + channel->DESTADDR = first_lli.destaddr; + channel->LLI = first_lli.lli; + channel->CONTROL = first_lli.control; + channel->CONFIG = config; } } /* namespace channel */ @@ -70,42 +68,42 @@ void Channel::configure( extern "C" { CH_IRQ_HANDLER(DMA_IRQHandler) { - CH_IRQ_PROLOGUE(); - - chSysLockFromIsr(); - - const auto tc_stat = LPC_GPDMA->INTTCSTAT; - /* TODO: Service the higher channel numbers first, they're higher priority - * right?!? - */ - for(size_t i=0; i> i) & 1 ) { - if( handlers_table[i].tc ) { - handlers_table[i].tc(); - } - } - } - LPC_GPDMA->INTTCCLR = tc_stat; - - /* Test for *any* error first, before looping, since errors should be - * exceptional and we should spend as little time on them in the common - * case. - */ - const auto err_stat = LPC_GPDMA->INTERRSTAT; - if( err_stat ) { - for(size_t i=0; i> i) & 1 ) { - if( handlers_table[i].err ) { - handlers_table[i].err(); - } - } - } - LPC_GPDMA->INTERRCLR = err_stat; - } - - chSysUnlockFromIsr(); - - CH_IRQ_EPILOGUE(); + CH_IRQ_PROLOGUE(); + + chSysLockFromIsr(); + + const auto tc_stat = LPC_GPDMA->INTTCSTAT; + /* TODO: Service the higher channel numbers first, they're higher priority + * right?!? + */ + for (size_t i = 0; i < handlers_table.size(); i++) { + if ((tc_stat >> i) & 1) { + if (handlers_table[i].tc) { + handlers_table[i].tc(); + } + } + } + LPC_GPDMA->INTTCCLR = tc_stat; + + /* Test for *any* error first, before looping, since errors should be + * exceptional and we should spend as little time on them in the common + * case. + */ + const auto err_stat = LPC_GPDMA->INTERRSTAT; + if (err_stat) { + for (size_t i = 0; i < handlers_table.size(); i++) { + if ((err_stat >> i) & 1) { + if (handlers_table[i].err) { + handlers_table[i].err(); + } + } + } + LPC_GPDMA->INTERRCLR = err_stat; + } + + chSysUnlockFromIsr(); + + CH_IRQ_EPILOGUE(); } } diff --git a/firmware/common/gpdma.hpp b/firmware/common/gpdma.hpp index cfdbd30d4..45bbbbc6a 100644 --- a/firmware/common/gpdma.hpp +++ b/firmware/common/gpdma.hpp @@ -39,177 +39,160 @@ namespace gpdma { */ constexpr size_t buffer_words(const size_t bytes, const size_t word_size) { - return (bytes + word_size - 1) / word_size; + return (bytes + word_size - 1) / word_size; } using TCHandler = void (*)(void); using ErrHandler = void (*)(void); enum class FlowControl { - MemoryToMemory_DMAControl = 0x0, - MemoryToPeripheral_DMAControl = 0x1, - PeripheralToMemory_DMAControl = 0x2, - SourcePeripheralToDestinationPeripheral_DMAControl = 0x3, - SourcePeripheralToDestinationPeripheral_DestinationControl = 0x4, - MemoryToPeripheral_PeripheralControl = 0x5, - PeripheralToMemory_PeripheralControl = 0x6, - SourcePeripheralToDestinationPeripheral_SourceControl = 0x7, + MemoryToMemory_DMAControl = 0x0, + MemoryToPeripheral_DMAControl = 0x1, + PeripheralToMemory_DMAControl = 0x2, + SourcePeripheralToDestinationPeripheral_DMAControl = 0x3, + SourcePeripheralToDestinationPeripheral_DestinationControl = 0x4, + MemoryToPeripheral_PeripheralControl = 0x5, + PeripheralToMemory_PeripheralControl = 0x6, + SourcePeripheralToDestinationPeripheral_SourceControl = 0x7, }; static const uint_fast8_t flow_control_peripheral_source_map = 0b11011100; constexpr uint_fast8_t source_endpoint_type(const FlowControl flow_control) { - return (flow_control_peripheral_source_map >> toUType(flow_control)) & 1; + return (flow_control_peripheral_source_map >> toUType(flow_control)) & 1; } static const uint_fast8_t flow_control_peripheral_destination_map = 0b11111010; constexpr uint_fast8_t destination_endpoint_type(const FlowControl flow_control) { - return (flow_control_peripheral_destination_map >> toUType(flow_control)) & 1; + return (flow_control_peripheral_destination_map >> toUType(flow_control)) & 1; } namespace mux { enum class Peripheral0 { - SPIFI = 0, - SCT_CTOUT_2 = 1, - SGPIO14 = 2, - TIMER3_MATCH_1 = 3, + SPIFI = 0, + SCT_CTOUT_2 = 1, + SGPIO14 = 2, + TIMER3_MATCH_1 = 3, }; enum class Peripheral1 { - TIMER0_MATCH_0 = 0, - USART0_TX = 1, + TIMER0_MATCH_0 = 0, + USART0_TX = 1, }; enum class Peripheral2 { - TIMER0_MATCH_1 = 0, - USART0_RX = 1, + TIMER0_MATCH_1 = 0, + USART0_RX = 1, }; enum class Peripheral3 { - TIMER1_MATCH_0 = 0, - UART1_TX = 1, - I2S1_DMAREQ_1 = 2, - SSP1_TX = 3, + TIMER1_MATCH_0 = 0, + UART1_TX = 1, + I2S1_DMAREQ_1 = 2, + SSP1_TX = 3, }; enum class Peripheral4 { - TIMER1_MATCH_1 = 0, - UART1_RX = 1, - I2S1_DMAREQ_2 = 2, - SSP1_RX = 3, + TIMER1_MATCH_1 = 0, + UART1_RX = 1, + I2S1_DMAREQ_2 = 2, + SSP1_RX = 3, }; enum class Peripheral5 { - TIMER2_MATCH_0 = 0, - USART2_TX = 1, - SSP1_TX = 2, - SGPIO15 = 3, + TIMER2_MATCH_0 = 0, + USART2_TX = 1, + SSP1_TX = 2, + SGPIO15 = 3, }; enum class Peripheral6 { - TIMER2_MATCH_1 = 0, - USART2_RX = 1, - SSP1_RX = 2, - SGPIO14 = 3, + TIMER2_MATCH_1 = 0, + USART2_RX = 1, + SSP1_RX = 2, + SGPIO14 = 3, }; enum class Peripheral7 { - TIMER3_MATCH_0 = 0, - USART3_TX = 1, - SCT_DMAREQ_0 = 2, - ADCHS_WRITE = 3, + TIMER3_MATCH_0 = 0, + USART3_TX = 1, + SCT_DMAREQ_0 = 2, + ADCHS_WRITE = 3, }; enum class Peripheral8 { - TIMER3_MATCH_1 = 0, - USART3_RX = 1, - SCT_DMAREQ_1 = 2, - ADCHS_READ = 3, + TIMER3_MATCH_1 = 0, + USART3_RX = 1, + SCT_DMAREQ_1 = 2, + ADCHS_READ = 3, }; enum class Peripheral9 { - SSP0_RX = 0, - I2S0_DMAREQ_1 = 1, - SCT_DMAREQ_1 = 2, + SSP0_RX = 0, + I2S0_DMAREQ_1 = 1, + SCT_DMAREQ_1 = 2, }; enum class Peripheral10 { - SSP0_TX = 0, - I2S0_DMAREQ_2 = 1, - SCT_DMAREQ_0 = 2, + SSP0_TX = 0, + I2S0_DMAREQ_2 = 1, + SCT_DMAREQ_0 = 2, }; enum class Peripheral11 { - SSP1_RX = 0, - SGPIO14 = 1, - USART0_TX = 2, + SSP1_RX = 0, + SGPIO14 = 1, + USART0_TX = 2, }; enum class Peripheral12 { - SSP1_TX = 0, - SGPIO15 = 1, - USART0_RX = 2, + SSP1_TX = 0, + SGPIO15 = 1, + USART0_RX = 2, }; enum class Peripheral13 { - ADC0 = 0, - SSP1_RX = 2, - USART3_RX = 3, + ADC0 = 0, + SSP1_RX = 2, + USART3_RX = 3, }; enum class Peripheral14 { - ADC1 = 0, - SSP1_TX = 2, - USART3_TX = 3, + ADC1 = 0, + SSP1_TX = 2, + USART3_TX = 3, }; enum class Peripheral15 { - DAC = 0, - SCT_CTOUT_3 = 1, - SGPIO15 = 2, - TIMER3_MATCH_0 = 3, + DAC = 0, + SCT_CTOUT_3 = 1, + SGPIO15 = 2, + TIMER3_MATCH_0 = 3, }; struct MUX { - Peripheral0 peripheral_0; - Peripheral1 peripheral_1; - Peripheral2 peripheral_2; - Peripheral3 peripheral_3; - Peripheral4 peripheral_4; - Peripheral5 peripheral_5; - Peripheral6 peripheral_6; - Peripheral7 peripheral_7; - Peripheral8 peripheral_8; - Peripheral9 peripheral_9; - Peripheral10 peripheral_10; - Peripheral11 peripheral_11; - Peripheral12 peripheral_12; - Peripheral13 peripheral_13; - Peripheral14 peripheral_14; - Peripheral15 peripheral_15; - - constexpr operator uint32_t() const { - return - (toUType(peripheral_0 ) << 0) - | (toUType(peripheral_1 ) << 2) - | (toUType(peripheral_2 ) << 4) - | (toUType(peripheral_3 ) << 6) - | (toUType(peripheral_4 ) << 8) - | (toUType(peripheral_5 ) << 10) - | (toUType(peripheral_6 ) << 12) - | (toUType(peripheral_7 ) << 14) - | (toUType(peripheral_8 ) << 16) - | (toUType(peripheral_9 ) << 18) - | (toUType(peripheral_10) << 20) - | (toUType(peripheral_11) << 22) - | (toUType(peripheral_12) << 24) - | (toUType(peripheral_13) << 26) - | (toUType(peripheral_14) << 28) - | (toUType(peripheral_15) << 30) - ; - } + Peripheral0 peripheral_0; + Peripheral1 peripheral_1; + Peripheral2 peripheral_2; + Peripheral3 peripheral_3; + Peripheral4 peripheral_4; + Peripheral5 peripheral_5; + Peripheral6 peripheral_6; + Peripheral7 peripheral_7; + Peripheral8 peripheral_8; + Peripheral9 peripheral_9; + Peripheral10 peripheral_10; + Peripheral11 peripheral_11; + Peripheral12 peripheral_12; + Peripheral13 peripheral_13; + Peripheral14 peripheral_14; + Peripheral15 peripheral_15; + + constexpr operator uint32_t() const { + return (toUType(peripheral_0) << 0) | (toUType(peripheral_1) << 2) | (toUType(peripheral_2) << 4) | (toUType(peripheral_3) << 6) | (toUType(peripheral_4) << 8) | (toUType(peripheral_5) << 10) | (toUType(peripheral_6) << 12) | (toUType(peripheral_7) << 14) | (toUType(peripheral_8) << 16) | (toUType(peripheral_9) << 18) | (toUType(peripheral_10) << 20) | (toUType(peripheral_11) << 22) | (toUType(peripheral_12) << 24) | (toUType(peripheral_13) << 26) | (toUType(peripheral_14) << 28) | (toUType(peripheral_15) << 30); + } }; } /* namespace mux */ @@ -217,154 +200,131 @@ struct MUX { namespace channel { struct LLI { - uint32_t srcaddr; - uint32_t destaddr; - uint32_t lli; - uint32_t control; + uint32_t srcaddr; + uint32_t destaddr; + uint32_t lli; + uint32_t control; }; struct LLIPointer { - uint32_t lm; - uint32_t r; - uint32_t lli; - - constexpr operator uint32_t() const { - return - ((lm & 1) << 0) - | ((r & 1) << 1) - | (lli & 0xfffffffc) - ; - } + uint32_t lm; + uint32_t r; + uint32_t lli; + + constexpr operator uint32_t() const { + return ((lm & 1) << 0) | ((r & 1) << 1) | (lli & 0xfffffffc); + } }; struct Control { - uint32_t transfersize; - uint32_t sbsize; - uint32_t dbsize; - uint32_t swidth; - uint32_t dwidth; - uint32_t s; - uint32_t d; - uint32_t si; - uint32_t di; - uint32_t prot1; - uint32_t prot2; - uint32_t prot3; - uint32_t i; - - constexpr operator uint32_t() const { - return - ((transfersize & 0xfff) << 0) - | ((sbsize & 7) << 12) - | ((dbsize & 7) << 15) - | ((swidth & 7) << 18) - | ((dwidth & 7) << 21) - | ((s & 1) << 24) - | ((d & 1) << 25) - | ((si & 1) << 26) - | ((di & 1) << 27) - | ((prot1 & 1) << 28) - | ((prot2 & 1) << 29) - | ((prot3 & 1) << 30) - | ((i & 1) << 31) - ; - } + uint32_t transfersize; + uint32_t sbsize; + uint32_t dbsize; + uint32_t swidth; + uint32_t dwidth; + uint32_t s; + uint32_t d; + uint32_t si; + uint32_t di; + uint32_t prot1; + uint32_t prot2; + uint32_t prot3; + uint32_t i; + + constexpr operator uint32_t() const { + return ((transfersize & 0xfff) << 0) | ((sbsize & 7) << 12) | ((dbsize & 7) << 15) | ((swidth & 7) << 18) | ((dwidth & 7) << 21) | ((s & 1) << 24) | ((d & 1) << 25) | ((si & 1) << 26) | ((di & 1) << 27) | ((prot1 & 1) << 28) | ((prot2 & 1) << 29) | ((prot3 & 1) << 30) | ((i & 1) << 31); + } }; struct Config { - uint32_t e; - uint32_t srcperipheral; - uint32_t destperipheral; - FlowControl flowcntrl; - uint32_t ie; - uint32_t itc; - uint32_t l; - uint32_t a; - uint32_t h; - - constexpr operator uint32_t() const { - return - ((e & 1) << 0) - | ((srcperipheral & 0x1f) << 1) - | ((destperipheral & 0x1f) << 6) - | ((toUType(flowcntrl) & 7) << 11) - | ((ie & 1) << 14) - | ((itc & 1) << 15) - | ((l & 1) << 16) - | ((a & 1) << 17) - | ((h & 1) << 18) - ; - } + uint32_t e; + uint32_t srcperipheral; + uint32_t destperipheral; + FlowControl flowcntrl; + uint32_t ie; + uint32_t itc; + uint32_t l; + uint32_t a; + uint32_t h; + + constexpr operator uint32_t() const { + return ((e & 1) << 0) | ((srcperipheral & 0x1f) << 1) | ((destperipheral & 0x1f) << 6) | ((toUType(flowcntrl) & 7) << 11) | ((ie & 1) << 14) | ((itc & 1) << 15) | ((l & 1) << 16) | ((a & 1) << 17) | ((h & 1) << 18); + } }; class Channel { -public: - constexpr Channel( - const size_t number - ) : number(number) - { - } + public: + constexpr Channel( + const size_t number) + : number(number) { + } - void enable() const { - LPC_GPDMA->CH[number].CONFIG |= (1U << 0); - } + void enable() const { + LPC_GPDMA->CH[number].CONFIG |= (1U << 0); + } - bool is_enabled() const { - return LPC_GPDMA->CH[number].CONFIG & (1U << 0); - } + bool is_enabled() const { + return LPC_GPDMA->CH[number].CONFIG & (1U << 0); + } - void disable() const { - LPC_GPDMA->CH[number].CONFIG &= ~(1U << 0); - } + void disable() const { + LPC_GPDMA->CH[number].CONFIG &= ~(1U << 0); + } - void clear_interrupts() const { - LPC_GPDMA->INTTCCLR = (1U << number); - LPC_GPDMA->INTERRCLR = (1U << number); - } + void clear_interrupts() const { + LPC_GPDMA->INTTCCLR = (1U << number); + LPC_GPDMA->INTERRCLR = (1U << number); + } - void set_handlers(const TCHandler tc_handler, const ErrHandler err_handler) const; + void set_handlers(const TCHandler tc_handler, const ErrHandler err_handler) const; - void configure(const LLI& first_lli, const uint32_t config) const; + void configure(const LLI& first_lli, const uint32_t config) const; - const LLI* next_lli() const { - return reinterpret_cast(LPC_GPDMA->CH[number].LLI); - } + const LLI* next_lli() const { + return reinterpret_cast(LPC_GPDMA->CH[number].LLI); + } -private: - const size_t number; + private: + const size_t number; }; } /* namespace channel */ -constexpr std::array channels { { - { 0 }, { 1 }, { 2 }, { 3 }, - { 4 }, { 5 }, { 6 }, { 7 }, -} }; +constexpr std::array channels{{ + {0}, + {1}, + {2}, + {3}, + {4}, + {5}, + {6}, + {7}, +}}; static const gpdma_resources_t gpdma_resources = { - .base = { .clk = &LPC_CGU->BASE_M4_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 3) }, - .branch = { .cfg = &LPC_CCU1->CLK_M4_DMA_CFG, .stat = &LPC_CCU1->CLK_M4_DMA_STAT }, - .reset = { .output_index = 19 }, + .base = {.clk = &LPC_CGU->BASE_M4_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 3)}, + .branch = {.cfg = &LPC_CCU1->CLK_M4_DMA_CFG, .stat = &LPC_CCU1->CLK_M4_DMA_STAT}, + .reset = {.output_index = 19}, }; class Controller { -public: - void enable() const { - base_clock_enable(&gpdma_resources.base); - branch_clock_enable(&gpdma_resources.branch); - peripheral_reset(&gpdma_resources.reset); - LPC_GPDMA->CONFIG |= (1U << 0); - } - - void disable() const { - for(const auto& channel : channels) { - channel.disable(); - } - LPC_GPDMA->CONFIG &= ~(1U << 0); - peripheral_reset(&gpdma_resources.reset); - branch_clock_disable(&gpdma_resources.branch); - base_clock_disable(&gpdma_resources.base); - } + public: + void enable() const { + base_clock_enable(&gpdma_resources.base); + branch_clock_enable(&gpdma_resources.branch); + peripheral_reset(&gpdma_resources.reset); + LPC_GPDMA->CONFIG |= (1U << 0); + } + + void disable() const { + for (const auto& channel : channels) { + channel.disable(); + } + LPC_GPDMA->CONFIG &= ~(1U << 0); + peripheral_reset(&gpdma_resources.reset); + branch_clock_disable(&gpdma_resources.branch); + base_clock_disable(&gpdma_resources.base); + } }; constexpr Controller controller; @@ -372,4 +332,4 @@ constexpr Controller controller; } /* namespace gpdma */ } /* namespace lpc43xx */ -#endif/*__GPDMA_H__*/ +#endif /*__GPDMA_H__*/ diff --git a/firmware/common/gpio.hpp b/firmware/common/gpio.hpp index c28cda463..7ff3bdaf1 100644 --- a/firmware/common/gpio.hpp +++ b/firmware/common/gpio.hpp @@ -28,276 +28,256 @@ #include "hal.h" struct PinConfig { - const uint32_t mode; - const uint32_t pd; - const uint32_t pu; - const uint32_t fast; - const uint32_t input; - const uint32_t ifilt; - - constexpr operator uint16_t() const { - return - (((~ifilt) & 1) << 7) - | ((input & 1) << 6) - | ((fast & 1) << 5) - | (((~pu) & 1) << 4) - | ((pd & 1) << 3) - | ((mode & 7) << 0); - } -/* - constexpr operator uint32_t() { - return scu::sfs::mode::value(mode) - << scu::sfs::epd::value(pd) - << scu::sfs::epun::value(~pu) - << scu::sfs::ehs::value(fast) - << scu::sfs::ezi::value(input) - << scu::sfs::zif::value(~ifilt) - ; - } + const uint32_t mode; + const uint32_t pd; + const uint32_t pu; + const uint32_t fast; + const uint32_t input; + const uint32_t ifilt; + + constexpr operator uint16_t() const { + return (((~ifilt) & 1) << 7) | ((input & 1) << 6) | ((fast & 1) << 5) | (((~pu) & 1) << 4) | ((pd & 1) << 3) | ((mode & 7) << 0); + } + /* + constexpr operator uint32_t() { + return scu::sfs::mode::value(mode) + << scu::sfs::epd::value(pd) + << scu::sfs::epun::value(~pu) + << scu::sfs::ehs::value(fast) + << scu::sfs::ezi::value(input) + << scu::sfs::zif::value(~ifilt) + ; + } */ - static constexpr PinConfig reset() { - return { .mode = 0, .pd = 0, .pu = 1, .fast = 0, .input = 0, .ifilt = 1 }; - } - - static constexpr PinConfig floating( - const uint32_t mode - ) { - return { - .mode = mode, - .pd = 0, - .pu = 0, - .fast = 0, - .input = 0, - .ifilt = 1 - }; - } - - static constexpr PinConfig floating_input( - const uint32_t mode - ) { - return { - .mode = mode, - .pd = 0, - .pu = 0, - .fast = 0, - .input = 1, - .ifilt = 1 - }; - } - - static constexpr PinConfig floating_input_with_pull( - const uint32_t pull_direction, - const uint32_t mode - ) { - return { - .mode = mode, - .pd = (pull_direction == 0) ? 1U : 0U, - .pu = (pull_direction == 1) ? 1U : 0U, - .fast = 0, - .input = 1, - .ifilt = 1 - }; - } - - static constexpr PinConfig gpio_led(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 0, .ifilt = 1 }; - } - - static constexpr PinConfig gpio_inout_with_pull( - const uint32_t mode, - const uint32_t pull_direction - ) { - return { - .mode = mode, - .pd = (pull_direction == 0) ? 1U : 0U, - .pu = (pull_direction == 1) ? 1U : 0U, - .fast = 0, - .input = 1, - .ifilt = 1 - }; - } - - static constexpr PinConfig gpio_inout_with_pullup(const uint32_t mode) { - return gpio_inout_with_pull(mode, 1); - } - - static constexpr PinConfig gpio_inout_with_pulldown(const uint32_t mode) { - return gpio_inout_with_pull(mode, 0); - } - - static constexpr PinConfig gpio_out_with_pull( - const uint32_t mode, - const uint32_t pull_direction - ) { - return { - .mode = mode, - .pd = (pull_direction == 0) ? 1U : 0U, - .pu = (pull_direction == 1) ? 1U : 0U, - .fast = 0, - .input = 0, - .ifilt = 1 - }; - } - - static constexpr PinConfig gpio_out_with_pulldown(const uint32_t mode) { - return gpio_out_with_pull(mode, 0); - } - - static constexpr PinConfig gpio_out_with_pullup(const uint32_t mode) { - return gpio_out_with_pull(mode, 1); - } - - static constexpr PinConfig sgpio_in_fast(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 1, .ifilt = 0 }; - } - - static constexpr PinConfig sgpio_out_fast_with_pull( - const uint32_t mode, - const uint32_t pull_direction - ) { - return { - .mode = mode, - .pd = (pull_direction == 0) ? 1U : 0U, - .pu = (pull_direction == 1) ? 1U : 0U, - .fast = 1, - .input = 0, - .ifilt = 1 - }; - } - - static constexpr PinConfig sgpio_out_fast_with_pullup(const uint32_t mode) { - return sgpio_out_fast_with_pull(mode, 1); - } - - static constexpr PinConfig sgpio_inout_fast(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0 }; - } - - static constexpr PinConfig i2c(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 1, .ifilt = 1 }; - } - - static constexpr PinConfig spifi_sck(const uint32_t mode ) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0 }; - } - - static constexpr PinConfig spifi_inout(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0 }; - } - - static constexpr PinConfig spifi_cs(const uint32_t mode) { - return { .mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0 }; - } + static constexpr PinConfig reset() { + return {.mode = 0, .pd = 0, .pu = 1, .fast = 0, .input = 0, .ifilt = 1}; + } + + static constexpr PinConfig floating( + const uint32_t mode) { + return { + .mode = mode, + .pd = 0, + .pu = 0, + .fast = 0, + .input = 0, + .ifilt = 1}; + } + + static constexpr PinConfig floating_input( + const uint32_t mode) { + return { + .mode = mode, + .pd = 0, + .pu = 0, + .fast = 0, + .input = 1, + .ifilt = 1}; + } + + static constexpr PinConfig floating_input_with_pull( + const uint32_t pull_direction, + const uint32_t mode) { + return { + .mode = mode, + .pd = (pull_direction == 0) ? 1U : 0U, + .pu = (pull_direction == 1) ? 1U : 0U, + .fast = 0, + .input = 1, + .ifilt = 1}; + } + + static constexpr PinConfig gpio_led(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 0, .ifilt = 1}; + } + + static constexpr PinConfig gpio_inout_with_pull( + const uint32_t mode, + const uint32_t pull_direction) { + return { + .mode = mode, + .pd = (pull_direction == 0) ? 1U : 0U, + .pu = (pull_direction == 1) ? 1U : 0U, + .fast = 0, + .input = 1, + .ifilt = 1}; + } + + static constexpr PinConfig gpio_inout_with_pullup(const uint32_t mode) { + return gpio_inout_with_pull(mode, 1); + } + + static constexpr PinConfig gpio_inout_with_pulldown(const uint32_t mode) { + return gpio_inout_with_pull(mode, 0); + } + + static constexpr PinConfig gpio_out_with_pull( + const uint32_t mode, + const uint32_t pull_direction) { + return { + .mode = mode, + .pd = (pull_direction == 0) ? 1U : 0U, + .pu = (pull_direction == 1) ? 1U : 0U, + .fast = 0, + .input = 0, + .ifilt = 1}; + } + + static constexpr PinConfig gpio_out_with_pulldown(const uint32_t mode) { + return gpio_out_with_pull(mode, 0); + } + + static constexpr PinConfig gpio_out_with_pullup(const uint32_t mode) { + return gpio_out_with_pull(mode, 1); + } + + static constexpr PinConfig sgpio_in_fast(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 1, .ifilt = 0}; + } + + static constexpr PinConfig sgpio_out_fast_with_pull( + const uint32_t mode, + const uint32_t pull_direction) { + return { + .mode = mode, + .pd = (pull_direction == 0) ? 1U : 0U, + .pu = (pull_direction == 1) ? 1U : 0U, + .fast = 1, + .input = 0, + .ifilt = 1}; + } + + static constexpr PinConfig sgpio_out_fast_with_pullup(const uint32_t mode) { + return sgpio_out_fast_with_pull(mode, 1); + } + + static constexpr PinConfig sgpio_inout_fast(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0}; + } + + static constexpr PinConfig i2c(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 0, .input = 1, .ifilt = 1}; + } + + static constexpr PinConfig spifi_sck(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0}; + } + + static constexpr PinConfig spifi_inout(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0}; + } + + static constexpr PinConfig spifi_cs(const uint32_t mode) { + return {.mode = mode, .pd = 0, .pu = 0, .fast = 1, .input = 1, .ifilt = 0}; + } }; struct Pin { - // Pin() = delete; - // Pin(const Pin&) = delete; - // Pin(Pin&&) = delete; - - constexpr Pin( - const uint8_t port, - const uint8_t pad - ) : _pin_port { port }, - _pin_pad { pad } - { - } - - void mode(const uint_fast16_t mode) const { - LPC_SCU->SFSP[_pin_port][_pin_pad] = - (LPC_SCU->SFSP[_pin_port][_pin_pad] & 0xfffffff8) | mode; - } - - void configure(const PinConfig config) const { - LPC_SCU->SFSP[_pin_port][_pin_pad] = config; - } - - uint8_t _pin_port; - uint8_t _pin_pad; + // Pin() = delete; + // Pin(const Pin&) = delete; + // Pin(Pin&&) = delete; + + constexpr Pin( + const uint8_t port, + const uint8_t pad) + : _pin_port{port}, + _pin_pad{pad} { + } + + void mode(const uint_fast16_t mode) const { + LPC_SCU->SFSP[_pin_port][_pin_pad] = + (LPC_SCU->SFSP[_pin_port][_pin_pad] & 0xfffffff8) | mode; + } + + void configure(const PinConfig config) const { + LPC_SCU->SFSP[_pin_port][_pin_pad] = config; + } + + uint8_t _pin_port; + uint8_t _pin_pad; }; struct GPIO { - // GPIO() = delete; - // GPIO(const GPIO& gpio) = delete; - // GPIO(GPIO&&) = delete; - - constexpr GPIO( - const Pin& pin, - const ioportid_t gpio_port, - const iopadid_t gpio_pad, - const uint16_t gpio_mode - ) : _pin { pin }, - _gpio_port { gpio_port }, - _gpio_pad { gpio_pad }, - _gpio_mode { gpio_mode } - { - } -/* - constexpr GPIO( - const GPIO& gpio - ) : _pin { gpio._pin }, - _gpio_port { gpio._gpio_port }, - _gpio_pad { gpio._gpio_pad }, - _gpio_mode { gpio._gpio_mode } - { - } + // GPIO() = delete; + // GPIO(const GPIO& gpio) = delete; + // GPIO(GPIO&&) = delete; + + constexpr GPIO( + const Pin& pin, + const ioportid_t gpio_port, + const iopadid_t gpio_pad, + const uint16_t gpio_mode) + : _pin{pin}, + _gpio_port{gpio_port}, + _gpio_pad{gpio_pad}, + _gpio_mode{gpio_mode} { + } + /* + constexpr GPIO( + const GPIO& gpio + ) : _pin { gpio._pin }, + _gpio_port { gpio._gpio_port }, + _gpio_pad { gpio._gpio_pad }, + _gpio_mode { gpio._gpio_mode } + { + } */ - constexpr ioportid_t port() const { - return _gpio_port; - } - - constexpr iopadid_t pad() const { - return _gpio_pad; - } - - constexpr Pin pin() const { - return _pin; - } - - void configure() const { - _pin.mode(_gpio_mode); - } - - uint_fast16_t mode() const { - return _gpio_mode; - } - - void set() const { - palSetPad(_gpio_port, _gpio_pad); - } - - void clear() const { - palClearPad(_gpio_port, _gpio_pad); - } - - void toggle() const { - palTogglePad(_gpio_port, _gpio_pad); - } - - void output() const { - palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_OUTPUT_PUSHPULL); - } - - void input() const { - palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_INPUT); - } - - void write(const bool value) const { - palWritePad(_gpio_port, _gpio_pad, value); - } - - bool read() const { - return palReadPad(_gpio_port, _gpio_pad); - } - - bool operator!=(const GPIO& other) const { - return (port() != other.port()) || (pad() != other.pad()); - } - - const Pin _pin; - const ioportid_t _gpio_port; - const iopadid_t _gpio_pad; - const uint16_t _gpio_mode; + constexpr ioportid_t port() const { + return _gpio_port; + } + + constexpr iopadid_t pad() const { + return _gpio_pad; + } + + constexpr Pin pin() const { + return _pin; + } + + void configure() const { + _pin.mode(_gpio_mode); + } + + uint_fast16_t mode() const { + return _gpio_mode; + } + + void set() const { + palSetPad(_gpio_port, _gpio_pad); + } + + void clear() const { + palClearPad(_gpio_port, _gpio_pad); + } + + void toggle() const { + palTogglePad(_gpio_port, _gpio_pad); + } + + void output() const { + palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_OUTPUT_PUSHPULL); + } + + void input() const { + palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_INPUT); + } + + void write(const bool value) const { + palWritePad(_gpio_port, _gpio_pad, value); + } + + bool read() const { + return palReadPad(_gpio_port, _gpio_pad); + } + + bool operator!=(const GPIO& other) const { + return (port() != other.port()) || (pad() != other.pad()); + } + + const Pin _pin; + const ioportid_t _gpio_port; + const iopadid_t _gpio_pad; + const uint16_t _gpio_mode; }; -#endif/*__GPIO_H__*/ +#endif /*__GPIO_H__*/ diff --git a/firmware/common/hackrf_cpld_data.hpp b/firmware/common/hackrf_cpld_data.hpp index 888d41eac..37bf8487d 100644 --- a/firmware/common/hackrf_cpld_data.hpp +++ b/firmware/common/hackrf_cpld_data.hpp @@ -32,8 +32,8 @@ using CPLD = ::cpld::xilinx::XC2C64A; extern const CPLD::verify_blocks_t verify_blocks; -} /* namespace hackrf */ +} // namespace cpld } /* namespace one */ -} /* namespace cpld */ +} // namespace hackrf -#endif/*__HACKRF_CPLD_DATA_H__*/ +#endif /*__HACKRF_CPLD_DATA_H__*/ diff --git a/firmware/common/hackrf_gpio.hpp b/firmware/common/hackrf_gpio.hpp index 8e3fffa25..acbb820c3 100644 --- a/firmware/common/hackrf_gpio.hpp +++ b/firmware/common/hackrf_gpio.hpp @@ -89,11 +89,11 @@ constexpr GPIO gpio_r9_not_ant_pwr = gpio[GPIO2_4]; /* LEDs */ -constexpr LED led_usb { gpio_led_usb }; -constexpr LED led_rx { gpio_led_rx }; -constexpr LED led_tx { gpio_led_tx }; +constexpr LED led_usb{gpio_led_usb}; +constexpr LED led_rx{gpio_led_rx}; +constexpr LED led_tx{gpio_led_tx}; } /* namespace one */ } /* namespace hackrf */ -#endif/*__HACKRF_GPIO_H__*/ +#endif /*__HACKRF_GPIO_H__*/ diff --git a/firmware/common/hackrf_hal.hpp b/firmware/common/hackrf_hal.hpp index 60e518ff8..9e5e2695b 100644 --- a/firmware/common/hackrf_hal.hpp +++ b/firmware/common/hackrf_hal.hpp @@ -35,40 +35,40 @@ namespace one { using ClockFrequency = uint32_t; -constexpr ClockFrequency si5351_xtal_f = 25000000U; -constexpr ClockFrequency si5351_clkin_f = 10000000U; +constexpr ClockFrequency si5351_xtal_f = 25000000U; +constexpr ClockFrequency si5351_clkin_f = 10000000U; /* TODO: Use this many other places. */ /* TODO: M4/M0 and peripheral rates may be more PortaPack-specific? Move out * of HackRF header? */ -constexpr ClockFrequency base_m4_clk_f = 200000000U; -constexpr ClockFrequency base_m0_clk_f = base_m4_clk_f; -constexpr ClockFrequency base_apb3_clk_f = base_m4_clk_f; -constexpr ClockFrequency ssp1_pclk_f = base_m4_clk_f; +constexpr ClockFrequency base_m4_clk_f = 200000000U; +constexpr ClockFrequency base_m0_clk_f = base_m4_clk_f; +constexpr ClockFrequency base_apb3_clk_f = base_m4_clk_f; +constexpr ClockFrequency ssp1_pclk_f = base_m4_clk_f; -constexpr ClockFrequency max5864_spi_f = 20000000U; -constexpr ClockFrequency max283x_spi_f = 20000000U; +constexpr ClockFrequency max5864_spi_f = 20000000U; +constexpr ClockFrequency max283x_spi_f = 20000000U; -constexpr ClockFrequency rffc5072_reference_f = 40000000U; -constexpr ClockFrequency max283x_reference_f = 40000000U; -constexpr ClockFrequency mcu_clkin_og_f = 40000000U; -constexpr ClockFrequency mcu_clkin_r9_f = 10000000U; +constexpr ClockFrequency rffc5072_reference_f = 40000000U; +constexpr ClockFrequency max283x_reference_f = 40000000U; +constexpr ClockFrequency mcu_clkin_og_f = 40000000U; +constexpr ClockFrequency mcu_clkin_r9_f = 10000000U; constexpr uint8_t si5351_i2c_address = 0x60; /* Clock Generator */ -constexpr size_t clock_generator_output_og_codec = 0; -constexpr size_t clock_generator_output_og_cpld = 1; -constexpr size_t clock_generator_output_og_sgpio = 2; -constexpr size_t clock_generator_output_og_clkout = 3; -constexpr size_t clock_generator_output_og_first_if = 4; -constexpr size_t clock_generator_output_og_second_if = 5; -constexpr size_t clock_generator_output_og_mcu_clkin = 7; +constexpr size_t clock_generator_output_og_codec = 0; +constexpr size_t clock_generator_output_og_cpld = 1; +constexpr size_t clock_generator_output_og_sgpio = 2; +constexpr size_t clock_generator_output_og_clkout = 3; +constexpr size_t clock_generator_output_og_first_if = 4; +constexpr size_t clock_generator_output_og_second_if = 5; +constexpr size_t clock_generator_output_og_mcu_clkin = 7; -constexpr size_t clock_generator_output_r9_if = 0; -constexpr size_t clock_generator_output_r9_sgpio = 1; -constexpr size_t clock_generator_output_r9_mcu_clkin = 2; +constexpr size_t clock_generator_output_r9_if = 0; +constexpr size_t clock_generator_output_r9_sgpio = 1; +constexpr size_t clock_generator_output_r9_mcu_clkin = 2; /* ADC0 */ @@ -81,4 +81,4 @@ using adc1 = adc::ADC; } /* namespace one */ } /* namespace hackrf */ -#endif/*__HACKRF_HAL_H__*/ +#endif /*__HACKRF_HAL_H__*/ diff --git a/firmware/common/i2c_pp.cpp b/firmware/common/i2c_pp.cpp index 6cd7a6f80..0e37f1ec5 100644 --- a/firmware/common/i2c_pp.cpp +++ b/firmware/common/i2c_pp.cpp @@ -22,44 +22,43 @@ #include "i2c_pp.hpp" void I2C::start(const I2CConfig& config) { - i2cStart(_driver, &config); + i2cStart(_driver, &config); } void I2C::stop() { - i2cStop(_driver); + i2cStop(_driver); } bool I2C::transfer( - const address_t slave_address, - const uint8_t* const data_tx, const size_t count_tx, - uint8_t* const data_rx, const size_t count_rx, - systime_t timeout -) { - i2cAcquireBus(_driver); - const msg_t status = i2cMasterTransmitTimeout( - _driver, slave_address, data_tx, count_tx, data_rx, count_rx, timeout - ); - i2cReleaseBus(_driver); - return (status == RDY_OK); + const address_t slave_address, + const uint8_t* const data_tx, + const size_t count_tx, + uint8_t* const data_rx, + const size_t count_rx, + systime_t timeout) { + i2cAcquireBus(_driver); + const msg_t status = i2cMasterTransmitTimeout( + _driver, slave_address, data_tx, count_tx, data_rx, count_rx, timeout); + i2cReleaseBus(_driver); + return (status == RDY_OK); } bool I2C::receive( - const address_t slave_address, - uint8_t* const data, const size_t count, - systime_t timeout -) { - i2cAcquireBus(_driver); - const msg_t status = i2cMasterReceiveTimeout( - _driver, slave_address, data, count, timeout - ); - i2cReleaseBus(_driver); - return (status == RDY_OK); + const address_t slave_address, + uint8_t* const data, + const size_t count, + systime_t timeout) { + i2cAcquireBus(_driver); + const msg_t status = i2cMasterReceiveTimeout( + _driver, slave_address, data, count, timeout); + i2cReleaseBus(_driver); + return (status == RDY_OK); } bool I2C::transmit( - const address_t slave_address, - const uint8_t* const data, const size_t count, - systime_t timeout -) { - return transfer(slave_address, data, count, NULL, 0, timeout); + const address_t slave_address, + const uint8_t* const data, + const size_t count, + systime_t timeout) { + return transfer(slave_address, data, count, NULL, 0, timeout); } diff --git a/firmware/common/i2c_pp.hpp b/firmware/common/i2c_pp.hpp index ddf1c1105..4fd23ac97 100644 --- a/firmware/common/i2c_pp.hpp +++ b/firmware/common/i2c_pp.hpp @@ -28,59 +28,60 @@ #include "hal.h" struct I2CClockConfig { - float clock_source_f; - float bus_f; - float high_period_ns; + float clock_source_f; + float bus_f; + float high_period_ns; - static constexpr float period_ns(const float f) { - return 1e9 / f; - } + static constexpr float period_ns(const float f) { + return 1e9 / f; + } - constexpr uint32_t i2c_period_count() const { - return period_ns(bus_f) / period_ns(clock_source_f) + 0.5f; - } + constexpr uint32_t i2c_period_count() const { + return period_ns(bus_f) / period_ns(clock_source_f) + 0.5f; + } - constexpr uint32_t i2c_high_count() const { - return high_period_ns / period_ns(clock_source_f) + 0.5f; - } + constexpr uint32_t i2c_high_count() const { + return high_period_ns / period_ns(clock_source_f) + 0.5f; + } - constexpr uint32_t i2c_low_count() const { - return i2c_period_count() - i2c_high_count(); - } + constexpr uint32_t i2c_low_count() const { + return i2c_period_count() - i2c_high_count(); + } }; class I2C { -public: - using address_t = uint8_t; - - constexpr I2C(I2CDriver* const driver) : - _driver(driver) { - } - - void start(const I2CConfig& config); - void stop(); - - bool receive( - const address_t slave_address, - uint8_t* const data, const size_t count, - const systime_t timeout = TIME_INFINITE - ); - - bool transmit( - const address_t slave_address, - const uint8_t* const data, const size_t count, - const systime_t timeout = TIME_INFINITE - ); - -private: - I2CDriver* const _driver; - - bool transfer( - const address_t slave_address, - const uint8_t* const data_tx, const size_t count_tx, - uint8_t* const data_rx, const size_t count_rx, - const systime_t timeout - ); + public: + using address_t = uint8_t; + + constexpr I2C(I2CDriver* const driver) + : _driver(driver) { + } + + void start(const I2CConfig& config); + void stop(); + + bool receive( + const address_t slave_address, + uint8_t* const data, + const size_t count, + const systime_t timeout = TIME_INFINITE); + + bool transmit( + const address_t slave_address, + const uint8_t* const data, + const size_t count, + const systime_t timeout = TIME_INFINITE); + + private: + I2CDriver* const _driver; + + bool transfer( + const address_t slave_address, + const uint8_t* const data_tx, + const size_t count_tx, + uint8_t* const data_rx, + const size_t count_rx, + const systime_t timeout); }; -#endif/*__I2C_PP_H__*/ +#endif /*__I2C_PP_H__*/ diff --git a/firmware/common/i2s.hpp b/firmware/common/i2s.hpp index a7f45e98f..429eefad6 100644 --- a/firmware/common/i2s.hpp +++ b/firmware/common/i2s.hpp @@ -30,242 +30,213 @@ namespace lpc43xx { namespace i2s { enum class WordWidth { - Bits8 = 0x0, - Bits16 = 0x1, - Bits32 = 0x3, + Bits8 = 0x0, + Bits16 = 0x1, + Bits32 = 0x3, }; enum class ClockSelect { - FractionalDivider = 0x0, - BaseAudioClkOrExternalMCLK = 0x01, - OtherMCLK = 0x2, + FractionalDivider = 0x0, + BaseAudioClkOrExternalMCLK = 0x01, + OtherMCLK = 0x2, }; struct DAO { - WordWidth wordwidth; - uint32_t mono; - uint32_t stop; - uint32_t reset; - uint32_t ws_sel; - uint32_t ws_halfperiod; - uint32_t mute; - - constexpr operator uint32_t() const { - return - ((toUType(wordwidth) & 3) << 0) - | ((mono & 1) << 2) - | ((stop & 1) << 3) - | ((reset & 1) << 4) - | ((ws_sel & 1) << 5) - | ((ws_halfperiod & 0x1ff) << 6) - | ((mute & 1) << 15) - ; - } + WordWidth wordwidth; + uint32_t mono; + uint32_t stop; + uint32_t reset; + uint32_t ws_sel; + uint32_t ws_halfperiod; + uint32_t mute; + + constexpr operator uint32_t() const { + return ((toUType(wordwidth) & 3) << 0) | ((mono & 1) << 2) | ((stop & 1) << 3) | ((reset & 1) << 4) | ((ws_sel & 1) << 5) | ((ws_halfperiod & 0x1ff) << 6) | ((mute & 1) << 15); + } }; struct DAI { - WordWidth wordwidth; - uint32_t mono; - uint32_t stop; - uint32_t reset; - uint32_t ws_sel; - uint32_t ws_halfperiod; - - constexpr operator uint32_t() const { - return - ((toUType(wordwidth) & 3) << 0) - | ((mono & 1) << 2) - | ((stop & 1) << 3) - | ((reset & 1) << 4) - | ((ws_sel & 1) << 5) - | ((ws_halfperiod & 0x1ff) << 6) - ; - } + WordWidth wordwidth; + uint32_t mono; + uint32_t stop; + uint32_t reset; + uint32_t ws_sel; + uint32_t ws_halfperiod; + + constexpr operator uint32_t() const { + return ((toUType(wordwidth) & 3) << 0) | ((mono & 1) << 2) | ((stop & 1) << 3) | ((reset & 1) << 4) | ((ws_sel & 1) << 5) | ((ws_halfperiod & 0x1ff) << 6); + } }; struct MCLKRate { - uint32_t x_divider; - uint32_t y_divider; - - constexpr operator uint32_t() const { - return - ((y_divider & 0xff) << 0) - | ((x_divider & 0xff) << 8) - ; - } + uint32_t x_divider; + uint32_t y_divider; + + constexpr operator uint32_t() const { + return ((y_divider & 0xff) << 0) | ((x_divider & 0xff) << 8); + } }; struct BitRate { - uint32_t bitrate; + uint32_t bitrate; - constexpr operator uint32_t() const { - return ((bitrate & 0x3f) << 0); - } + constexpr operator uint32_t() const { + return ((bitrate & 0x3f) << 0); + } }; struct Mode { - ClockSelect clksel; - uint32_t four_pin; - uint32_t mclk_out_en; - - constexpr operator uint32_t() const { - return - ((toUType(clksel) & 3) << 0) - | ((four_pin & 1) << 2) - | ((mclk_out_en & 1) << 3) - ; - } + ClockSelect clksel; + uint32_t four_pin; + uint32_t mclk_out_en; + + constexpr operator uint32_t() const { + return ((toUType(clksel) & 3) << 0) | ((four_pin & 1) << 2) | ((mclk_out_en & 1) << 3); + } }; struct DMA { - uint32_t rx_enable; - uint32_t tx_enable; - size_t rx_depth; - size_t tx_depth; - - constexpr operator uint32_t() const { - return - ((rx_enable & 1) << 0) - | ((tx_enable & 1) << 1) - | ((rx_depth & 0xf) << 8) - | ((tx_depth & 0xf) << 16) - ; - } + uint32_t rx_enable; + uint32_t tx_enable; + size_t rx_depth; + size_t tx_depth; + + constexpr operator uint32_t() const { + return ((rx_enable & 1) << 0) | ((tx_enable & 1) << 1) | ((rx_depth & 0xf) << 8) | ((tx_depth & 0xf) << 16); + } }; struct ConfigTX { - uint32_t dao; - uint32_t txrate; - uint32_t txbitrate; - uint32_t txmode; - uint32_t sck_in_sel; + uint32_t dao; + uint32_t txrate; + uint32_t txbitrate; + uint32_t txmode; + uint32_t sck_in_sel; }; struct ConfigRX { - uint32_t dai; - uint32_t rxrate; - uint32_t rxbitrate; - uint32_t rxmode; - uint32_t sck_in_sel; + uint32_t dai; + uint32_t rxrate; + uint32_t rxbitrate; + uint32_t rxmode; + uint32_t sck_in_sel; }; struct ConfigDMA { - uint32_t dma1; - uint32_t dma2; + uint32_t dma1; + uint32_t dma2; }; static const audio_clock_resources_t audio_clock_resources = { - .base = { .clk = &LPC_CGU->BASE_AUDIO_CLK, .stat = &LPC_CCU2->BASE_STAT, .stat_mask = 0 }, - .branch = { .cfg = &LPC_CCU2->CLK_AUDIO_CFG, .stat = &LPC_CCU2->CLK_AUDIO_STAT }, + .base = {.clk = &LPC_CGU->BASE_AUDIO_CLK, .stat = &LPC_CCU2->BASE_STAT, .stat_mask = 0}, + .branch = {.cfg = &LPC_CCU2->CLK_AUDIO_CFG, .stat = &LPC_CCU2->CLK_AUDIO_STAT}, }; static const i2s_resources_t i2s_resources = { - .base = { .clk = &LPC_CGU->BASE_APB1_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 1) }, - .branch = { .cfg = &LPC_CCU1->CLK_APB1_I2S_CFG, .stat = &LPC_CCU1->CLK_APB1_I2S_STAT }, - .reset = { { .output_index = 52 }, { .output_index = 53 } }, + .base = {.clk = &LPC_CGU->BASE_APB1_CLK, .stat = &LPC_CCU1->BASE_STAT, .stat_mask = (1 << 1)}, + .branch = {.cfg = &LPC_CCU1->CLK_APB1_I2S_CFG, .stat = &LPC_CCU1->CLK_APB1_I2S_STAT}, + .reset = {{.output_index = 52}, {.output_index = 53}}, }; -template +template class I2S { -public: - static void configure( - const ConfigTX& config_tx, - const ConfigRX& config_rx - ) { - base_clock_enable(&i2s_resources.base); - branch_clock_enable(&i2s_resources.branch); - - base_clock_enable(&audio_clock_resources.base); - branch_clock_enable(&audio_clock_resources.branch); - - if( &p() == LPC_I2S0 ) { - peripheral_reset(&i2s_resources.reset[0]); - } - if( &p() == LPC_I2S1 ) { - peripheral_reset(&i2s_resources.reset[1]); - } - - reset(); - - if( &p() == LPC_I2S0 ) { - LPC_CREG->CREG6.I2S0_TX_SCK_IN_SEL = config_tx.sck_in_sel; - LPC_CREG->CREG6.I2S0_RX_SCK_IN_SEL = config_rx.sck_in_sel; - } - if( &p() == LPC_I2S1 ) { - LPC_CREG->CREG6.I2S1_TX_SCK_IN_SEL = config_tx.sck_in_sel; - LPC_CREG->CREG6.I2S1_RX_SCK_IN_SEL = config_rx.sck_in_sel; - } - - p().DAO = config_tx.dao; - p().TXRATE = config_tx.txrate; - p().TXBITRATE = config_tx.txbitrate; - p().TXMODE = config_tx.txmode; - - p().DAI = config_rx.dai; - p().RXRATE = config_rx.rxrate; - p().RXBITRATE = config_rx.rxbitrate; - p().RXMODE = config_rx.rxmode; - } - - static void configure( - const ConfigTX& config_tx, - const ConfigRX& config_rx, - const ConfigDMA& config_dma - ) { - configure(config_tx, config_rx); - - p().DMA1 = config_dma.dma1; - p().DMA2 = config_dma.dma2; - } - - static void shutdown() { - if( &p() == LPC_I2S0 ) { - peripheral_reset(&i2s_resources.reset[0]); - } - if( &p() == LPC_I2S1 ) { - peripheral_reset(&i2s_resources.reset[1]); - } - - branch_clock_disable(&audio_clock_resources.branch); - base_clock_disable(&audio_clock_resources.base); - - branch_clock_disable(&i2s_resources.branch); - base_clock_disable(&i2s_resources.base); - } - - static void rx_start() { - p().DAI &= ~(1U << 3); - } - - static void rx_stop() { - p().DAI |= (1U << 3); - } - - static void tx_start() { - p().DAO &= ~(1U << 3); - } - - static void tx_stop() { - p().DAO |= (1U << 3); - } - - static void tx_mute() { - p().DAO |= (1U << 15); - } - - static void tx_unmute() { - p().DAO &= ~(1U << 15); - } - -private: - static void reset() { - p().DAO |= (1U << 4); - p().DAI |= (1U << 4); - } - - static LPC_I2S_Type& p() { - return *reinterpret_cast(BaseAddress); - } + public: + static void configure( + const ConfigTX& config_tx, + const ConfigRX& config_rx) { + base_clock_enable(&i2s_resources.base); + branch_clock_enable(&i2s_resources.branch); + + base_clock_enable(&audio_clock_resources.base); + branch_clock_enable(&audio_clock_resources.branch); + + if (&p() == LPC_I2S0) { + peripheral_reset(&i2s_resources.reset[0]); + } + if (&p() == LPC_I2S1) { + peripheral_reset(&i2s_resources.reset[1]); + } + + reset(); + + if (&p() == LPC_I2S0) { + LPC_CREG->CREG6.I2S0_TX_SCK_IN_SEL = config_tx.sck_in_sel; + LPC_CREG->CREG6.I2S0_RX_SCK_IN_SEL = config_rx.sck_in_sel; + } + if (&p() == LPC_I2S1) { + LPC_CREG->CREG6.I2S1_TX_SCK_IN_SEL = config_tx.sck_in_sel; + LPC_CREG->CREG6.I2S1_RX_SCK_IN_SEL = config_rx.sck_in_sel; + } + + p().DAO = config_tx.dao; + p().TXRATE = config_tx.txrate; + p().TXBITRATE = config_tx.txbitrate; + p().TXMODE = config_tx.txmode; + + p().DAI = config_rx.dai; + p().RXRATE = config_rx.rxrate; + p().RXBITRATE = config_rx.rxbitrate; + p().RXMODE = config_rx.rxmode; + } + + static void configure( + const ConfigTX& config_tx, + const ConfigRX& config_rx, + const ConfigDMA& config_dma) { + configure(config_tx, config_rx); + + p().DMA1 = config_dma.dma1; + p().DMA2 = config_dma.dma2; + } + + static void shutdown() { + if (&p() == LPC_I2S0) { + peripheral_reset(&i2s_resources.reset[0]); + } + if (&p() == LPC_I2S1) { + peripheral_reset(&i2s_resources.reset[1]); + } + + branch_clock_disable(&audio_clock_resources.branch); + base_clock_disable(&audio_clock_resources.base); + + branch_clock_disable(&i2s_resources.branch); + base_clock_disable(&i2s_resources.base); + } + + static void rx_start() { + p().DAI &= ~(1U << 3); + } + + static void rx_stop() { + p().DAI |= (1U << 3); + } + + static void tx_start() { + p().DAO &= ~(1U << 3); + } + + static void tx_stop() { + p().DAO |= (1U << 3); + } + + static void tx_mute() { + p().DAO |= (1U << 15); + } + + static void tx_unmute() { + p().DAO &= ~(1U << 15); + } + + private: + static void reset() { + p().DAO |= (1U << 4); + p().DAI |= (1U << 4); + } + + static LPC_I2S_Type& p() { + return *reinterpret_cast(BaseAddress); + } }; using i2s0 = I2S; @@ -274,4 +245,4 @@ using i2s1 = I2S; } /* namespace i2s */ } /* namespace lpc43xx */ -#endif/*__I2S_H__*/ +#endif /*__I2S_H__*/ diff --git a/firmware/common/jammer.hpp b/firmware/common/jammer.hpp index 140d7559a..29fadde8d 100644 --- a/firmware/common/jammer.hpp +++ b/firmware/common/jammer.hpp @@ -29,17 +29,17 @@ namespace jammer { typedef struct jammer_range { - bool enabled; - int64_t min; - int64_t max; + bool enabled; + int64_t min; + int64_t max; } jammer_range_t; enum JammerType : uint32_t { - TYPE_FSK = 0, - TYPE_TONE = 1, - TYPE_SWEEP = 2 + TYPE_FSK = 0, + TYPE_TONE = 1, + TYPE_SWEEP = 2 }; } /* namespace jammer */ -#endif/*__JAMMER_H__*/ +#endif /*__JAMMER_H__*/ diff --git a/firmware/common/jtag.cpp b/firmware/common/jtag.cpp index 84679e33e..aea27cee0 100644 --- a/firmware/common/jtag.cpp +++ b/firmware/common/jtag.cpp @@ -27,15 +27,14 @@ namespace jtag { uint32_t JTAG::shift(const size_t count, uint32_t value) { - for(size_t i=0; i>= 1; - value |= tdo << (count - 1); - } - return value; + for (size_t i = 0; i < count; i++) { + const auto tdo = target.clock( + (i == (count - 1)) ? 1 : 0, + value & 1); + value >>= 1; + value |= tdo << (count - 1); + } + return value; } } /* namespace jtag */ diff --git a/firmware/common/jtag.hpp b/firmware/common/jtag.hpp index d0f72035f..37e18e7fc 100644 --- a/firmware/common/jtag.hpp +++ b/firmware/common/jtag.hpp @@ -32,70 +32,69 @@ namespace jtag { class JTAG { -public: - constexpr JTAG( - Target& target - ) : target(target) - { - } - - void reset() { - /* ??? -> Test-Logic-Reset */ - for(size_t i=0; i<8; i++) { - target.clock(1, 0); - } - } - - void run_test_idle() { - /* Test-Logic-Reset -> Run-Test/Idle */ - target.clock(0, 0); - } - - void runtest_tck(const size_t count) { - target.delay(count); - } - - uint32_t shift_ir(const size_t count, const uint32_t value) { - /* Run-Test/Idle -> Select-DR-Scan -> Select-IR-Scan */ - target.clock(1, 0); - target.clock(1, 0); - /* Scan -> Capture -> Shift */ - target.clock(0, 0); - target.clock(0, 0); - - const auto result = shift(count, value); - - /* Exit1 -> Update */ - target.clock(1, 0); - /* Update -> Run-Test/Idle */ - target.clock(0, 0); - - return result; - } - - uint32_t shift_dr(const size_t count, const uint32_t value) { - /* Run-Test/Idle -> Select-DR-Scan */ - target.clock(1, 0); - /* Scan -> Capture -> Shift */ - target.clock(0, 0); - target.clock(0, 0); - - const auto result = shift(count, value); - - /* Exit1 -> Update */ - target.clock(1, 0); - /* Update -> Run-Test/Idle */ - target.clock(0, 0); - - return result; - } - -private: - Target& target; - - uint32_t shift(const size_t count, uint32_t value); + public: + constexpr JTAG( + Target& target) + : target(target) { + } + + void reset() { + /* ??? -> Test-Logic-Reset */ + for (size_t i = 0; i < 8; i++) { + target.clock(1, 0); + } + } + + void run_test_idle() { + /* Test-Logic-Reset -> Run-Test/Idle */ + target.clock(0, 0); + } + + void runtest_tck(const size_t count) { + target.delay(count); + } + + uint32_t shift_ir(const size_t count, const uint32_t value) { + /* Run-Test/Idle -> Select-DR-Scan -> Select-IR-Scan */ + target.clock(1, 0); + target.clock(1, 0); + /* Scan -> Capture -> Shift */ + target.clock(0, 0); + target.clock(0, 0); + + const auto result = shift(count, value); + + /* Exit1 -> Update */ + target.clock(1, 0); + /* Update -> Run-Test/Idle */ + target.clock(0, 0); + + return result; + } + + uint32_t shift_dr(const size_t count, const uint32_t value) { + /* Run-Test/Idle -> Select-DR-Scan */ + target.clock(1, 0); + /* Scan -> Capture -> Shift */ + target.clock(0, 0); + target.clock(0, 0); + + const auto result = shift(count, value); + + /* Exit1 -> Update */ + target.clock(1, 0); + /* Update -> Run-Test/Idle */ + target.clock(0, 0); + + return result; + } + + private: + Target& target; + + uint32_t shift(const size_t count, uint32_t value); }; } /* namespace jtag */ -#endif/*__JTAG_H__*/ +#endif /*__JTAG_H__*/ diff --git a/firmware/common/jtag_tap.cpp b/firmware/common/jtag_tap.cpp index 57541b85c..c00843e91 100644 --- a/firmware/common/jtag_tap.cpp +++ b/firmware/common/jtag_tap.cpp @@ -29,44 +29,44 @@ namespace jtag { namespace tap { size_t bits_t::length() const { - return count; + return count; } bits_t::operator bool() const { - return (count > 0); + return (count > 0); } bool bits_t::operator[](const size_t index) const { - if( p && (index < count) ) { - const auto n = bytes() * 8 - index - 1; - const auto byte = n >> 3; - const auto bit = (n ^ 7) & 7; - return (p[byte] >> bit) & 1; - } else { - return default_value; - } + if (p && (index < count)) { + const auto n = bytes() * 8 - index - 1; + const auto byte = n >> 3; + const auto bit = (n ^ 7) & 7; + return (p[byte] >> bit) & 1; + } else { + return default_value; + } } size_t bits_t::bytes() const { - return (count + 7) >> 3; + return (count + 7) >> 3; } #if JTAG_TAP_DEBUG static char nibble_to_hex_char(const uint8_t v) { - if( v < 0xa ) { - return 0x30 + v; - } else { - return 0x57 + v; - } + if (v < 0xa) { + return 0x30 + v; + } else { + return 0x57 + v; + } } std::string to_string(const bits_t& bits) { - std::string s { std::to_string(bits.length()) + "/0x" }; - for(size_t i=0; i> 4) & 0xf); - s += nibble_to_hex_char((bits.p[i] >> 0) & 0xf); - } - return s; + std::string s{std::to_string(bits.length()) + "/0x"}; + for (size_t i = 0; i < bytes(); i++) { + s += nibble_to_hex_char((bits.p[i] >> 4) & 0xf); + s += nibble_to_hex_char((bits.p[i] >> 0) & 0xf); + } + return s; } #endif @@ -75,191 +75,191 @@ namespace { using route_t = uint16_t; struct entry_t { - state_t tms[2]; - route_t route; + state_t tms[2]; + route_t route; }; static_assert(sizeof(entry_t) == 4, "Unexpected size of entry_t"); using table_t = std::array; static_assert(sizeof(table_t) == (16 * 4), "Unexpected size of table_t"); -const table_t table { { - { state_t::run_test_idle, state_t::test_logic_reset, 0b0000000000000001 }, // test_logic_reset test_logic_reset => 1, others => 0 - { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // run_test_idle run_test_idle => 0, others => 1 - { state_t::capture_dr, state_t::select_ir_scan, 0b1111111000000001 }, // select_dr_scan run_test_idle..update_dr => 0, others => 1 - { state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111 }, // capture_dr shift_dr => 0, others => 1 - { state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111 }, // shift_dr shift_dr => 0, others => 1 - { state_t::pause_dr, state_t::update_dr, 0b1111111100111111 }, // exit1_dr pause_dr, exit2_dr => 0, others => 1 - { state_t::pause_dr, state_t::exit2_dr, 0b1111111110111111 }, // pause_dr pause_dr => 0, others => 1 - { state_t::shift_dr, state_t::update_dr, 0b1111111111101111 }, // exit2_dr shift_dr => 0, others => 1 - { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // update_dr run_test_idle => 0, others => 1 - { state_t::capture_ir, state_t::test_logic_reset, 0b0000000000000001 }, // select_ir_scan test_logic_reset => 1, others => 0 - { state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111 }, // capture_ir shift_ir => 0, others => 1 - { state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111 }, // shift_ir shift_ir => 0, others => 1 - { state_t::pause_ir, state_t::update_ir, 0b1001111111111111 }, // exit1_ir pause_ir, exit2_ir => 0, others => 1 - { state_t::pause_ir, state_t::exit2_ir, 0b1101111111111111 }, // pause_ir pause_ir => 0, others => 1 - { state_t::shift_ir, state_t::update_ir, 0b1111011111111111 }, // exit2_ir shift_ir => 0, others => 1 - { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // update_ir run_test_idle => 0, others => 1 -} }; - -const std::array state_name { { - "test_logic_reset", - "run_test_idle", - "select_dr_scan", - "capture_dr", - "shift_dr", - "exit1_dr", - "pause_dr", - "exit2_dr", - "update_dr", - "select_ir_scan", - "capture_ir", - "shift_ir", - "exit1_ir", - "pause_ir", - "exit2_ir", - "update_ir", -} }; - -const std::array state_long_name { { - "Test-Logic-Reset", - "Run-Test/Idle", - "Select-DR-Scan", - "Capture-DR", - "Shift-DR", - "Exit1-DR", - "Pause-DR", - "Exit2-DR", - "Update-DR", - "Select-IR-Scan", - "Capture-IR", - "Shift-IR", - "Exit1-IR", - "Pause-IR", - "Exit2-IR", - "Update-IR", -} }; +const table_t table{{ + {state_t::run_test_idle, state_t::test_logic_reset, 0b0000000000000001}, // test_logic_reset test_logic_reset => 1, others => 0 + {state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101}, // run_test_idle run_test_idle => 0, others => 1 + {state_t::capture_dr, state_t::select_ir_scan, 0b1111111000000001}, // select_dr_scan run_test_idle..update_dr => 0, others => 1 + {state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111}, // capture_dr shift_dr => 0, others => 1 + {state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111}, // shift_dr shift_dr => 0, others => 1 + {state_t::pause_dr, state_t::update_dr, 0b1111111100111111}, // exit1_dr pause_dr, exit2_dr => 0, others => 1 + {state_t::pause_dr, state_t::exit2_dr, 0b1111111110111111}, // pause_dr pause_dr => 0, others => 1 + {state_t::shift_dr, state_t::update_dr, 0b1111111111101111}, // exit2_dr shift_dr => 0, others => 1 + {state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101}, // update_dr run_test_idle => 0, others => 1 + {state_t::capture_ir, state_t::test_logic_reset, 0b0000000000000001}, // select_ir_scan test_logic_reset => 1, others => 0 + {state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111}, // capture_ir shift_ir => 0, others => 1 + {state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111}, // shift_ir shift_ir => 0, others => 1 + {state_t::pause_ir, state_t::update_ir, 0b1001111111111111}, // exit1_ir pause_ir, exit2_ir => 0, others => 1 + {state_t::pause_ir, state_t::exit2_ir, 0b1101111111111111}, // pause_ir pause_ir => 0, others => 1 + {state_t::shift_ir, state_t::update_ir, 0b1111011111111111}, // exit2_ir shift_ir => 0, others => 1 + {state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101}, // update_ir run_test_idle => 0, others => 1 +}}; + +const std::array state_name{{ + "test_logic_reset", + "run_test_idle", + "select_dr_scan", + "capture_dr", + "shift_dr", + "exit1_dr", + "pause_dr", + "exit2_dr", + "update_dr", + "select_ir_scan", + "capture_ir", + "shift_ir", + "exit1_ir", + "pause_ir", + "exit2_ir", + "update_ir", +}}; + +const std::array state_long_name{{ + "Test-Logic-Reset", + "Run-Test/Idle", + "Select-DR-Scan", + "Capture-DR", + "Shift-DR", + "Exit1-DR", + "Pause-DR", + "Exit2-DR", + "Update-DR", + "Select-IR-Scan", + "Capture-IR", + "Shift-IR", + "Exit1-IR", + "Pause-IR", + "Exit2-IR", + "Update-IR", +}}; const entry_t& entry(const state_t state) { - return table[toUType(state)]; + return table[toUType(state)]; } } /* namespace */ const char* c_str(const state_t state) { - return state_name[toUType(state)]; + return state_name[toUType(state)]; } /* TAPState **************************************************************/ state_t TAPState::state() const { - return _state; + return _state; } void TAPState::advance(const bool tms) { - _state = entry(_state).tms[tms]; + _state = entry(_state).tms[tms]; } bool TAPState::advance_toward(const state_t desired_state) const { - return (entry(_state).route >> toUType(desired_state)) & 1; + return (entry(_state).route >> toUType(desired_state)) & 1; } /* TAPMachine ************************************************************/ void TAPMachine::set_run_test(const uint32_t value) { - _run_test = value; + _run_test = value; } void TAPMachine::set_repeat(const uint8_t value) { - _repeat = value; + _repeat = value; } void TAPMachine::set_end_ir(const state_t state) { - _end_ir = state; + _end_ir = state; } void TAPMachine::set_end_dr(const state_t state) { - _end_dr = state; + _end_dr = state; } bool TAPMachine::shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected, const bits_t& tdo_mask) { - return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_ir, _end_ir, _run_test); + return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_ir, _end_ir, _run_test); } bool TAPMachine::shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected, const bits_t& tdo_mask) { - return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test); + return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test); } void TAPMachine::state(const state_t state) { - if( state == state_t::test_logic_reset ) { - for(int i=0; i<5; i++) { - clock(1); - } - } else { - advance_to_state(state); - } + if (state == state_t::test_logic_reset) { + for (int i = 0; i < 5; i++) { + clock(1); + } + } else { + advance_to_state(state); + } } void TAPMachine::wait(const state_t wait_state, const state_t end_state, const uint32_t wait_time) { - advance_to_state(wait_state); - delay_us(wait_time); - advance_to_state(end_state); + advance_to_state(wait_state); + delay_us(wait_time); + advance_to_state(end_state); } bool TAPMachine::clock(const bool tms, const bool tdi) { - tap.advance(tms); - return target.clock(tms, tdi); + tap.advance(tms); + return target.clock(tms, tdi); } void TAPMachine::advance_to_state(const state_t desired_state) { - while( tap.state() != desired_state ) { - const auto tms = tap.advance_toward(desired_state); - clock(tms); - } + while (tap.state() != desired_state) { + const auto tms = tap.advance_toward(desired_state); + clock(tms); + } } void TAPMachine::delay_us(const uint32_t microseconds) { - target.delay(microseconds); + target.delay(microseconds); } void TAPMachine::shift_start(const state_t state) { - advance_to_state(state); + advance_to_state(state); } bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms) { - if( tdo_expected.length() != tdo_mask.length() ) { - return false; - } - if( tdo_expected && (tdi.length() != tdo_expected.length()) ) { - return false; - } - - auto tdo_error = false; - for(uint32_t i=0; i S1) - // GS = 0 (gate output scan direction G1 -> G320) - // REV = 1 (normally white) - // NL = 0b100111 (default) - // PCDIV = 0b000000 (default?) - io.lcd_data_write_command_and_data(0xB6, { 0x0A, 0xA2, 0x27, 0x00 }); - - // Power Control 1 - //VRH[5:0] - io.lcd_data_write_command_and_data(0xC0, { 0x1B }); - - // Power Control 2 - //SAP[2:0];BT[3:0] - io.lcd_data_write_command_and_data(0xC1, { 0x12 }); - - // VCOM Control 1 - io.lcd_data_write_command_and_data(0xC5, { 0x32, 0x3C }); - - // VCOM Control 2 - io.lcd_data_write_command_and_data(0xC7, { 0x9B }); - - // Memory Access Control - // Invert X and Y memory access order, so upper-left of - // screen is (0,0) when writing to display. - io.lcd_data_write_command_and_data(0x36, { - (1 << 7) | // MY=1 - (1 << 6) | // MX=1 - (0 << 5) | // MV=0 - (1 << 4) | // ML=1: reverse vertical refresh to simplify scrolling logic - (1 << 3) // BGR=1: For Kingtech LCD, BGR filter. - }); - - // COLMOD: Pixel Format Set - // DPI=101 (16 bits/pixel), DBI=101 (16 bits/pixel) - io.lcd_data_write_command_and_data(0x3A, { 0x55 }); - - //io.lcd_data_write_command_and_data(0xF6, { 0x01, 0x30 }); - // WEMODE=1 (reset column and page number on overflow) - // MDT[1:0] - // EPF[1:0]=00 (use channel MSB for LSB) - // RIM=0 (If COLMOD[6:4]=101 (65k color), 16-bit RGB interface (1 transfer/pixel)) - // RM=0 (system interface/VSYNC interface) - // DM[1:0]=00 (internal clock operation) - // ENDIAN=0 (doesn't matter with 16-bit interface) - io.lcd_data_write_command_and_data(0xF6, { 0x01, 0x30, 0x00 }); - - // 3Gamma Function Disable - io.lcd_data_write_command_and_data(0xF2, { 0x00 }); - - // Gamma curve selected - io.lcd_data_write_command_and_data(0x26, { 0x01 }); - - // Set Gamma - io.lcd_data_write_command_and_data(0xE0, { - 0x0F, 0x1D, 0x19, 0x0E, 0x10, 0x07, 0x4C, 0x63, - 0x3F, 0x03, 0x0D, 0x00, 0x26, 0x24, 0x04 - }); - - // Set Gamma - io.lcd_data_write_command_and_data(0xE1, { - 0x00, 0x1C, 0x1F, 0x02, 0x0F, 0x03, 0x35, 0x25, - 0x47, 0x04, 0x0C, 0x0B, 0x29, 0x2F, 0x05 - }); - - lcd_wake(); - - // Turn on Tearing Effect Line (TE) output signal. - io.lcd_data_write_command_and_data(0x35, { 0b00000000 }); + // LCDs are configured for IM[2:0] = 001 + // 8080-I system, 16-bit parallel bus + + // + // 0x3a: DBI[2:0] = 101 + // MDT[1:0] = XX (if not in 18-bit mode, right?) + + // Power control B + // 0 + // PCEQ=1, DRV_ena=0, Power control=3 + io.lcd_data_write_command_and_data(0xCF, {0x00, 0xD9, 0x30}); + + // Power on sequence control + io.lcd_data_write_command_and_data(0xED, {0x64, 0x03, 0x12, 0x81}); + + // Driver timing control A + io.lcd_data_write_command_and_data(0xE8, {0x85, 0x10, 0x78}); + + // Power control A + io.lcd_data_write_command_and_data(0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}); + + // Pump ratio control + io.lcd_data_write_command_and_data(0xF7, {0x20}); + + // Driver timing control B + io.lcd_data_write_command_and_data(0xEA, {0x00, 0x00}); + + io.lcd_data_write_command_and_data(0xB1, {0x00, 0x1B}); + + // Blanking Porch Control + // VFP = 0b0000010 = 2 (number of HSYNC of vertical front porch) + // VBP = 0b0000010 = 2 (number of HSYNC of vertical back porch) + // HFP = 0b0001010 = 10 (number of DOTCLOCK of horizontal front porch) + // HBP = 0b0010100 = 20 (number of DOTCLOCK of horizontal back porch) + io.lcd_data_write_command_and_data(0xB5, {0x02, 0x02, 0x0a, 0x14}); + + // Display Function Control + // PT[1:0] = 0b10 + // PTG[1:0] = 0b10 + // ISC[3:0] = 0b0010 (scan cycle interval of gate driver: 5 frames) + // SM = 0 (gate driver pin arrangement in combination with GS) + // SS = 1 (source output scan direction S720 -> S1) + // GS = 0 (gate output scan direction G1 -> G320) + // REV = 1 (normally white) + // NL = 0b100111 (default) + // PCDIV = 0b000000 (default?) + io.lcd_data_write_command_and_data(0xB6, {0x0A, 0xA2, 0x27, 0x00}); + + // Power Control 1 + // VRH[5:0] + io.lcd_data_write_command_and_data(0xC0, {0x1B}); + + // Power Control 2 + // SAP[2:0];BT[3:0] + io.lcd_data_write_command_and_data(0xC1, {0x12}); + + // VCOM Control 1 + io.lcd_data_write_command_and_data(0xC5, {0x32, 0x3C}); + + // VCOM Control 2 + io.lcd_data_write_command_and_data(0xC7, {0x9B}); + + // Memory Access Control + // Invert X and Y memory access order, so upper-left of + // screen is (0,0) when writing to display. + io.lcd_data_write_command_and_data(0x36, { + (1 << 7) | // MY=1 + (1 << 6) | // MX=1 + (0 << 5) | // MV=0 + (1 << 4) | // ML=1: reverse vertical refresh to simplify scrolling logic + (1 << 3) // BGR=1: For Kingtech LCD, BGR filter. + }); + + // COLMOD: Pixel Format Set + // DPI=101 (16 bits/pixel), DBI=101 (16 bits/pixel) + io.lcd_data_write_command_and_data(0x3A, {0x55}); + + // io.lcd_data_write_command_and_data(0xF6, { 0x01, 0x30 }); + // WEMODE=1 (reset column and page number on overflow) + // MDT[1:0] + // EPF[1:0]=00 (use channel MSB for LSB) + // RIM=0 (If COLMOD[6:4]=101 (65k color), 16-bit RGB interface (1 transfer/pixel)) + // RM=0 (system interface/VSYNC interface) + // DM[1:0]=00 (internal clock operation) + // ENDIAN=0 (doesn't matter with 16-bit interface) + io.lcd_data_write_command_and_data(0xF6, {0x01, 0x30, 0x00}); + + // 3Gamma Function Disable + io.lcd_data_write_command_and_data(0xF2, {0x00}); + + // Gamma curve selected + io.lcd_data_write_command_and_data(0x26, {0x01}); + + // Set Gamma + io.lcd_data_write_command_and_data(0xE0, {0x0F, 0x1D, 0x19, 0x0E, 0x10, 0x07, 0x4C, 0x63, + 0x3F, 0x03, 0x0D, 0x00, 0x26, 0x24, 0x04}); + + // Set Gamma + io.lcd_data_write_command_and_data(0xE1, {0x00, 0x1C, 0x1F, 0x02, 0x0F, 0x03, 0x35, 0x25, + 0x47, 0x04, 0x0C, 0x0B, 0x29, 0x2F, 0x05}); + + lcd_wake(); + + // Turn on Tearing Effect Line (TE) output signal. + io.lcd_data_write_command_and_data(0x35, {0b00000000}); } void lcd_set(const uint_fast8_t command, const uint_fast16_t start, const uint_fast16_t end) { - io.lcd_data_write_command_and_data(command, { - static_cast(start >> 8), static_cast(start & 0xff), - static_cast(end >> 8), static_cast(end & 0xff) - }); + io.lcd_data_write_command_and_data(command, {static_cast(start >> 8), static_cast(start & 0xff), + static_cast(end >> 8), static_cast(end & 0xff)}); } void lcd_ramwr_start() { - io.lcd_data_write_command_and_data(0x2c, {}); + io.lcd_data_write_command_and_data(0x2c, {}); } void lcd_ramrd_start() { - io.lcd_data_write_command_and_data(0x2e, {}); - io.lcd_read_word(); + io.lcd_data_write_command_and_data(0x2e, {}); + io.lcd_read_word(); } void lcd_caset(const uint_fast16_t start_column, uint_fast16_t end_column) { - lcd_set(0x2a, start_column, end_column); + lcd_set(0x2a, start_column, end_column); } void lcd_paset(const uint_fast16_t start_page, const uint_fast16_t end_page) { - lcd_set(0x2b, start_page, end_page); + lcd_set(0x2b, start_page, end_page); } void lcd_start_ram_write( - const ui::Point p, - const ui::Size s -) { - lcd_caset(p.x(), p.x() + s.width() - 1); - lcd_paset(p.y(), p.y() + s.height() - 1); - lcd_ramwr_start(); + const ui::Point p, + const ui::Size s) { + lcd_caset(p.x(), p.x() + s.width() - 1); + lcd_paset(p.y(), p.y() + s.height() - 1); + lcd_ramwr_start(); } void lcd_start_ram_read( - const ui::Point p, - const ui::Size s -) { - lcd_caset(p.x(), p.x() + s.width() - 1); - lcd_paset(p.y(), p.y() + s.height() - 1); - lcd_ramrd_start(); + const ui::Point p, + const ui::Size s) { + lcd_caset(p.x(), p.x() + s.width() - 1); + lcd_paset(p.y(), p.y() + s.height() - 1); + lcd_ramrd_start(); } void lcd_start_ram_write( - const ui::Rect& r -) { - lcd_start_ram_write(r.location(), r.size()); + const ui::Rect& r) { + lcd_start_ram_write(r.location(), r.size()); } void lcd_start_ram_read( - const ui::Rect& r -) { - lcd_start_ram_read(r.location(), r.size()); + const ui::Rect& r) { + lcd_start_ram_read(r.location(), r.size()); } void lcd_vertical_scrolling_definition( - const uint_fast16_t top_fixed_area, - const uint_fast16_t vertical_scrolling_area, - const uint_fast16_t bottom_fixed_area -) { - io.lcd_data_write_command_and_data(0x33, { - static_cast(top_fixed_area >> 8), - static_cast(top_fixed_area & 0xff), - static_cast(vertical_scrolling_area >> 8), - static_cast(vertical_scrolling_area & 0xff), - static_cast(bottom_fixed_area >> 8), - static_cast(bottom_fixed_area & 0xff) - }); + const uint_fast16_t top_fixed_area, + const uint_fast16_t vertical_scrolling_area, + const uint_fast16_t bottom_fixed_area) { + io.lcd_data_write_command_and_data(0x33, {static_cast(top_fixed_area >> 8), + static_cast(top_fixed_area & 0xff), + static_cast(vertical_scrolling_area >> 8), + static_cast(vertical_scrolling_area & 0xff), + static_cast(bottom_fixed_area >> 8), + static_cast(bottom_fixed_area & 0xff)}); } void lcd_vertical_scrolling_start_address( - const uint_fast16_t vertical_scrolling_pointer -) { - io.lcd_data_write_command_and_data(0x37, { - static_cast(vertical_scrolling_pointer >> 8), - static_cast(vertical_scrolling_pointer & 0xff) - }); + const uint_fast16_t vertical_scrolling_pointer) { + io.lcd_data_write_command_and_data(0x37, {static_cast(vertical_scrolling_pointer >> 8), + static_cast(vertical_scrolling_pointer & 0xff)}); } -} +} // namespace void ILI9341::init() { - lcd_reset(); - lcd_init(); + lcd_reset(); + lcd_init(); } void ILI9341::shutdown() { - lcd_reset(); + lcd_reset(); } void ILI9341::sleep() { - lcd_sleep(); + lcd_sleep(); } void ILI9341::wake() { - lcd_wake(); + lcd_wake(); } void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) { - const auto r_clipped = r.intersect(screen_rect()); - if( !r_clipped.is_empty() ) { - lcd_start_ram_write(r_clipped); - size_t count = r_clipped.width() * r_clipped.height(); - io.lcd_write_pixels(c, count); - } + const auto r_clipped = r.intersect(screen_rect()); + if (!r_clipped.is_empty()) { + lcd_start_ram_write(r_clipped); + size_t count = r_clipped.width() * r_clipped.height(); + io.lcd_write_pixels(c, count); + } } void ILI9341::fill_rectangle_unrolled8(ui::Rect r, const ui::Color c) { - const auto r_clipped = r.intersect(screen_rect()); - if( !r_clipped.is_empty() ) { - lcd_start_ram_write(r_clipped); - size_t count = r_clipped.width() * r_clipped.height(); - io.lcd_write_pixels_unrolled8(c, count); - } + const auto r_clipped = r.intersect(screen_rect()); + if (!r_clipped.is_empty()) { + lcd_start_ram_write(r_clipped); + size_t count = r_clipped.width() * r_clipped.height(); + io.lcd_write_pixels_unrolled8(c, count); + } } void ILI9341::render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer) { - lcd_start_ram_write(p, { count, 1 }); - io.lcd_write_pixels(line_buffer, count); + lcd_start_ram_write(p, {count, 1}); + io.lcd_write_pixels(line_buffer, count); } void ILI9341::render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer) { - lcd_start_ram_write(p, s); - io.lcd_write_pixels(line_buffer, s.width() * s.height()); + lcd_start_ram_write(p, s); + io.lcd_write_pixels(line_buffer, s.width() * s.height()); } // RLE_4 BMP loader (delta not implemented) -void ILI9341::drawBMP(const ui::Point p, const uint8_t * bitmap, const bool transparency) { - const bmp_header_t * bmp_header = (const bmp_header_t *)bitmap; - uint32_t data_idx; - uint8_t by, c, count, transp_idx = 0; - ui::Color line_buffer[240]; - uint16_t px = 0, py; - ui::Color palette[16]; - - // Abort if bad depth or no RLE - if ((bmp_header->bpp != 4) || - (bmp_header->compression != 2)) return; - - data_idx = bmp_header->image_data; - const bmp_palette_t * bmp_palette = (const bmp_palette_t *)&bitmap[bmp_header->BIH_size + 14]; - - // Convert palette and find pure magenta index (alpha color key) - for (c = 0; c < 16; c++) { - palette[c] = ui::Color(bmp_palette->color[c].R, bmp_palette->color[c].G, bmp_palette->color[c].B); - if ((bmp_palette->color[c].R == 0xFF) && - (bmp_palette->color[c].G == 0x00) && - (bmp_palette->color[c].B == 0xFF)) transp_idx = c; - } - - if (!transparency) { - py = bmp_header->height + 16; - do { - by = bitmap[data_idx++]; - if (by) { - count = by >> 1; - by = bitmap[data_idx++]; - for (c = 0; c < count; c++) { - line_buffer[px++] = palette[by >> 4]; - if (px < bmp_header->width) line_buffer[px++] = palette[by & 15]; - } - if (data_idx & 1) data_idx++; - } else { - by = bitmap[data_idx++]; - if (by == 0) { - render_line({p.x(), p.y() + py}, bmp_header->width, line_buffer); - py--; - px = 0; - } else if (by == 1) { - break; - } else if (by == 2) { - // Delta - } else { - count = by >> 1; - for (c = 0; c < count; c++) { - by = bitmap[data_idx++]; - line_buffer[px++] = palette[by >> 4]; - if (px < bmp_header->width) line_buffer[px++] = palette[by & 15]; - } - if (data_idx & 1) data_idx++; - } - } - } while (1); - } else { - py = bmp_header->height; - do { - by = bitmap[data_idx++]; - if (by) { - count = by >> 1; - by = bitmap[data_idx++]; - for (c = 0; c < count; c++) { - if ((by >> 4) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by >> 4]); - px++; - if (px < bmp_header->width) { - if ((by & 15) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by & 15]); - } - px++; - } - if (data_idx & 1) data_idx++; - } else { - by = bitmap[data_idx++]; - if (by == 0) { - py--; - px = 0; - } else if (by == 1) { - break; - } else if (by == 2) { - // Delta - } else { - count = by >> 1; - for (c = 0; c < count; c++) { - by = bitmap[data_idx++]; - if ((by >> 4) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by >> 4]); - px++; - if (px < bmp_header->width) { - if ((by & 15) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by & 15]); - } - px++; - } - if (data_idx & 1) data_idx++; - } - } - } while (1); - } +void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const bool transparency) { + const bmp_header_t* bmp_header = (const bmp_header_t*)bitmap; + uint32_t data_idx; + uint8_t by, c, count, transp_idx = 0; + ui::Color line_buffer[240]; + uint16_t px = 0, py; + ui::Color palette[16]; + + // Abort if bad depth or no RLE + if ((bmp_header->bpp != 4) || + (bmp_header->compression != 2)) return; + + data_idx = bmp_header->image_data; + const bmp_palette_t* bmp_palette = (const bmp_palette_t*)&bitmap[bmp_header->BIH_size + 14]; + + // Convert palette and find pure magenta index (alpha color key) + for (c = 0; c < 16; c++) { + palette[c] = ui::Color(bmp_palette->color[c].R, bmp_palette->color[c].G, bmp_palette->color[c].B); + if ((bmp_palette->color[c].R == 0xFF) && + (bmp_palette->color[c].G == 0x00) && + (bmp_palette->color[c].B == 0xFF)) transp_idx = c; + } + + if (!transparency) { + py = bmp_header->height + 16; + do { + by = bitmap[data_idx++]; + if (by) { + count = by >> 1; + by = bitmap[data_idx++]; + for (c = 0; c < count; c++) { + line_buffer[px++] = palette[by >> 4]; + if (px < bmp_header->width) line_buffer[px++] = palette[by & 15]; + } + if (data_idx & 1) data_idx++; + } else { + by = bitmap[data_idx++]; + if (by == 0) { + render_line({p.x(), p.y() + py}, bmp_header->width, line_buffer); + py--; + px = 0; + } else if (by == 1) { + break; + } else if (by == 2) { + // Delta + } else { + count = by >> 1; + for (c = 0; c < count; c++) { + by = bitmap[data_idx++]; + line_buffer[px++] = palette[by >> 4]; + if (px < bmp_header->width) line_buffer[px++] = palette[by & 15]; + } + if (data_idx & 1) data_idx++; + } + } + } while (1); + } else { + py = bmp_header->height; + do { + by = bitmap[data_idx++]; + if (by) { + count = by >> 1; + by = bitmap[data_idx++]; + for (c = 0; c < count; c++) { + if ((by >> 4) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by >> 4]); + px++; + if (px < bmp_header->width) { + if ((by & 15) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by & 15]); + } + px++; + } + if (data_idx & 1) data_idx++; + } else { + by = bitmap[data_idx++]; + if (by == 0) { + py--; + px = 0; + } else if (by == 1) { + break; + } else if (by == 2) { + // Delta + } else { + count = by >> 1; + for (c = 0; c < count; c++) { + by = bitmap[data_idx++]; + if ((by >> 4) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by >> 4]); + px++; + if (px < bmp_header->width) { + if ((by & 15) != transp_idx) draw_pixel({static_cast(p.x() + px), static_cast(p.y() + py)}, palette[by & 15]); + } + px++; + } + if (data_idx & 1) data_idx++; + } + } + } while (1); + } } /* - Draw BMP from SD card. - Currently supported formats: - 16bpp ARGB, RGB565 - 24bpp RGB - 32bpp ARGB + Draw BMP from SD card. + Currently supported formats: + 16bpp ARGB, RGB565 + 24bpp RGB + 32bpp ARGB */ bool ILI9341::drawBMP2(const ui::Point p, const std::string file) { - File bmpimage; - size_t file_pos = 0; - uint16_t pointer = 0; - int16_t px = 0, py, width, height; - bmp_header_t bmp_header; - uint8_t type = 0; - char buffer[257]; - ui::Color line_buffer[240]; - - auto result = bmpimage.open(file); - if(result.is_valid()) - return false; - - bmpimage.seek(file_pos); - auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header)); - if (!((bmp_header.signature == 0x4D42) && // "BM" Signature - (bmp_header.planes == 1) && // Seems always to be 1 - (bmp_header.compression == 0 || bmp_header.compression == 3 ))) { // No compression - return false; - } - - switch(bmp_header.bpp) { - case 16: - file_pos = 0x36; - memset(buffer, 0, 16); - bmpimage.read(buffer, 16); - if(buffer[1] == 0x7C) - type = 3; // A1R5G5B5 - else - type = 0; // R5G6B5 - break; - case 24: - type = 1; - break; - case 32: - default: - type = 2; - break; - } - - width = bmp_header.width; - height = bmp_header.height; - - file_pos = bmp_header.image_data; - - py = height + 16; - - while(1) { - while(px < width) { - bmpimage.seek(file_pos); - memset(buffer, 0, 257); - read_size = bmpimage.read(buffer, 256); - if (read_size.is_error()) - return false; // Read error - - pointer = 0; - while(pointer < 256) { - if(pointer + 4 > 256) - break; - switch(type) { - case 0: // R5G6B5 - case 3: // A1R5G5B5 - if(!type) - line_buffer[px] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); - else - line_buffer[px] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); - pointer += 2; - file_pos += 2; - break; - case 1: // 24 - default: - line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - pointer += 3; - file_pos += 3; - break; - case 2: // 32 - line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); - pointer += 4; - file_pos += 4; - break; - } - px++; - if(px >= width) { - break; - } - } - if(read_size.value() != 256) - break; - } - render_line({ p.x(), p.y() + py }, px, line_buffer); - px = 0; - py--; - - if(read_size.value() < 256 || py < 0) - break; - } - return true; + File bmpimage; + size_t file_pos = 0; + uint16_t pointer = 0; + int16_t px = 0, py, width, height; + bmp_header_t bmp_header; + uint8_t type = 0; + char buffer[257]; + ui::Color line_buffer[240]; + + auto result = bmpimage.open(file); + if (result.is_valid()) + return false; + + bmpimage.seek(file_pos); + auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header)); + if (!((bmp_header.signature == 0x4D42) && // "BM" Signature + (bmp_header.planes == 1) && // Seems always to be 1 + (bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression + return false; + } + + switch (bmp_header.bpp) { + case 16: + file_pos = 0x36; + memset(buffer, 0, 16); + bmpimage.read(buffer, 16); + if (buffer[1] == 0x7C) + type = 3; // A1R5G5B5 + else + type = 0; // R5G6B5 + break; + case 24: + type = 1; + break; + case 32: + default: + type = 2; + break; + } + + width = bmp_header.width; + height = bmp_header.height; + + file_pos = bmp_header.image_data; + + py = height + 16; + + while (1) { + while (px < width) { + bmpimage.seek(file_pos); + memset(buffer, 0, 257); + read_size = bmpimage.read(buffer, 256); + if (read_size.is_error()) + return false; // Read error + + pointer = 0; + while (pointer < 256) { + if (pointer + 4 > 256) + break; + switch (type) { + case 0: // R5G6B5 + case 3: // A1R5G5B5 + if (!type) + line_buffer[px] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8)); + else + line_buffer[px] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9); + pointer += 2; + file_pos += 2; + break; + case 1: // 24 + default: + line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + pointer += 3; + file_pos += 3; + break; + case 2: // 32 + line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]); + pointer += 4; + file_pos += 4; + break; + } + px++; + if (px >= width) { + break; + } + } + if (read_size.value() != 256) + break; + } + render_line({p.x(), p.y() + py}, px, line_buffer); + px = 0; + py--; + + if (read_size.value() < 256 || py < 0) + break; + } + return true; } void ILI9341::draw_line(const ui::Point start, const ui::Point end, const ui::Color color) { - int x0 = start.x(); - int y0 = start.y(); - int x1 = end.x(); - int y1 = end.y(); - - int dx = std::abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; - - for(;;){ - draw_pixel({static_cast(x0), static_cast(y0)}, color); - if (x0==x1 && y0==y1) break; - e2 = err; - if (e2 >-dx) { err -= dy; x0 += sx; } - if (e2 < dy) { err += dx; y0 += sy; } - } + int x0 = start.x(); + int y0 = start.y(); + int x1 = end.x(); + int y1 = end.y(); + + int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + for (;;) { + draw_pixel({static_cast(x0), static_cast(y0)}, color); + if (x0 == x1 && y0 == y1) break; + e2 = err; + if (e2 > -dx) { + err -= dy; + x0 += sx; + } + if (e2 < dy) { + err += dx; + y0 += sy; + } + } } void ILI9341::fill_circle( - const ui::Point center, - const ui::Dim radius, - const ui::Color foreground, - const ui::Color background -) { - const uint32_t radius2 = radius * radius; - for(int32_t y=-radius; y(colors), - count * sizeof(ui::ColorRGB888) - ); + const ui::Rect r, + ui::ColorRGB888* const colors, + const size_t count) { + /* TODO: Assert that rectangle width x height < count */ + lcd_start_ram_read(r); + io.lcd_read_bytes( + reinterpret_cast(colors), + count * sizeof(ui::ColorRGB888)); } void ILI9341::draw_bitmap( - const ui::Point p, - const ui::Size size, - const uint8_t* const pixels, - const ui::Color foreground, - const ui::Color background -) { - // Not a transparent background - if (ui::Color::magenta().v!=background.v) - { - lcd_start_ram_write(p, size); - - const size_t count = size.width() * size.height(); - for(size_t i=0; i> 3] & (1U << (i & 0x7)); - io.lcd_write_pixel(pixel ? foreground : background); - } - } - else - { - int x = p.x(); - int y = p.y(); - int maxX = x + size.width(); - const size_t count = size.width() * size.height(); - for(size_t i=0; i> 3] & (1U << (i & 0x7)); - if (pixel) { - draw_pixel(ui::Point(x,y), foreground); - } - // Move to the next pixel - x++; - if (x>=maxX){ - x = p.x(); - y++; - } - } - } + const ui::Point p, + const ui::Size size, + const uint8_t* const pixels, + const ui::Color foreground, + const ui::Color background) { + // Not a transparent background + if (ui::Color::magenta().v != background.v) { + lcd_start_ram_write(p, size); + + const size_t count = size.width() * size.height(); + for (size_t i = 0; i < count; i++) { + const auto pixel = pixels[i >> 3] & (1U << (i & 0x7)); + io.lcd_write_pixel(pixel ? foreground : background); + } + } else { + int x = p.x(); + int y = p.y(); + int maxX = x + size.width(); + const size_t count = size.width() * size.height(); + for (size_t i = 0; i < count; i++) { + const auto pixel = pixels[i >> 3] & (1U << (i & 0x7)); + if (pixel) { + draw_pixel(ui::Point(x, y), foreground); + } + // Move to the next pixel + x++; + if (x >= maxX) { + x = p.x(); + y++; + } + } + } } void ILI9341::draw_glyph( - const ui::Point p, - const ui::Glyph& glyph, - const ui::Color foreground, - const ui::Color background -) { - draw_bitmap(p, glyph.size(), glyph.pixels(), foreground, background); + const ui::Point p, + const ui::Glyph& glyph, + const ui::Color foreground, + const ui::Color background) { + draw_bitmap(p, glyph.size(), glyph.pixels(), foreground, background); } void ILI9341::scroll_set_area( - const ui::Coord top_y, - const ui::Coord bottom_y -) { - scroll_state.top_area = top_y; - scroll_state.bottom_area = height() - bottom_y; - scroll_state.height = bottom_y - top_y; - lcd_vertical_scrolling_definition(scroll_state.top_area, scroll_state.height, scroll_state.bottom_area); + const ui::Coord top_y, + const ui::Coord bottom_y) { + scroll_state.top_area = top_y; + scroll_state.bottom_area = height() - bottom_y; + scroll_state.height = bottom_y - top_y; + lcd_vertical_scrolling_definition(scroll_state.top_area, scroll_state.height, scroll_state.bottom_area); } ui::Coord ILI9341::scroll_set_position( - const ui::Coord position -) { - scroll_state.current_position = position % scroll_state.height; - const uint_fast16_t address = scroll_state.top_area + scroll_state.current_position; - lcd_vertical_scrolling_start_address(address); - return address; + const ui::Coord position) { + scroll_state.current_position = position % scroll_state.height; + const uint_fast16_t address = scroll_state.top_area + scroll_state.current_position; + lcd_vertical_scrolling_start_address(address); + return address; } ui::Coord ILI9341::scroll(const int32_t delta) { - return scroll_set_position(scroll_state.current_position + scroll_state.height - delta); + return scroll_set_position(scroll_state.current_position + scroll_state.height - delta); } ui::Coord ILI9341::scroll_area_y(const ui::Coord y) const { - const auto wrapped_y = (scroll_state.current_position + y) % scroll_state.height; - return wrapped_y + scroll_state.top_area; + const auto wrapped_y = (scroll_state.current_position + y) % scroll_state.height; + return wrapped_y + scroll_state.top_area; } void ILI9341::scroll_disable() { - lcd_vertical_scrolling_definition(0, height(), 0); - lcd_vertical_scrolling_start_address(0); + lcd_vertical_scrolling_definition(0, height(), 0); + lcd_vertical_scrolling_start_address(0); } } /* namespace lcd */ diff --git a/firmware/common/lcd_ili9341.hpp b/firmware/common/lcd_ili9341.hpp index d564a5c2b..ce9eb0d02 100644 --- a/firmware/common/lcd_ili9341.hpp +++ b/firmware/common/lcd_ili9341.hpp @@ -32,93 +32,87 @@ namespace lcd { class ILI9341 { -public: - constexpr ILI9341( - ) : scroll_state { 0, 0, height(), 0 } - { - } - - ILI9341(const ILI9341&) = delete; - ILI9341(ILI9341&&) = delete; - void operator=(const ILI9341&) = delete; - - void init(); - void shutdown(); - - void sleep(); - void wake(); - - void fill_rectangle(ui::Rect r, const ui::Color c); - void fill_rectangle_unrolled8(ui::Rect r, const ui::Color c); - void draw_line(const ui::Point start, const ui::Point end, const ui::Color color); - void fill_circle( - const ui::Point center, - const ui::Dim radius, - const ui::Color foreground, - const ui::Color background - ); - - void draw_pixel(const ui::Point p, const ui::Color color); - void drawBMP(const ui::Point p, const uint8_t * bitmap, const bool transparency); - bool drawBMP2(const ui::Point p, const std::string file); - void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer); - void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer); - - template - void draw_pixels( - const ui::Rect r, - const std::array& colors - ) { - draw_pixels(r, colors.data(), colors.size()); - } - - template - void read_pixels( - const ui::Rect r, - std::array& colors - ) { - read_pixels(r, colors.data(), colors.size()); - } - - void draw_bitmap( - const ui::Point p, - const ui::Size size, - const uint8_t* const data, - const ui::Color foreground, - const ui::Color background - ); - - void draw_glyph( - const ui::Point p, - const ui::Glyph& glyph, - const ui::Color foreground, - const ui::Color background - ); - - void scroll_set_area(const ui::Coord top_y, const ui::Coord bottom_y); - ui::Coord scroll_set_position(const ui::Coord position); - ui::Coord scroll(const int32_t delta); - ui::Coord scroll_area_y(const ui::Coord y) const; - void scroll_disable(); - - constexpr ui::Dim width() const { return 240; } - constexpr ui::Dim height() const { return 320; } - constexpr ui::Rect screen_rect() const { return { 0, 0, width(), height() }; } - -private: - struct scroll_t { - ui::Coord top_area; - ui::Coord bottom_area; - ui::Dim height; - ui::Coord current_position; - }; - - scroll_t scroll_state; - - void draw_pixels(const ui::Rect r, const ui::Color* const colors, const size_t count); - void read_pixels(const ui::Rect r, ui::ColorRGB888* const colors, const size_t count); + public: + constexpr ILI9341() + : scroll_state{0, 0, height(), 0} { + } + + ILI9341(const ILI9341&) = delete; + ILI9341(ILI9341&&) = delete; + void operator=(const ILI9341&) = delete; + + void init(); + void shutdown(); + + void sleep(); + void wake(); + + void fill_rectangle(ui::Rect r, const ui::Color c); + void fill_rectangle_unrolled8(ui::Rect r, const ui::Color c); + void draw_line(const ui::Point start, const ui::Point end, const ui::Color color); + void fill_circle( + const ui::Point center, + const ui::Dim radius, + const ui::Color foreground, + const ui::Color background); + + void draw_pixel(const ui::Point p, const ui::Color color); + void drawBMP(const ui::Point p, const uint8_t* bitmap, const bool transparency); + bool drawBMP2(const ui::Point p, const std::string file); + void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer); + void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer); + + template + void draw_pixels( + const ui::Rect r, + const std::array& colors) { + draw_pixels(r, colors.data(), colors.size()); + } + + template + void read_pixels( + const ui::Rect r, + std::array& colors) { + read_pixels(r, colors.data(), colors.size()); + } + + void draw_bitmap( + const ui::Point p, + const ui::Size size, + const uint8_t* const data, + const ui::Color foreground, + const ui::Color background); + + void draw_glyph( + const ui::Point p, + const ui::Glyph& glyph, + const ui::Color foreground, + const ui::Color background); + + void scroll_set_area(const ui::Coord top_y, const ui::Coord bottom_y); + ui::Coord scroll_set_position(const ui::Coord position); + ui::Coord scroll(const int32_t delta); + ui::Coord scroll_area_y(const ui::Coord y) const; + void scroll_disable(); + + constexpr ui::Dim width() const { return 240; } + constexpr ui::Dim height() const { return 320; } + constexpr ui::Rect screen_rect() const { return {0, 0, width(), height()}; } + + private: + struct scroll_t { + ui::Coord top_area; + ui::Coord bottom_area; + ui::Dim height; + ui::Coord current_position; + }; + + scroll_t scroll_state; + + void draw_pixels(const ui::Rect r, const ui::Color* const colors, const size_t count); + void read_pixels(const ui::Rect r, ui::ColorRGB888* const colors, const size_t count); }; } /* namespace lcd */ -#endif/*__LCD_ILI9341_H__*/ +#endif /*__LCD_ILI9341_H__*/ diff --git a/firmware/common/led.hpp b/firmware/common/led.hpp index 26e4b3b8b..ed62dfa27 100644 --- a/firmware/common/led.hpp +++ b/firmware/common/led.hpp @@ -25,34 +25,34 @@ #include "gpio.hpp" struct LED { - constexpr LED(const GPIO gpio) : - _gpio { gpio } { - } - - void setup() const { - _gpio.clear(); - _gpio.output(); - _gpio.configure(); - } - - void on() const { - _gpio.set(); - } - - void off() const { - _gpio.clear(); - } - - void toggle() const { - _gpio.toggle(); - } - - void write(const bool value) const { - _gpio.write(value); - } - -private: - const GPIO _gpio; + constexpr LED(const GPIO gpio) + : _gpio{gpio} { + } + + void setup() const { + _gpio.clear(); + _gpio.output(); + _gpio.configure(); + } + + void on() const { + _gpio.set(); + } + + void off() const { + _gpio.clear(); + } + + void toggle() const { + _gpio.toggle(); + } + + void write(const bool value) const { + _gpio.write(value); + } + + private: + const GPIO _gpio; }; -#endif/*__LED_H__*/ +#endif /*__LED_H__*/ diff --git a/firmware/common/lfsr_random.cpp b/firmware/common/lfsr_random.cpp index ddeb044a1..cf911eba0 100644 --- a/firmware/common/lfsr_random.cpp +++ b/firmware/common/lfsr_random.cpp @@ -21,86 +21,67 @@ #include "lfsr_random.hpp" -static void lfsr_iterate_internal(lfsr_word_t& v) -{ - /* - Generated with lfsr-generator: - http://lfsr-generator.sourceforge.net - ============================================= - config : fibonacci - length : 31 - taps : (31, 18) - shift-amounts : (12, 12, 8) - shift-direction : left - */ - enum { - length = 31, - tap_0 = 31, - tap_1 = 18, - shift_amount_0 = 12, - shift_amount_1 = 12, - shift_amount_2 = 8 - }; +static void lfsr_iterate_internal(lfsr_word_t& v) { + /* + Generated with lfsr-generator: + http://lfsr-generator.sourceforge.net + ============================================= + config : fibonacci + length : 31 + taps : (31, 18) + shift-amounts : (12, 12, 8) + shift-direction : left + */ + enum { + length = 31, + tap_0 = 31, + tap_1 = 18, + shift_amount_0 = 12, + shift_amount_1 = 12, + shift_amount_2 = 8 + }; - const lfsr_word_t zero = 0; - v = ( - ( - v << shift_amount_0 - ) | ( - ( - (v >> (tap_0 - shift_amount_0)) ^ - (v >> (tap_1 - shift_amount_0)) - ) & ( - ~(~zero << shift_amount_0) - ) - ) - ); - v = ( - ( - v << shift_amount_1 - ) | ( - ( - (v >> (tap_0 - shift_amount_1)) ^ - (v >> (tap_1 - shift_amount_1)) - ) & ( - ~(~zero << shift_amount_1) - ) - ) - ); - v = ( - ( - v << shift_amount_2 - ) | ( - ( - (v >> (tap_0 - shift_amount_2)) ^ - (v >> (tap_1 - shift_amount_2)) - ) & ( - ~(~zero << shift_amount_2) - ) - ) - ); + const lfsr_word_t zero = 0; + v = (( + v << shift_amount_0) | + (( + (v >> (tap_0 - shift_amount_0)) ^ + (v >> (tap_1 - shift_amount_0))) & + (~(~zero << shift_amount_0)))); + v = (( + v << shift_amount_1) | + (( + (v >> (tap_0 - shift_amount_1)) ^ + (v >> (tap_1 - shift_amount_1))) & + (~(~zero << shift_amount_1)))); + v = (( + v << shift_amount_2) | + (( + (v >> (tap_0 - shift_amount_2)) ^ + (v >> (tap_1 - shift_amount_2))) & + (~(~zero << shift_amount_2)))); } lfsr_word_t lfsr_iterate(lfsr_word_t v) { - lfsr_iterate_internal(v); - return v; + lfsr_iterate_internal(v); + return v; } void lfsr_fill(lfsr_word_t& v, lfsr_word_t* buffer, size_t word_count) { - while( word_count != 0 ) { - lfsr_iterate_internal(v); - *(buffer++) = v; - word_count--; - } + while (word_count != 0) { + lfsr_iterate_internal(v); + *(buffer++) = v; + word_count--; + } } bool lfsr_compare(lfsr_word_t& v, const lfsr_word_t* buffer, size_t word_count) { - while( word_count != 0 ) { - lfsr_iterate_internal(v); - if( *(buffer++) != v ) { - return false; - } - word_count--; - } - return true; + while (word_count != 0) { + lfsr_iterate_internal(v); + if (*(buffer++) != v) { + return false; + } + word_count--; + } + return true; } diff --git a/firmware/common/lfsr_random.hpp b/firmware/common/lfsr_random.hpp index 0eaadc192..5000892ee 100644 --- a/firmware/common/lfsr_random.hpp +++ b/firmware/common/lfsr_random.hpp @@ -31,4 +31,4 @@ lfsr_word_t lfsr_iterate(lfsr_word_t v); void lfsr_fill(lfsr_word_t& v, lfsr_word_t* buffer, size_t word_count); bool lfsr_compare(lfsr_word_t& v, const lfsr_word_t* buffer, size_t word_count); -#endif/*__LFSR_RANDOM_HPP__*/ +#endif /*__LFSR_RANDOM_HPP__*/ diff --git a/firmware/common/lpc43xx_cpp.hpp b/firmware/common/lpc43xx_cpp.hpp index 129ef3900..d97cd5e0b 100644 --- a/firmware/common/lpc43xx_cpp.hpp +++ b/firmware/common/lpc43xx_cpp.hpp @@ -34,12 +34,14 @@ namespace lpc43xx { namespace m4 { static inline bool flag_saturation() { - return __get_APSR() & (1U << 27); + return __get_APSR() & (1U << 27); } static inline void clear_flag_saturation() { - uint32_t flags = 1; - __asm volatile ("MSR APSR_nzcvqg, %0" : : "r" (flags)); + uint32_t flags = 1; + __asm volatile("MSR APSR_nzcvqg, %0" + : + : "r"(flags)); } } /* namespace m4 */ @@ -60,22 +62,22 @@ namespace m4txevent { #if defined(LPC43XX_M0) inline void enable() { - nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY)); + nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY)); } inline void disable() { - nvicDisableVector(M4CORE_IRQn); + nvicDisableVector(M4CORE_IRQn); } #endif #if defined(LPC43XX_M4) inline void assert_event() { - __SEV(); + __SEV(); } #endif inline void clear() { - LPC_CREG->M4TXEVENT = 0; + LPC_CREG->M4TXEVENT = 0; } } /* namespace m4txevent */ @@ -84,157 +86,137 @@ namespace m0apptxevent { #if defined(LPC43XX_M4) inline void enable() { - nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY)); + nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY)); } inline void disable() { - nvicDisableVector(M0CORE_IRQn); + nvicDisableVector(M0CORE_IRQn); } #endif #if defined(LPC43XX_M0) inline void assert_event() { - __SEV(); + __SEV(); } #endif inline void clear() { - LPC_CREG->M0APPTXEVENT = 0; + LPC_CREG->M0APPTXEVENT = 0; } -} /* namespace */ +} // namespace m0apptxevent } /* namespace creg */ namespace cgu { enum class CLK_SEL : uint8_t { - RTC_32KHZ = 0x00, - IRC = 0x01, - ENET_RX_CLK = 0x02, - ENET_TX_CLK = 0x03, - GP_CLKIN = 0x04, - XTAL = 0x06, - PLL0USB = 0x07, - PLL0AUDIO = 0x08, - PLL1 = 0x09, - IDIVA = 0x0c, - IDIVB = 0x0d, - IDIVC = 0x0e, - IDIVD = 0x0f, - IDIVE = 0x10, + RTC_32KHZ = 0x00, + IRC = 0x01, + ENET_RX_CLK = 0x02, + ENET_TX_CLK = 0x03, + GP_CLKIN = 0x04, + XTAL = 0x06, + PLL0USB = 0x07, + PLL0AUDIO = 0x08, + PLL1 = 0x09, + IDIVA = 0x0c, + IDIVB = 0x0d, + IDIVC = 0x0e, + IDIVD = 0x0f, + IDIVE = 0x10, }; struct IDIV_CTRL { - uint32_t pd; - uint32_t idiv; - uint32_t autoblock; - CLK_SEL clk_sel; - - constexpr operator uint32_t() const { - return - ((pd & 1) << 0) - | ((idiv & 255) << 2) - | ((autoblock & 1) << 11) - | ((toUType(clk_sel) & 0x1f) << 24) - ; - } + uint32_t pd; + uint32_t idiv; + uint32_t autoblock; + CLK_SEL clk_sel; + + constexpr operator uint32_t() const { + return ((pd & 1) << 0) | ((idiv & 255) << 2) | ((autoblock & 1) << 11) | ((toUType(clk_sel) & 0x1f) << 24); + } }; namespace pll0audio { struct CTRL { - uint32_t pd; - uint32_t bypass; - uint32_t directi; - uint32_t directo; - uint32_t clken; - uint32_t frm; - uint32_t autoblock; - uint32_t pllfract_req; - uint32_t sel_ext; - uint32_t mod_pd; - CLK_SEL clk_sel; - - constexpr operator uint32_t() const { - return - ((pd & 1) << 0) - | ((bypass & 1) << 1) - | ((directi & 1) << 2) - | ((directo & 1) << 3) - | ((clken & 1) << 4) - | ((frm & 1) << 6) - | ((autoblock & 1) << 11) - | ((pllfract_req & 1) << 12) - | ((sel_ext & 1) << 13) - | ((mod_pd & 1) << 14) - | ((toUType(clk_sel) & 0x1f) << 24) - ; - } + uint32_t pd; + uint32_t bypass; + uint32_t directi; + uint32_t directo; + uint32_t clken; + uint32_t frm; + uint32_t autoblock; + uint32_t pllfract_req; + uint32_t sel_ext; + uint32_t mod_pd; + CLK_SEL clk_sel; + + constexpr operator uint32_t() const { + return ((pd & 1) << 0) | ((bypass & 1) << 1) | ((directi & 1) << 2) | ((directo & 1) << 3) | ((clken & 1) << 4) | ((frm & 1) << 6) | ((autoblock & 1) << 11) | ((pllfract_req & 1) << 12) | ((sel_ext & 1) << 13) | ((mod_pd & 1) << 14) | ((toUType(clk_sel) & 0x1f) << 24); + } }; struct MDIV { - uint32_t mdec; + uint32_t mdec; - constexpr operator uint32_t() const { - return ((mdec & 0x1ffff) << 0); - } + constexpr operator uint32_t() const { + return ((mdec & 0x1ffff) << 0); + } }; struct NP_DIV { - uint32_t pdec; - uint32_t ndec; - - constexpr operator uint32_t() const { - return - ((pdec & 0x7f) << 0) - | ((ndec & 0x3ff) << 12) - ; - } + uint32_t pdec; + uint32_t ndec; + + constexpr operator uint32_t() const { + return ((pdec & 0x7f) << 0) | ((ndec & 0x3ff) << 12); + } }; struct FRAC { - uint32_t pllfract_ctrl; + uint32_t pllfract_ctrl; - constexpr operator uint32_t() const { - return ((pllfract_ctrl & 0x3fffff) << 0); - } + constexpr operator uint32_t() const { + return ((pllfract_ctrl & 0x3fffff) << 0); + } }; inline void ctrl(const CTRL& value) { - LPC_CGU->PLL0AUDIO_CTRL = value; + LPC_CGU->PLL0AUDIO_CTRL = value; } inline void mdiv(const MDIV& value) { - LPC_CGU->PLL0AUDIO_MDIV = value; + LPC_CGU->PLL0AUDIO_MDIV = value; } inline void np_div(const NP_DIV& value) { - LPC_CGU->PLL0AUDIO_NP_DIV = value; + LPC_CGU->PLL0AUDIO_NP_DIV = value; } inline void frac(const FRAC& value) { - LPC_CGU->PLL0AUDIO_FRAC = value; + LPC_CGU->PLL0AUDIO_FRAC = value; } inline void power_up() { - LPC_CGU->PLL0AUDIO_CTRL &= ~(1U << 0); + LPC_CGU->PLL0AUDIO_CTRL &= ~(1U << 0); } inline void power_down() { - LPC_CGU->PLL0AUDIO_CTRL |= (1U << 0); + LPC_CGU->PLL0AUDIO_CTRL |= (1U << 0); } inline bool is_locked() { - return LPC_CGU->PLL0AUDIO_STAT & (1U << 0); + return LPC_CGU->PLL0AUDIO_STAT & (1U << 0); } inline void clock_enable() { - LPC_CGU->PLL0AUDIO_CTRL |= (1U << 4); + LPC_CGU->PLL0AUDIO_CTRL |= (1U << 4); } inline void clock_disable() { - LPC_CGU->PLL0AUDIO_CTRL &= ~(1U << 4); + LPC_CGU->PLL0AUDIO_CTRL &= ~(1U << 4); } } /* namespace pll0audio */ @@ -242,49 +224,39 @@ inline void clock_disable() { namespace pll1 { struct CTRL { - uint32_t pd; - uint32_t bypass; - uint32_t fbsel; - uint32_t direct; - uint32_t psel; - uint32_t autoblock; - uint32_t nsel; - uint32_t msel; - CLK_SEL clk_sel; - - constexpr operator uint32_t() const { - return - ((pd & 1) << 0) - | ((bypass & 1) << 1) - | ((fbsel & 1) << 6) - | ((direct & 1) << 7) - | ((psel & 3) << 8) - | ((autoblock & 1) << 11) - | ((nsel & 3) << 12) - | ((msel & 0xff) << 16) - | ((toUType(clk_sel) & 0x1f) << 24) - ; - } + uint32_t pd; + uint32_t bypass; + uint32_t fbsel; + uint32_t direct; + uint32_t psel; + uint32_t autoblock; + uint32_t nsel; + uint32_t msel; + CLK_SEL clk_sel; + + constexpr operator uint32_t() const { + return ((pd & 1) << 0) | ((bypass & 1) << 1) | ((fbsel & 1) << 6) | ((direct & 1) << 7) | ((psel & 3) << 8) | ((autoblock & 1) << 11) | ((nsel & 3) << 12) | ((msel & 0xff) << 16) | ((toUType(clk_sel) & 0x1f) << 24); + } }; inline void ctrl(const CTRL& value) { - LPC_CGU->PLL1_CTRL = value; + LPC_CGU->PLL1_CTRL = value; } inline void enable() { - LPC_CGU->PLL1_CTRL &= ~(1U << 0); + LPC_CGU->PLL1_CTRL &= ~(1U << 0); } inline void disable() { - LPC_CGU->PLL1_CTRL |= (1U << 0); + LPC_CGU->PLL1_CTRL |= (1U << 0); } inline void direct() { - LPC_CGU->PLL1_CTRL |= (1U << 7); + LPC_CGU->PLL1_CTRL |= (1U << 7); } inline bool is_locked() { - return LPC_CGU->PLL1_STAT & (1U << 0); + return LPC_CGU->PLL1_STAT & (1U << 0); } } /* namespace pll1 */ @@ -300,90 +272,89 @@ static_assert(offsetof(LPC_CCU1_Type, CLK_ADCHS_STAT) == 0xb04, "CLK_ADCHS_STAT namespace rgu { enum class Reset { - CORE = 0, - PERIPH = 1, - MASTER = 2, - WWDT = 4, - CREG = 5, - BUS = 8, - SCU = 9, - M0_SUB = 12, - M4_RST = 13, - LCD = 16, - USB0 = 17, - USB1 = 18, - DMA = 19, - SDIO = 20, - EMC = 21, - ETHERNET = 22, - FLASHA = 25, - EEPROM = 27, - GPIO = 28, - FLASHB = 29, - TIMER0 = 32, - TIMER1 = 33, - TIMER2 = 34, - TIMER3 = 35, - RITIMER = 36, - SCT = 37, - MOTOCONPWM = 38, - QEI = 39, - ADC0 = 40, - ADC1 = 41, - DAC = 42, - UART0 = 44, - UART1 = 45, - UART2 = 46, - UART3 = 47, - I2C0 = 48, - I2C1 = 49, - SSP0 = 50, - SSP1 = 51, - I2S = 52, - SPIFI = 53, - CAN1 = 54, - CAN0 = 55, - M0APP = 56, - SGPIO = 57, - SPI = 58, - ADCHS = 60, + CORE = 0, + PERIPH = 1, + MASTER = 2, + WWDT = 4, + CREG = 5, + BUS = 8, + SCU = 9, + M0_SUB = 12, + M4_RST = 13, + LCD = 16, + USB0 = 17, + USB1 = 18, + DMA = 19, + SDIO = 20, + EMC = 21, + ETHERNET = 22, + FLASHA = 25, + EEPROM = 27, + GPIO = 28, + FLASHB = 29, + TIMER0 = 32, + TIMER1 = 33, + TIMER2 = 34, + TIMER3 = 35, + RITIMER = 36, + SCT = 37, + MOTOCONPWM = 38, + QEI = 39, + ADC0 = 40, + ADC1 = 41, + DAC = 42, + UART0 = 44, + UART1 = 45, + UART2 = 46, + UART3 = 47, + I2C0 = 48, + I2C1 = 49, + SSP0 = 50, + SSP1 = 51, + I2S = 52, + SPIFI = 53, + CAN1 = 54, + CAN0 = 55, + M0APP = 56, + SGPIO = 57, + SPI = 58, + ADCHS = 60, }; enum class Status { - NotActive = 0b00, - ActivatedByRGUInput = 0b01, - ActivatedBySoftware = 0b11, + NotActive = 0b00, + ActivatedByRGUInput = 0b01, + ActivatedBySoftware = 0b11, }; inline void reset(const Reset reset) { - LPC_RGU->RESET_CTRL[toUType(reset) >> 5] = (1U << (toUType(reset) & 0x1f)); + LPC_RGU->RESET_CTRL[toUType(reset) >> 5] = (1U << (toUType(reset) & 0x1f)); } inline void reset_mask(const uint64_t mask) { - LPC_RGU->RESET_CTRL[0] = mask & 0xffffffffU; - LPC_RGU->RESET_CTRL[1] = mask >> 32; + LPC_RGU->RESET_CTRL[0] = mask & 0xffffffffU; + LPC_RGU->RESET_CTRL[1] = mask >> 32; } inline Status status(const Reset reset) { - return static_cast( - (LPC_RGU->RESET_STATUS[toUType(reset) >> 4] >> ((toUType(reset) & 0xf) * 2)) & 3 - ); + return static_cast( + (LPC_RGU->RESET_STATUS[toUType(reset) >> 4] >> ((toUType(reset) & 0xf) * 2)) & 3); } inline bool active(const Reset reset) { - return (LPC_RGU->RESET_ACTIVE_STATUS[toUType(reset) >> 5] >> (toUType(reset) & 0x1f)) & 1; + return (LPC_RGU->RESET_ACTIVE_STATUS[toUType(reset) >> 5] >> (toUType(reset) & 0x1f)) & 1; } inline uint32_t external_status(const Reset reset) { - return LPC_RGU->RESET_EXT_STAT[toUType(reset)]; + return LPC_RGU->RESET_EXT_STAT[toUType(reset)]; } inline uint64_t operator|(Reset r1, Reset r2) { - return (1ULL << toUType(r1)) | (1ULL << toUType(r2)); + return (1ULL << toUType(r1)) | (1ULL << toUType(r2)); } inline uint64_t operator|(uint64_t m, Reset r) { - return m | (1ULL << toUType(r)); + return m | (1ULL << toUType(r)); } static_assert(offsetof(LPC_RGU_Type, RESET_CTRL[0]) == 0x100, "RESET_CTRL[0] offset wrong"); @@ -397,23 +368,16 @@ static_assert(offsetof(LPC_RGU_Type, RESET_EXT_STAT[60]) == 0x4f0, "RESET_EXT_ST namespace scu { struct SFS { - uint32_t mode; - uint32_t epd; - uint32_t epun; - uint32_t ehs; - uint32_t ezi; - uint32_t zif; - - constexpr operator uint32_t() const { - return - ((mode & 7) << 0) - | ((epd & 1) << 3) - | ((epun & 1) << 4) - | ((ehs & 1) << 5) - | ((ezi & 1) << 6) - | ((zif & 1) << 7) - ; - } + uint32_t mode; + uint32_t epd; + uint32_t epun; + uint32_t ehs; + uint32_t ezi; + uint32_t zif; + + constexpr operator uint32_t() const { + return ((mode & 7) << 0) | ((epd & 1) << 3) | ((epun & 1) << 4) | ((ehs & 1) << 5) | ((ezi & 1) << 6) | ((zif & 1) << 7); + } }; static_assert(offsetof(LPC_SCU_Type, PINTSEL0) == 0xe00, "PINTSEL0 offset wrong"); @@ -455,31 +419,20 @@ static_assert(offsetof(LPC_SDMMC_Type, DATA) == 0x100, "SDMMC DATA offset wrong" namespace spifi { struct CTRL { - uint32_t timeout; - uint32_t cshigh; - uint32_t d_prftch_dis; - uint32_t inten; - uint32_t mode3; - uint32_t prftch_dis; - uint32_t dual; - uint32_t rfclk; - uint32_t fbclk; - uint32_t dmaen; - - constexpr operator uint32_t() const { - return - ((timeout & 0xffff) << 0) - | ((cshigh & 1) << 16) - | ((d_prftch_dis & 1) << 21) - | ((inten & 1) << 22) - | ((mode3 & 1) << 23) - | ((prftch_dis & 1) << 27) - | ((dual & 1) << 28) - | ((rfclk & 1) << 29) - | ((fbclk & 1) << 30) - | ((dmaen & 1) << 31) - ; - } + uint32_t timeout; + uint32_t cshigh; + uint32_t d_prftch_dis; + uint32_t inten; + uint32_t mode3; + uint32_t prftch_dis; + uint32_t dual; + uint32_t rfclk; + uint32_t fbclk; + uint32_t dmaen; + + constexpr operator uint32_t() const { + return ((timeout & 0xffff) << 0) | ((cshigh & 1) << 16) | ((d_prftch_dis & 1) << 21) | ((inten & 1) << 22) | ((mode3 & 1) << 23) | ((prftch_dis & 1) << 27) | ((dual & 1) << 28) | ((rfclk & 1) << 29) | ((fbclk & 1) << 30) | ((dmaen & 1) << 31); + } }; static_assert(offsetof(LPC_SPIFI_Type, STAT) == 0x01c, "SPIFI STAT offset wrong"); @@ -500,59 +453,56 @@ namespace rtc { namespace interrupt { inline void clear_all() { - LPC_RTC->ILR = (1U << 1) | (1U << 0); + LPC_RTC->ILR = (1U << 1) | (1U << 0); } inline void enable_second_inc() { - LPC_RTC->CIIR = (1U << 0); + LPC_RTC->CIIR = (1U << 0); } -} /* namespace */ +} // namespace interrupt #if HAL_USE_RTC struct RTC : public RTCTime { - constexpr RTC( - uint32_t year, - uint32_t month, - uint32_t day, - uint32_t hour, - uint32_t minute, - uint32_t second - ) : RTCTime { - (year << 16) | (month << 8) | (day << 0), - (hour << 16) | (minute << 8) | (second << 0) - } - { - } - - constexpr RTC( - ) : RTCTime { 0, 0 } - { - } - - uint16_t year() const { - return (tv_date >> 16) & 0xfff; - } - - uint8_t month() const { - return (tv_date >> 8) & 0x00f; - } - - uint8_t day() const { - return (tv_date >> 0) & 0x01f; - } - - uint8_t hour() const { - return (tv_time >> 16) & 0x01f; - } - - uint8_t minute() const { - return (tv_time >> 8) & 0x03f; - } - - uint8_t second() const { - return (tv_time >> 0) & 0x03f; - } + constexpr RTC( + uint32_t year, + uint32_t month, + uint32_t day, + uint32_t hour, + uint32_t minute, + uint32_t second) + : RTCTime{ + (year << 16) | (month << 8) | (day << 0), + (hour << 16) | (minute << 8) | (second << 0)} { + } + + constexpr RTC() + : RTCTime{0, 0} { + } + + uint16_t year() const { + return (tv_date >> 16) & 0xfff; + } + + uint8_t month() const { + return (tv_date >> 8) & 0x00f; + } + + uint8_t day() const { + return (tv_date >> 0) & 0x01f; + } + + uint8_t hour() const { + return (tv_time >> 16) & 0x01f; + } + + uint8_t minute() const { + return (tv_time >> 8) & 0x03f; + } + + uint8_t second() const { + return (tv_time >> 0) & 0x03f; + } }; #endif @@ -563,4 +513,4 @@ static_assert(offsetof(LPC_RTC_Type, ASEC) == 0x060, "RTC ASEC offset wrong"); } /* namespace lpc43xx */ -#endif/*__LPC43XX_CPP_H__*/ +#endif /*__LPC43XX_CPP_H__*/ diff --git a/firmware/common/manchester.cpp b/firmware/common/manchester.cpp index bc14c0c1c..9d86f6e96 100644 --- a/firmware/common/manchester.cpp +++ b/firmware/common/manchester.cpp @@ -24,73 +24,72 @@ #include "string_format.hpp" size_t ManchesterBase::symbols_count() const { - return packet.size() / 2; + return packet.size() / 2; } DecodedSymbol ManchesterDecoder::operator[](const size_t index) const { - const size_t encoded_index = index * 2; - if( (encoded_index + 1) < packet.size() ) { - const auto value = packet[encoded_index + sense]; - const auto error = packet[encoded_index + 0] == packet[encoded_index + 1]; - return { value, error }; - } else { - return { 0, 1 }; - } + const size_t encoded_index = index * 2; + if ((encoded_index + 1) < packet.size()) { + const auto value = packet[encoded_index + sense]; + const auto error = packet[encoded_index + 0] == packet[encoded_index + 1]; + return {value, error}; + } else { + return {0, 1}; + } } DecodedSymbol BiphaseMDecoder::operator[](const size_t index) const { - const size_t encoded_index = index * 2; - if( (encoded_index + 1) < packet.size() ) { - const auto value = packet[encoded_index + 0] != packet[encoded_index + 1]; - const uint_fast8_t error = encoded_index ? (packet[encoded_index - 1] == packet[encoded_index + 0]) : 0; - return { value, error }; - } else { - return { 0, 1 }; - } + const size_t encoded_index = index * 2; + if ((encoded_index + 1) < packet.size()) { + const auto value = packet[encoded_index + 0] != packet[encoded_index + 1]; + const uint_fast8_t error = encoded_index ? (packet[encoded_index - 1] == packet[encoded_index + 0]) : 0; + return {value, error}; + } else { + return {0, 1}; + } } FormattedSymbols format_symbols( - const ManchesterBase& decoder -) { - const size_t payload_length_decoded = decoder.symbols_count(); - const size_t payload_length_hex_characters = (payload_length_decoded + 3) / 4; - const size_t payload_length_symbols_rounded = payload_length_hex_characters * 4; + const ManchesterBase& decoder) { + const size_t payload_length_decoded = decoder.symbols_count(); + const size_t payload_length_hex_characters = (payload_length_decoded + 3) / 4; + const size_t payload_length_symbols_rounded = payload_length_hex_characters * 4; - std::string hex_data; - std::string hex_error; - hex_data.reserve(payload_length_hex_characters); - hex_error.reserve(payload_length_hex_characters); + std::string hex_data; + std::string hex_error; + hex_data.reserve(payload_length_hex_characters); + hex_error.reserve(payload_length_hex_characters); - uint_fast8_t data = 0; - uint_fast8_t error = 0; - for(size_t i=0; i> 3] << (c & 7)) & 0x80) { - *(dest++) = part; - *(dest++) = ~part; - } else { - *(dest++) = ~part; - *(dest++) = part; - } - } +void manchester_encode(uint8_t* dest, uint8_t* src, const size_t length, const size_t sense) { + uint8_t part = sense ? 0 : 0xFF; + + for (size_t c = 0; c < length; c++) { + if ((src[c >> 3] << (c & 7)) & 0x80) { + *(dest++) = part; + *(dest++) = ~part; + } else { + *(dest++) = ~part; + *(dest++) = part; + } + } } diff --git a/firmware/common/manchester.hpp b/firmware/common/manchester.hpp index 91bd1f192..f95318acb 100644 --- a/firmware/common/manchester.hpp +++ b/firmware/common/manchester.hpp @@ -30,57 +30,55 @@ #include "baseband_packet.hpp" struct DecodedSymbol { - uint_fast8_t value; - uint_fast8_t error; + uint_fast8_t value; + uint_fast8_t error; }; class ManchesterBase { -public: - constexpr ManchesterBase( - const baseband::Packet& packet, - const size_t sense = 0 - ) : packet { packet }, - sense { sense } - { - } - - virtual DecodedSymbol operator[](const size_t index) const = 0; - - virtual size_t symbols_count() const; - - virtual ~ManchesterBase() { }; - -protected: - const baseband::Packet& packet; - const size_t sense; + public: + constexpr ManchesterBase( + const baseband::Packet& packet, + const size_t sense = 0) + : packet{packet}, + sense{sense} { + } + + virtual DecodedSymbol operator[](const size_t index) const = 0; + + virtual size_t symbols_count() const; + + virtual ~ManchesterBase(){}; + + protected: + const baseband::Packet& packet; + const size_t sense; }; class ManchesterDecoder : public ManchesterBase { -public: - using ManchesterBase::ManchesterBase; - DecodedSymbol operator[](const size_t index) const; + public: + using ManchesterBase::ManchesterBase; + DecodedSymbol operator[](const size_t index) const; }; class BiphaseMDecoder : public ManchesterBase { -public: - using ManchesterBase::ManchesterBase; - DecodedSymbol operator[](const size_t index) const; + public: + using ManchesterBase::ManchesterBase; + DecodedSymbol operator[](const size_t index) const; }; -template +template T operator|(const T& l, const DecodedSymbol& r) { - return l | r.value; + return l | r.value; } struct FormattedSymbols { - const std::string data; - const std::string errors; + const std::string data; + const std::string errors; }; FormattedSymbols format_symbols( - const ManchesterBase& decoder -); + const ManchesterBase& decoder); -void manchester_encode(uint8_t * dest, uint8_t * src, const size_t length, const size_t sense = 0); +void manchester_encode(uint8_t* dest, uint8_t* src, const size_t length, const size_t sense = 0); -#endif/*__MANCHESTER_H__*/ +#endif /*__MANCHESTER_H__*/ diff --git a/firmware/common/memory_map.hpp b/firmware/common/memory_map.hpp index d8624d746..afce53694 100644 --- a/firmware/common/memory_map.hpp +++ b/firmware/common/memory_map.hpp @@ -34,55 +34,54 @@ namespace portapack { namespace memory { struct region_t { -public: - constexpr region_t( - const uint32_t base, - const size_t size - ) : base_ { base }, - size_ { size } { - - } - - constexpr uint32_t base() const { - return base_; - } - - constexpr uint32_t end() const { - return base_ + size_; - } - - constexpr size_t size() const { - return size_; - } - -private: - const uint32_t base_; - const size_t size_; + public: + constexpr region_t( + const uint32_t base, + const size_t size) + : base_{base}, + size_{size} { + } + + constexpr uint32_t base() const { + return base_; + } + + constexpr uint32_t end() const { + return base_ + size_; + } + + constexpr size_t size() const { + return size_; + } + + private: + const uint32_t base_; + const size_t size_; }; namespace map { -constexpr region_t local_sram_0 { 0x10000000, 96_KiB }; -constexpr region_t local_sram_1 { 0x10080000, 40_KiB }; +constexpr region_t local_sram_0{0x10000000, 96_KiB}; +constexpr region_t local_sram_1{0x10080000, 40_KiB}; -constexpr region_t ahb_ram_0 { 0x20000000, 32_KiB }; -constexpr region_t ahb_ram_1 { 0x20008000, 16_KiB }; -constexpr region_t ahb_ram_2 { 0x2000c000, 16_KiB }; +constexpr region_t ahb_ram_0{0x20000000, 32_KiB}; +constexpr region_t ahb_ram_1{0x20008000, 16_KiB}; +constexpr region_t ahb_ram_2{0x2000c000, 16_KiB}; -constexpr region_t backup_ram { LPC_BACKUP_REG_BASE, 256 }; +constexpr region_t backup_ram{LPC_BACKUP_REG_BASE, 256}; -constexpr region_t spifi_uncached { LPC_SPIFI_DATA_BASE, 1_MiB }; -constexpr region_t spifi_cached { LPC_SPIFI_DATA_CACHED_BASE, spifi_uncached.size() }; +constexpr region_t spifi_uncached{LPC_SPIFI_DATA_BASE, 1_MiB}; +constexpr region_t spifi_cached{LPC_SPIFI_DATA_CACHED_BASE, spifi_uncached.size()}; ///////////////////////////////// -constexpr region_t m4_code { local_sram_1.base(), 32_KiB }; -constexpr region_t shared_memory { m4_code.end(), 8_KiB }; +constexpr region_t m4_code{local_sram_1.base(), 32_KiB}; +constexpr region_t shared_memory{m4_code.end(), 8_KiB}; -constexpr region_t m4_code_hackrf = local_sram_0; +constexpr region_t m4_code_hackrf = local_sram_0; } /* namespace map */ } /* namespace memory */ } /* namespace portapack */ -#endif/*__MEMORY_MAP_H__*/ +#endif /*__MEMORY_MAP_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 4b71f5545..9a378b149 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -49,1136 +49,1071 @@ #include "ch.h" class Message { -public: - static constexpr size_t MAX_SIZE = 512; - - enum class ID : uint32_t { - /* Assign consecutive IDs. IDs are used to index array. */ - RSSIStatistics = 0, - BasebandStatistics = 1, - ChannelStatistics = 2, - DisplayFrameSync = 3, - AudioStatistics = 4, - Shutdown = 5, - TPMSPacket = 6, - ACARSPacket = 7, - AISPacket = 8, - ERTPacket = 9, - SondePacket = 10, - UpdateSpectrum = 11, - NBFMConfigure = 12, - WFMConfigure = 13, - AMConfigure = 14, - ChannelSpectrumConfig = 15, - SpectrumStreamingConfig = 16, - DisplaySleep = 17, - CaptureConfig = 18, - CaptureThreadDone = 19, - ReplayConfig = 20, - ReplayThreadDone = 21, - AFSKRxConfigure = 22, - StatusRefresh = 23, - SamplerateConfig = 24, - BTLERxConfigure = 25, - NRFRxConfigure = 26, - TXProgress = 27, - Retune = 28, - TonesConfigure = 29, - AFSKTxConfigure = 30, - PitchRSSIConfigure = 31, - OOKConfigure = 32, - RDSConfigure = 33, - AudioTXConfig = 34, - POCSAGConfigure = 35, - DTMFTXConfig = 36, - ADSBConfigure = 37, - JammerConfigure = 38, - WidebandSpectrumConfig = 39, - FSKConfigure = 40, - SSTVConfigure = 41, - SigGenConfig = 42, - SigGenTone = 43, - POCSAGPacket = 44, - ADSBFrame = 45, - AFSKData = 46, - TestAppPacket = 47, - RequestSignal = 48, - FIFOData = 49, - AudioLevelReport = 50, - CodedSquelch = 51, - AudioSpectrum = 52, - APRSPacket = 53, - APRSRxConfigure = 54, - SpectrumPainterBufferRequestConfigure = 55, - SpectrumPainterBufferResponseConfigure = 56, - MAX - }; - - constexpr Message( - ID id - ) : id { id } - { - } - - const ID id; + public: + static constexpr size_t MAX_SIZE = 512; + + enum class ID : uint32_t { + /* Assign consecutive IDs. IDs are used to index array. */ + RSSIStatistics = 0, + BasebandStatistics = 1, + ChannelStatistics = 2, + DisplayFrameSync = 3, + AudioStatistics = 4, + Shutdown = 5, + TPMSPacket = 6, + ACARSPacket = 7, + AISPacket = 8, + ERTPacket = 9, + SondePacket = 10, + UpdateSpectrum = 11, + NBFMConfigure = 12, + WFMConfigure = 13, + AMConfigure = 14, + ChannelSpectrumConfig = 15, + SpectrumStreamingConfig = 16, + DisplaySleep = 17, + CaptureConfig = 18, + CaptureThreadDone = 19, + ReplayConfig = 20, + ReplayThreadDone = 21, + AFSKRxConfigure = 22, + StatusRefresh = 23, + SamplerateConfig = 24, + BTLERxConfigure = 25, + NRFRxConfigure = 26, + TXProgress = 27, + Retune = 28, + TonesConfigure = 29, + AFSKTxConfigure = 30, + PitchRSSIConfigure = 31, + OOKConfigure = 32, + RDSConfigure = 33, + AudioTXConfig = 34, + POCSAGConfigure = 35, + DTMFTXConfig = 36, + ADSBConfigure = 37, + JammerConfigure = 38, + WidebandSpectrumConfig = 39, + FSKConfigure = 40, + SSTVConfigure = 41, + SigGenConfig = 42, + SigGenTone = 43, + POCSAGPacket = 44, + ADSBFrame = 45, + AFSKData = 46, + TestAppPacket = 47, + RequestSignal = 48, + FIFOData = 49, + AudioLevelReport = 50, + CodedSquelch = 51, + AudioSpectrum = 52, + APRSPacket = 53, + APRSRxConfigure = 54, + SpectrumPainterBufferRequestConfigure = 55, + SpectrumPainterBufferResponseConfigure = 56, + MAX + }; + + constexpr Message( + ID id) + : id{id} { + } + + const ID id; }; struct RSSIStatistics { - uint32_t accumulator { 0 }; - uint32_t min { 0 }; - uint32_t max { 0 }; - uint32_t count { 0 }; + uint32_t accumulator{0}; + uint32_t min{0}; + uint32_t max{0}; + uint32_t count{0}; }; class RSSIStatisticsMessage : public Message { -public: - constexpr RSSIStatisticsMessage( - const RSSIStatistics& statistics - ) : Message { ID::RSSIStatistics }, - statistics { statistics } - { - } + public: + constexpr RSSIStatisticsMessage( + const RSSIStatistics& statistics) + : Message{ID::RSSIStatistics}, + statistics{statistics} { + } - RSSIStatistics statistics; + RSSIStatistics statistics; }; struct BasebandStatistics { - uint32_t idle_ticks { 0 }; - uint32_t main_ticks { 0 }; - uint32_t rssi_ticks { 0 }; - uint32_t baseband_ticks { 0 }; - bool saturation { false }; + uint32_t idle_ticks{0}; + uint32_t main_ticks{0}; + uint32_t rssi_ticks{0}; + uint32_t baseband_ticks{0}; + bool saturation{false}; }; class BasebandStatisticsMessage : public Message { -public: - constexpr BasebandStatisticsMessage( - const BasebandStatistics& statistics - ) : Message { ID::BasebandStatistics }, - statistics { statistics } - { - } + public: + constexpr BasebandStatisticsMessage( + const BasebandStatistics& statistics) + : Message{ID::BasebandStatistics}, + statistics{statistics} { + } - BasebandStatistics statistics; + BasebandStatistics statistics; }; struct ChannelStatistics { - int32_t max_db; - size_t count; - - constexpr ChannelStatistics( - int32_t max_db = -120, - size_t count = 0 - ) : max_db { max_db }, - count { count } - { - } + int32_t max_db; + size_t count; + + constexpr ChannelStatistics( + int32_t max_db = -120, + size_t count = 0) + : max_db{max_db}, + count{count} { + } }; class ChannelStatisticsMessage : public Message { -public: - constexpr ChannelStatisticsMessage( - const ChannelStatistics& statistics - ) : Message { ID::ChannelStatistics }, - statistics { statistics } - { - } + public: + constexpr ChannelStatisticsMessage( + const ChannelStatistics& statistics) + : Message{ID::ChannelStatistics}, + statistics{statistics} { + } - ChannelStatistics statistics; + ChannelStatistics statistics; }; class DisplayFrameSyncMessage : public Message { -public: - constexpr DisplayFrameSyncMessage( - ) : Message { ID::DisplayFrameSync } - { - } + public: + constexpr DisplayFrameSyncMessage() + : Message{ID::DisplayFrameSync} { + } }; struct AudioStatistics { - int32_t rms_db; - int32_t max_db; - size_t count; - - constexpr AudioStatistics( - ) : rms_db { -120 }, - max_db { -120 }, - count { 0 } - { - } - - constexpr AudioStatistics( - int32_t rms_db, - int32_t max_db, - size_t count - ) : rms_db { rms_db }, - max_db { max_db }, - count { count } - { - } + int32_t rms_db; + int32_t max_db; + size_t count; + + constexpr AudioStatistics() + : rms_db{-120}, + max_db{-120}, + count{0} { + } + + constexpr AudioStatistics( + int32_t rms_db, + int32_t max_db, + size_t count) + : rms_db{rms_db}, + max_db{max_db}, + count{count} { + } }; class DisplaySleepMessage : public Message { -public: - constexpr DisplaySleepMessage( - ) : Message { ID::DisplaySleep } - { - } + public: + constexpr DisplaySleepMessage() + : Message{ID::DisplaySleep} { + } }; class StatusRefreshMessage : public Message { -public: - constexpr StatusRefreshMessage( - ) : Message { ID::StatusRefresh } - { - } + public: + constexpr StatusRefreshMessage() + : Message{ID::StatusRefresh} { + } }; class AudioStatisticsMessage : public Message { -public: - constexpr AudioStatisticsMessage( - const AudioStatistics& statistics - ) : Message { ID::AudioStatistics }, - statistics { statistics } - { - } + public: + constexpr AudioStatisticsMessage( + const AudioStatistics& statistics) + : Message{ID::AudioStatistics}, + statistics{statistics} { + } - AudioStatistics statistics; + AudioStatistics statistics; }; class SpectrumStreamingConfigMessage : public Message { -public: - enum class Mode : uint32_t { - Stopped = 0, - Running = 1, - }; - - constexpr SpectrumStreamingConfigMessage( - Mode mode - ) : Message { ID::SpectrumStreamingConfig }, - mode { mode } - { - } + public: + enum class Mode : uint32_t { + Stopped = 0, + Running = 1, + }; + + constexpr SpectrumStreamingConfigMessage( + Mode mode) + : Message{ID::SpectrumStreamingConfig}, + mode{mode} { + } - Mode mode { Mode::Stopped }; + Mode mode{Mode::Stopped}; }; class WidebandSpectrumConfigMessage : public Message { -public: - constexpr WidebandSpectrumConfigMessage ( - size_t sampling_rate, - size_t trigger - ) : Message { ID::WidebandSpectrumConfig }, - sampling_rate { sampling_rate }, - trigger { trigger } - { - } + public: + constexpr WidebandSpectrumConfigMessage( + size_t sampling_rate, + size_t trigger) + : Message{ID::WidebandSpectrumConfig}, + sampling_rate{sampling_rate}, + trigger{trigger} { + } - size_t sampling_rate { 0 }; - size_t trigger { 0 }; + size_t sampling_rate{0}; + size_t trigger{0}; }; struct AudioSpectrum { - std::array db { { 0 } }; - //uint32_t sampling_rate { 0 }; + std::array db{{0}}; + // uint32_t sampling_rate { 0 }; }; class AudioSpectrumMessage : public Message { -public: - constexpr AudioSpectrumMessage( - AudioSpectrum* data - ) : Message { ID::AudioSpectrum }, - data { data } - { - } + public: + constexpr AudioSpectrumMessage( + AudioSpectrum* data) + : Message{ID::AudioSpectrum}, + data{data} { + } - AudioSpectrum* data { nullptr }; + AudioSpectrum* data{nullptr}; }; struct ChannelSpectrum { - std::array db { { 0 } }; - uint32_t sampling_rate { 0 }; - int32_t channel_filter_low_frequency { 0 }; - int32_t channel_filter_high_frequency { 0 }; - int32_t channel_filter_transition { 0 }; + std::array db{{0}}; + uint32_t sampling_rate{0}; + int32_t channel_filter_low_frequency{0}; + int32_t channel_filter_high_frequency{0}; + int32_t channel_filter_transition{0}; }; using ChannelSpectrumFIFO = FIFO; class ChannelSpectrumConfigMessage : public Message { -public: - static constexpr size_t fifo_k = 2; + public: + static constexpr size_t fifo_k = 2; - constexpr ChannelSpectrumConfigMessage( - ChannelSpectrumFIFO* fifo - ) : Message { ID::ChannelSpectrumConfig }, - fifo { fifo } - { - } + constexpr ChannelSpectrumConfigMessage( + ChannelSpectrumFIFO* fifo) + : Message{ID::ChannelSpectrumConfig}, + fifo{fifo} { + } - ChannelSpectrumFIFO* fifo { nullptr }; + ChannelSpectrumFIFO* fifo{nullptr}; }; class AISPacketMessage : public Message { -public: - constexpr AISPacketMessage( - const baseband::Packet& packet - ) : Message { ID::AISPacket }, - packet { packet } - { - } + public: + constexpr AISPacketMessage( + const baseband::Packet& packet) + : Message{ID::AISPacket}, + packet{packet} { + } - baseband::Packet packet; + baseband::Packet packet; }; class TPMSPacketMessage : public Message { -public: - constexpr TPMSPacketMessage( - const tpms::SignalType signal_type, - const baseband::Packet& packet - ) : Message { ID::TPMSPacket }, - signal_type { signal_type }, - packet { packet } - { - } + public: + constexpr TPMSPacketMessage( + const tpms::SignalType signal_type, + const baseband::Packet& packet) + : Message{ID::TPMSPacket}, + signal_type{signal_type}, + packet{packet} { + } - tpms::SignalType signal_type; - baseband::Packet packet; + tpms::SignalType signal_type; + baseband::Packet packet; }; class POCSAGPacketMessage : public Message { -public: - constexpr POCSAGPacketMessage( - const pocsag::POCSAGPacket& packet - ) : Message { ID::POCSAGPacket }, - packet { packet } - { - } + public: + constexpr POCSAGPacketMessage( + const pocsag::POCSAGPacket& packet) + : Message{ID::POCSAGPacket}, + packet{packet} { + } - pocsag::POCSAGPacket packet; + pocsag::POCSAGPacket packet; }; class ACARSPacketMessage : public Message { -public: - constexpr ACARSPacketMessage( - const baseband::Packet& packet - ) : Message { ID::ACARSPacket }, - packet { packet } - { - } + public: + constexpr ACARSPacketMessage( + const baseband::Packet& packet) + : Message{ID::ACARSPacket}, + packet{packet} { + } - baseband::Packet packet; + baseband::Packet packet; }; class ADSBFrameMessage : public Message { -public: - constexpr ADSBFrameMessage( - const adsb::ADSBFrame& frame, - const uint32_t amp - ) : Message { ID::ADSBFrame }, - frame { frame }, - amp(amp) - { - } + public: + constexpr ADSBFrameMessage( + const adsb::ADSBFrame& frame, + const uint32_t amp) + : Message{ID::ADSBFrame}, + frame{frame}, + amp(amp) { + } - adsb::ADSBFrame frame; - uint32_t amp; + adsb::ADSBFrame frame; + uint32_t amp; }; class AFSKDataMessage : public Message { -public: - constexpr AFSKDataMessage( - const bool is_data, - const uint32_t value - ) : Message { ID::AFSKData }, - is_data { is_data }, - value { value } - { - } + public: + constexpr AFSKDataMessage( + const bool is_data, + const uint32_t value) + : Message{ID::AFSKData}, + is_data{is_data}, + value{value} { + } - bool is_data; - uint32_t value; + bool is_data; + uint32_t value; }; class CodedSquelchMessage : public Message { -public: - constexpr CodedSquelchMessage( - const uint32_t value - ) : Message { ID::CodedSquelch }, - value { value } - { - } + public: + constexpr CodedSquelchMessage( + const uint32_t value) + : Message{ID::CodedSquelch}, + value{value} { + } - uint32_t value; + uint32_t value; }; class ShutdownMessage : public Message { -public: - constexpr ShutdownMessage( - ) : Message { ID::Shutdown } - { - } + public: + constexpr ShutdownMessage() + : Message{ID::Shutdown} { + } }; class ERTPacketMessage : public Message { -public: - constexpr ERTPacketMessage( - const ert::Packet::Type type, - const baseband::Packet& packet - ) : Message { ID::ERTPacket }, - type { type }, - packet { packet } - { - } + public: + constexpr ERTPacketMessage( + const ert::Packet::Type type, + const baseband::Packet& packet) + : Message{ID::ERTPacket}, + type{type}, + packet{packet} { + } - ert::Packet::Type type; + ert::Packet::Type type; - baseband::Packet packet; + baseband::Packet packet; }; class SondePacketMessage : public Message { -public: - constexpr SondePacketMessage( - const sonde::Packet::Type type, - const baseband::Packet& packet - ) : Message { ID::SondePacket }, - type { type }, - packet { packet } - { - } + public: + constexpr SondePacketMessage( + const sonde::Packet::Type type, + const baseband::Packet& packet) + : Message{ID::SondePacket}, + type{type}, + packet{packet} { + } - sonde::Packet::Type type; + sonde::Packet::Type type; - baseband::Packet packet; + baseband::Packet packet; }; class TestAppPacketMessage : public Message { -public: - constexpr TestAppPacketMessage( - const baseband::Packet& packet - ) : Message { ID::TestAppPacket }, - packet { packet } - { - } + public: + constexpr TestAppPacketMessage( + const baseband::Packet& packet) + : Message{ID::TestAppPacket}, + packet{packet} { + } - baseband::Packet packet; + baseband::Packet packet; }; class UpdateSpectrumMessage : public Message { -public: - constexpr UpdateSpectrumMessage( - ) : Message { ID::UpdateSpectrum } - { - } + public: + constexpr UpdateSpectrumMessage() + : Message{ID::UpdateSpectrum} { + } }; class NBFMConfigureMessage : public Message { -public: - constexpr NBFMConfigureMessage( - const fir_taps_real<24> decim_0_filter, - const fir_taps_real<32> decim_1_filter, - const fir_taps_real<32> channel_filter, - const size_t channel_decimation, - const size_t deviation, - const iir_biquad_config_t audio_hpf_config, - const iir_biquad_config_t audio_deemph_config, - const uint8_t squelch_level - ) : Message { ID::NBFMConfigure }, - decim_0_filter(decim_0_filter), - decim_1_filter(decim_1_filter), - channel_filter(channel_filter), - channel_decimation { channel_decimation }, - deviation { deviation }, - audio_hpf_config(audio_hpf_config), - audio_deemph_config(audio_deemph_config), - squelch_level(squelch_level) - { - } - - const fir_taps_real<24> decim_0_filter; - const fir_taps_real<32> decim_1_filter; - const fir_taps_real<32> channel_filter; - const size_t channel_decimation; - const size_t deviation; - const iir_biquad_config_t audio_hpf_config; - const iir_biquad_config_t audio_deemph_config; - const uint8_t squelch_level; + public: + constexpr NBFMConfigureMessage( + const fir_taps_real<24> decim_0_filter, + const fir_taps_real<32> decim_1_filter, + const fir_taps_real<32> channel_filter, + const size_t channel_decimation, + const size_t deviation, + const iir_biquad_config_t audio_hpf_config, + const iir_biquad_config_t audio_deemph_config, + const uint8_t squelch_level) + : Message{ID::NBFMConfigure}, + decim_0_filter(decim_0_filter), + decim_1_filter(decim_1_filter), + channel_filter(channel_filter), + channel_decimation{channel_decimation}, + deviation{deviation}, + audio_hpf_config(audio_hpf_config), + audio_deemph_config(audio_deemph_config), + squelch_level(squelch_level) { + } + + const fir_taps_real<24> decim_0_filter; + const fir_taps_real<32> decim_1_filter; + const fir_taps_real<32> channel_filter; + const size_t channel_decimation; + const size_t deviation; + const iir_biquad_config_t audio_hpf_config; + const iir_biquad_config_t audio_deemph_config; + const uint8_t squelch_level; }; class WFMConfigureMessage : public Message { -public: - constexpr WFMConfigureMessage( - const fir_taps_real<24> decim_0_filter, - const fir_taps_real<16> decim_1_filter, - const fir_taps_real<64> audio_filter, - const size_t deviation, - const iir_biquad_config_t audio_hpf_config, - const iir_biquad_config_t audio_deemph_config - ) : Message { ID::WFMConfigure }, - decim_0_filter(decim_0_filter), - decim_1_filter(decim_1_filter), - audio_filter(audio_filter), - deviation { deviation }, - audio_hpf_config(audio_hpf_config), - audio_deemph_config(audio_deemph_config) - { - } - - const fir_taps_real<24> decim_0_filter; - const fir_taps_real<16> decim_1_filter; - const fir_taps_real<64> audio_filter; - const size_t deviation; - const iir_biquad_config_t audio_hpf_config; - const iir_biquad_config_t audio_deemph_config; + public: + constexpr WFMConfigureMessage( + const fir_taps_real<24> decim_0_filter, + const fir_taps_real<16> decim_1_filter, + const fir_taps_real<64> audio_filter, + const size_t deviation, + const iir_biquad_config_t audio_hpf_config, + const iir_biquad_config_t audio_deemph_config) + : Message{ID::WFMConfigure}, + decim_0_filter(decim_0_filter), + decim_1_filter(decim_1_filter), + audio_filter(audio_filter), + deviation{deviation}, + audio_hpf_config(audio_hpf_config), + audio_deemph_config(audio_deemph_config) { + } + + const fir_taps_real<24> decim_0_filter; + const fir_taps_real<16> decim_1_filter; + const fir_taps_real<64> audio_filter; + const size_t deviation; + const iir_biquad_config_t audio_hpf_config; + const iir_biquad_config_t audio_deemph_config; }; class AMConfigureMessage : public Message { -public: - enum class Modulation : int32_t { - DSB = 0, - SSB = 1, - }; - - constexpr AMConfigureMessage( - const fir_taps_real<24> decim_0_filter, - const fir_taps_real<32> decim_1_filter, - const fir_taps_real<32> decim_2_filter, - const fir_taps_complex<64> channel_filter, - const Modulation modulation, - const iir_biquad_config_t audio_hpf_config - ) : Message { ID::AMConfigure }, - decim_0_filter(decim_0_filter), - decim_1_filter(decim_1_filter), - decim_2_filter(decim_2_filter), - channel_filter(channel_filter), - modulation { modulation }, - audio_hpf_config(audio_hpf_config) - { - } - - const fir_taps_real<24> decim_0_filter; - const fir_taps_real<32> decim_1_filter; - const fir_taps_real<32> decim_2_filter; - const fir_taps_complex<64> channel_filter; - const Modulation modulation; - const iir_biquad_config_t audio_hpf_config; + public: + enum class Modulation : int32_t { + DSB = 0, + SSB = 1, + }; + + constexpr AMConfigureMessage( + const fir_taps_real<24> decim_0_filter, + const fir_taps_real<32> decim_1_filter, + const fir_taps_real<32> decim_2_filter, + const fir_taps_complex<64> channel_filter, + const Modulation modulation, + const iir_biquad_config_t audio_hpf_config) + : Message{ID::AMConfigure}, + decim_0_filter(decim_0_filter), + decim_1_filter(decim_1_filter), + decim_2_filter(decim_2_filter), + channel_filter(channel_filter), + modulation{modulation}, + audio_hpf_config(audio_hpf_config) { + } + + const fir_taps_real<24> decim_0_filter; + const fir_taps_real<32> decim_1_filter; + const fir_taps_real<32> decim_2_filter; + const fir_taps_complex<64> channel_filter; + const Modulation modulation; + const iir_biquad_config_t audio_hpf_config; }; // TODO: Put this somewhere else, or at least the implementation part. class StreamBuffer { - uint8_t* data_; - size_t used_; - size_t capacity_; - -public: - constexpr StreamBuffer( - void* const data = nullptr, - const size_t capacity = 0 - ) : data_ { static_cast(data) }, - used_ { 0 }, - capacity_ { capacity } - { - } - - size_t write(const void* p, const size_t count) { - const auto copy_size = std::min(capacity_ - used_, count); - memcpy(&data_[used_], p, copy_size); - used_ += copy_size; - return copy_size; - } - - size_t read(void* p, const size_t count) { - const auto copy_size = std::min(used_, count); - memcpy(p, &data_[capacity_ - used_], copy_size); - used_ -= copy_size; - return copy_size; - } - - bool is_full() const { - return used_ >= capacity_; - } - - bool is_empty() const { - return used_ == 0; - } - - void* data() const { - return data_; - } - - size_t size() const { - return used_; - } - - size_t capacity() const { - return capacity_; - } - - void set_size(const size_t value) { - used_ = value; - } - - void empty() { - used_ = 0; - } + uint8_t* data_; + size_t used_; + size_t capacity_; + + public: + constexpr StreamBuffer( + void* const data = nullptr, + const size_t capacity = 0) + : data_{static_cast(data)}, + used_{0}, + capacity_{capacity} { + } + + size_t write(const void* p, const size_t count) { + const auto copy_size = std::min(capacity_ - used_, count); + memcpy(&data_[used_], p, copy_size); + used_ += copy_size; + return copy_size; + } + + size_t read(void* p, const size_t count) { + const auto copy_size = std::min(used_, count); + memcpy(p, &data_[capacity_ - used_], copy_size); + used_ -= copy_size; + return copy_size; + } + + bool is_full() const { + return used_ >= capacity_; + } + + bool is_empty() const { + return used_ == 0; + } + + void* data() const { + return data_; + } + + size_t size() const { + return used_; + } + + size_t capacity() const { + return capacity_; + } + + void set_size(const size_t value) { + used_ = value; + } + + void empty() { + used_ = 0; + } }; struct CaptureConfig { - const size_t write_size; - const size_t buffer_count; - uint64_t baseband_bytes_received; - uint64_t baseband_bytes_dropped; - FIFO* fifo_buffers_empty; - FIFO* fifo_buffers_full; - - constexpr CaptureConfig( - const size_t write_size, - const size_t buffer_count - ) : write_size { write_size }, - buffer_count { buffer_count }, - baseband_bytes_received { 0 }, - baseband_bytes_dropped { 0 }, - fifo_buffers_empty { nullptr }, - fifo_buffers_full { nullptr } - { - } - - size_t dropped_percent() const { - if( baseband_bytes_dropped == 0 ) { - return 0; - } else { - const size_t percent = baseband_bytes_dropped * 100U / baseband_bytes_received; - return std::max(1U, percent); - } - } + const size_t write_size; + const size_t buffer_count; + uint64_t baseband_bytes_received; + uint64_t baseband_bytes_dropped; + FIFO* fifo_buffers_empty; + FIFO* fifo_buffers_full; + + constexpr CaptureConfig( + const size_t write_size, + const size_t buffer_count) + : write_size{write_size}, + buffer_count{buffer_count}, + baseband_bytes_received{0}, + baseband_bytes_dropped{0}, + fifo_buffers_empty{nullptr}, + fifo_buffers_full{nullptr} { + } + + size_t dropped_percent() const { + if (baseband_bytes_dropped == 0) { + return 0; + } else { + const size_t percent = baseband_bytes_dropped * 100U / baseband_bytes_received; + return std::max(1U, percent); + } + } }; class CaptureConfigMessage : public Message { -public: - constexpr CaptureConfigMessage( - CaptureConfig* const config - ) : Message { ID::CaptureConfig }, - config { config } - { - } + public: + constexpr CaptureConfigMessage( + CaptureConfig* const config) + : Message{ID::CaptureConfig}, + config{config} { + } - CaptureConfig* const config; + CaptureConfig* const config; }; struct ReplayConfig { - const size_t read_size; - const size_t buffer_count; - uint64_t baseband_bytes_received; - FIFO* fifo_buffers_empty; - FIFO* fifo_buffers_full; - - constexpr ReplayConfig( - const size_t read_size, - const size_t buffer_count - ) : read_size { read_size }, - buffer_count { buffer_count }, - baseband_bytes_received { 0 }, - fifo_buffers_empty { nullptr }, - fifo_buffers_full { nullptr } - { - } + const size_t read_size; + const size_t buffer_count; + uint64_t baseband_bytes_received; + FIFO* fifo_buffers_empty; + FIFO* fifo_buffers_full; + + constexpr ReplayConfig( + const size_t read_size, + const size_t buffer_count) + : read_size{read_size}, + buffer_count{buffer_count}, + baseband_bytes_received{0}, + fifo_buffers_empty{nullptr}, + fifo_buffers_full{nullptr} { + } }; class ReplayConfigMessage : public Message { -public: - constexpr ReplayConfigMessage( - ReplayConfig* const config - ) : Message { ID::ReplayConfig }, - config { config } - { - } + public: + constexpr ReplayConfigMessage( + ReplayConfig* const config) + : Message{ID::ReplayConfig}, + config{config} { + } - ReplayConfig* const config; + ReplayConfig* const config; }; class TXProgressMessage : public Message { -public: - constexpr TXProgressMessage( - ) : Message { ID::TXProgress } - { - } + public: + constexpr TXProgressMessage() + : Message{ID::TXProgress} { + } - uint32_t progress = 0; - bool done = false; + uint32_t progress = 0; + bool done = false; }; class AFSKRxConfigureMessage : public Message { -public: - constexpr AFSKRxConfigureMessage( - const uint32_t baudrate, - const uint32_t word_length, - const uint32_t trigger_value, - const bool trigger_word - ) : Message { ID::AFSKRxConfigure }, - baudrate(baudrate), - word_length(word_length), - trigger_value(trigger_value), - trigger_word(trigger_word) - { - } - - const uint32_t baudrate; - const uint32_t word_length; - const uint32_t trigger_value; - const bool trigger_word; + public: + constexpr AFSKRxConfigureMessage( + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word) + : Message{ID::AFSKRxConfigure}, + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) { + } + + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; }; class APRSRxConfigureMessage : public Message { -public: - constexpr APRSRxConfigureMessage( - const uint32_t baudrate - ) : Message { ID::APRSRxConfigure }, - baudrate(baudrate) - { - } + public: + constexpr APRSRxConfigureMessage( + const uint32_t baudrate) + : Message{ID::APRSRxConfigure}, + baudrate(baudrate) { + } - const uint32_t baudrate; + const uint32_t baudrate; }; class BTLERxConfigureMessage : public Message { -public: - constexpr BTLERxConfigureMessage( - const uint32_t baudrate, - const uint32_t word_length, - const uint32_t trigger_value, - const bool trigger_word - ) : Message { ID::BTLERxConfigure }, - baudrate(baudrate), - word_length(word_length), - trigger_value(trigger_value), - trigger_word(trigger_word) - { - } - const uint32_t baudrate; - const uint32_t word_length; - const uint32_t trigger_value; - const bool trigger_word; + public: + constexpr BTLERxConfigureMessage( + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word) + : Message{ID::BTLERxConfigure}, + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) { + } + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; }; class NRFRxConfigureMessage : public Message { -public: - constexpr NRFRxConfigureMessage( - const uint32_t baudrate, - const uint32_t word_length, - const uint32_t trigger_value, - const bool trigger_word - ) : Message { ID::NRFRxConfigure }, - baudrate(baudrate), - word_length(word_length), - trigger_value(trigger_value), - trigger_word(trigger_word) - { - } - const uint32_t baudrate; - const uint32_t word_length; - const uint32_t trigger_value; - const bool trigger_word; + public: + constexpr NRFRxConfigureMessage( + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word) + : Message{ID::NRFRxConfigure}, + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) { + } + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; }; class PitchRSSIConfigureMessage : public Message { -public: - constexpr PitchRSSIConfigureMessage( - const bool enabled, - const int32_t rssi - ) : Message { ID::PitchRSSIConfigure }, - enabled(enabled), - rssi(rssi) - { - } + public: + constexpr PitchRSSIConfigureMessage( + const bool enabled, + const int32_t rssi) + : Message{ID::PitchRSSIConfigure}, + enabled(enabled), + rssi(rssi) { + } - const bool enabled; - const int32_t rssi; + const bool enabled; + const int32_t rssi; }; class TonesConfigureMessage : public Message { -public: - constexpr TonesConfigureMessage( - const uint32_t fm_delta, - const uint32_t pre_silence, - const uint16_t tone_count, - const bool dual_tone, - const bool audio_out - ) : Message { ID::TonesConfigure }, - fm_delta(fm_delta), - pre_silence(pre_silence), - tone_count(tone_count), - dual_tone(dual_tone), - audio_out(audio_out) - { - } - - const uint32_t fm_delta; - const uint32_t pre_silence; - const uint16_t tone_count; - const bool dual_tone; - const bool audio_out; + public: + constexpr TonesConfigureMessage( + const uint32_t fm_delta, + const uint32_t pre_silence, + const uint16_t tone_count, + const bool dual_tone, + const bool audio_out) + : Message{ID::TonesConfigure}, + fm_delta(fm_delta), + pre_silence(pre_silence), + tone_count(tone_count), + dual_tone(dual_tone), + audio_out(audio_out) { + } + + const uint32_t fm_delta; + const uint32_t pre_silence; + const uint16_t tone_count; + const bool dual_tone; + const bool audio_out; }; class RDSConfigureMessage : public Message { -public: - constexpr RDSConfigureMessage( - const uint16_t length - ) : Message { ID::RDSConfigure }, - length(length) - { - } + public: + constexpr RDSConfigureMessage( + const uint16_t length) + : Message{ID::RDSConfigure}, + length(length) { + } - const uint16_t length = 0; + const uint16_t length = 0; }; class RetuneMessage : public Message { -public: - constexpr RetuneMessage( - ) : Message { ID::Retune } - { - } + public: + constexpr RetuneMessage() + : Message{ID::Retune} { + } - int64_t freq = 0; - uint32_t range = 0; + int64_t freq = 0; + uint32_t range = 0; }; class SamplerateConfigMessage : public Message { -public: - constexpr SamplerateConfigMessage( - const uint32_t sample_rate - ) : Message { ID::SamplerateConfig }, - sample_rate(sample_rate) - { - } + public: + constexpr SamplerateConfigMessage( + const uint32_t sample_rate) + : Message{ID::SamplerateConfig}, + sample_rate(sample_rate) { + } - const uint32_t sample_rate = 0; + const uint32_t sample_rate = 0; }; class AudioLevelReportMessage : public Message { -public: - constexpr AudioLevelReportMessage( - ) : Message { ID::AudioLevelReport } - { - } + public: + constexpr AudioLevelReportMessage() + : Message{ID::AudioLevelReport} { + } - uint32_t value = 0; + uint32_t value = 0; }; class AudioTXConfigMessage : public Message { -public: - constexpr AudioTXConfigMessage( - const uint32_t divider, - const float deviation_hz, - const float audio_gain, - const uint8_t audio_shift_bits_s16, - const uint32_t tone_key_delta, - const float tone_key_mix_weight, - const bool am_enabled, - const bool dsb_enabled, - const bool usb_enabled, - const bool lsb_enabled - ) : Message { ID::AudioTXConfig }, - divider(divider), - deviation_hz(deviation_hz), - audio_gain(audio_gain), - audio_shift_bits_s16(audio_shift_bits_s16), - tone_key_delta(tone_key_delta), - tone_key_mix_weight(tone_key_mix_weight), - am_enabled(am_enabled), - dsb_enabled(dsb_enabled), - usb_enabled(usb_enabled), - lsb_enabled(lsb_enabled) - { - } - - const uint32_t divider; - const float deviation_hz; - const float audio_gain; - const uint8_t audio_shift_bits_s16; - const uint32_t tone_key_delta; - const float tone_key_mix_weight; - const bool am_enabled; - const bool dsb_enabled; - const bool usb_enabled; - const bool lsb_enabled; + public: + constexpr AudioTXConfigMessage( + const uint32_t divider, + const float deviation_hz, + const float audio_gain, + const uint8_t audio_shift_bits_s16, + const uint32_t tone_key_delta, + const float tone_key_mix_weight, + const bool am_enabled, + const bool dsb_enabled, + const bool usb_enabled, + const bool lsb_enabled) + : Message{ID::AudioTXConfig}, + divider(divider), + deviation_hz(deviation_hz), + audio_gain(audio_gain), + audio_shift_bits_s16(audio_shift_bits_s16), + tone_key_delta(tone_key_delta), + tone_key_mix_weight(tone_key_mix_weight), + am_enabled(am_enabled), + dsb_enabled(dsb_enabled), + usb_enabled(usb_enabled), + lsb_enabled(lsb_enabled) { + } + + const uint32_t divider; + const float deviation_hz; + const float audio_gain; + const uint8_t audio_shift_bits_s16; + const uint32_t tone_key_delta; + const float tone_key_mix_weight; + const bool am_enabled; + const bool dsb_enabled; + const bool usb_enabled; + const bool lsb_enabled; }; class SigGenConfigMessage : public Message { -public: - constexpr SigGenConfigMessage( - const uint32_t bw, - const uint32_t shape, - const uint32_t duration - ) : Message { ID::SigGenConfig }, - bw(bw), - shape(shape), - duration(duration) - { - } - - const uint32_t bw; - const uint32_t shape; - const uint32_t duration; + public: + constexpr SigGenConfigMessage( + const uint32_t bw, + const uint32_t shape, + const uint32_t duration) + : Message{ID::SigGenConfig}, + bw(bw), + shape(shape), + duration(duration) { + } + + const uint32_t bw; + const uint32_t shape; + const uint32_t duration; }; class SigGenToneMessage : public Message { -public: - constexpr SigGenToneMessage( - const uint32_t tone_delta - ) : Message { ID::SigGenTone }, - tone_delta(tone_delta) - { - } + public: + constexpr SigGenToneMessage( + const uint32_t tone_delta) + : Message{ID::SigGenTone}, + tone_delta(tone_delta) { + } - const uint32_t tone_delta; + const uint32_t tone_delta; }; class AFSKTxConfigureMessage : public Message { -public: - constexpr AFSKTxConfigureMessage( - const uint32_t samples_per_bit, - const uint32_t phase_inc_mark, - const uint32_t phase_inc_space, - const uint8_t repeat, - const uint32_t fm_delta, - const uint8_t symbol_count - ) : Message { ID::AFSKTxConfigure }, - samples_per_bit(samples_per_bit), - phase_inc_mark(phase_inc_mark), - phase_inc_space(phase_inc_space), - repeat(repeat), - fm_delta(fm_delta), - symbol_count(symbol_count) - { - } - - const uint32_t samples_per_bit; - const uint32_t phase_inc_mark; - const uint32_t phase_inc_space; - const uint8_t repeat; - const uint32_t fm_delta; - const uint8_t symbol_count; + public: + constexpr AFSKTxConfigureMessage( + const uint32_t samples_per_bit, + const uint32_t phase_inc_mark, + const uint32_t phase_inc_space, + const uint8_t repeat, + const uint32_t fm_delta, + const uint8_t symbol_count) + : Message{ID::AFSKTxConfigure}, + samples_per_bit(samples_per_bit), + phase_inc_mark(phase_inc_mark), + phase_inc_space(phase_inc_space), + repeat(repeat), + fm_delta(fm_delta), + symbol_count(symbol_count) { + } + + const uint32_t samples_per_bit; + const uint32_t phase_inc_mark; + const uint32_t phase_inc_space; + const uint8_t repeat; + const uint32_t fm_delta; + const uint8_t symbol_count; }; class OOKConfigureMessage : public Message { -public: - constexpr OOKConfigureMessage( - const uint32_t stream_length, - const uint32_t samples_per_bit, - const uint8_t repeat, - const uint32_t pause_symbols, - const uint8_t de_bruijn_length - ) : Message { ID::OOKConfigure }, - stream_length(stream_length), - samples_per_bit(samples_per_bit), - repeat(repeat), - pause_symbols(pause_symbols), - de_bruijn_length(de_bruijn_length) - { - } - - const uint32_t stream_length; - const uint32_t samples_per_bit; - const uint8_t repeat; - const uint32_t pause_symbols; - const uint8_t de_bruijn_length; + public: + constexpr OOKConfigureMessage( + const uint32_t stream_length, + const uint32_t samples_per_bit, + const uint8_t repeat, + const uint32_t pause_symbols, + const uint8_t de_bruijn_length) + : Message{ID::OOKConfigure}, + stream_length(stream_length), + samples_per_bit(samples_per_bit), + repeat(repeat), + pause_symbols(pause_symbols), + de_bruijn_length(de_bruijn_length) { + } + + const uint32_t stream_length; + const uint32_t samples_per_bit; + const uint8_t repeat; + const uint32_t pause_symbols; + const uint8_t de_bruijn_length; }; class SSTVConfigureMessage : public Message { -public: - constexpr SSTVConfigureMessage( - const uint8_t vis_code, - const uint32_t pixel_duration - ) : Message { ID::SSTVConfigure }, - vis_code(vis_code), - pixel_duration(pixel_duration) - { - } + public: + constexpr SSTVConfigureMessage( + const uint8_t vis_code, + const uint32_t pixel_duration) + : Message{ID::SSTVConfigure}, + vis_code(vis_code), + pixel_duration(pixel_duration) { + } - const uint8_t vis_code; - const uint32_t pixel_duration; + const uint8_t vis_code; + const uint32_t pixel_duration; }; class FSKConfigureMessage : public Message { -public: - constexpr FSKConfigureMessage( - const uint32_t stream_length, - const uint32_t samples_per_bit, - const uint32_t shift, - const uint32_t progress_notice - ) : Message { ID::FSKConfigure }, - stream_length(stream_length), - samples_per_bit(samples_per_bit), - shift(shift), - progress_notice(progress_notice) - { - } - - const uint32_t stream_length; - const uint32_t samples_per_bit; - const uint32_t shift; - const uint32_t progress_notice; + public: + constexpr FSKConfigureMessage( + const uint32_t stream_length, + const uint32_t samples_per_bit, + const uint32_t shift, + const uint32_t progress_notice) + : Message{ID::FSKConfigure}, + stream_length(stream_length), + samples_per_bit(samples_per_bit), + shift(shift), + progress_notice(progress_notice) { + } + + const uint32_t stream_length; + const uint32_t samples_per_bit; + const uint32_t shift; + const uint32_t progress_notice; }; class POCSAGConfigureMessage : public Message { -public: - constexpr POCSAGConfigureMessage() - : Message { ID::POCSAGConfigure } - { - } + public: + constexpr POCSAGConfigureMessage() + : Message{ID::POCSAGConfigure} { + } }; class APRSPacketMessage : public Message { -public: - constexpr APRSPacketMessage( - const aprs::APRSPacket& packet - ) : Message { ID::APRSPacket }, - packet { packet } - { - } + public: + constexpr APRSPacketMessage( + const aprs::APRSPacket& packet) + : Message{ID::APRSPacket}, + packet{packet} { + } - aprs::APRSPacket packet; + aprs::APRSPacket packet; }; - class ADSBConfigureMessage : public Message { -public: - constexpr ADSBConfigureMessage( - const uint32_t test - ) : Message { ID::ADSBConfigure }, - test(test) - { - } + public: + constexpr ADSBConfigureMessage( + const uint32_t test) + : Message{ID::ADSBConfigure}, + test(test) { + } - const uint32_t test; + const uint32_t test; }; class JammerConfigureMessage : public Message { -public: - constexpr JammerConfigureMessage( - const bool run, - const jammer::JammerType type, - const uint32_t speed - ) : Message { ID::JammerConfigure }, - run(run), - type(type), - speed(speed) - { - } - - const bool run; - const jammer::JammerType type; - const uint32_t speed; + public: + constexpr JammerConfigureMessage( + const bool run, + const jammer::JammerType type, + const uint32_t speed) + : Message{ID::JammerConfigure}, + run(run), + type(type), + speed(speed) { + } + + const bool run; + const jammer::JammerType type; + const uint32_t speed; }; class DTMFTXConfigMessage : public Message { -public: - constexpr DTMFTXConfigMessage( - const uint32_t bw, - const uint32_t tone_length, - const uint32_t pause_length - ) : Message { ID::DTMFTXConfig }, - bw(bw), - tone_length(tone_length), - pause_length(pause_length) - { - } - - const uint32_t bw; - const uint32_t tone_length; - const uint32_t pause_length; + public: + constexpr DTMFTXConfigMessage( + const uint32_t bw, + const uint32_t tone_length, + const uint32_t pause_length) + : Message{ID::DTMFTXConfig}, + bw(bw), + tone_length(tone_length), + pause_length(pause_length) { + } + + const uint32_t bw; + const uint32_t tone_length; + const uint32_t pause_length; }; // TODO: use streaming buffer instead // TODO: rename (not only used for requests) class RequestSignalMessage : public Message { -public: - enum class Signal : char { - FillRequest = 1, - BeepRequest = 2, - Squelched = 3 - }; - - constexpr RequestSignalMessage( - Signal signal - ) : Message { ID::RequestSignal }, - signal ( signal ) - { - } + public: + enum class Signal : char { + FillRequest = 1, + BeepRequest = 2, + Squelched = 3 + }; + + constexpr RequestSignalMessage( + Signal signal) + : Message{ID::RequestSignal}, + signal(signal) { + } - Signal signal; + Signal signal; }; class FIFODataMessage : public Message { -public: - constexpr FIFODataMessage( - const int8_t * data - ) : Message { ID::FIFOData }, - data ( data ) - { - } + public: + constexpr FIFODataMessage( + const int8_t* data) + : Message{ID::FIFOData}, + data(data) { + } - const int8_t * data; + const int8_t* data; }; class CaptureThreadDoneMessage : public Message { -public: - constexpr CaptureThreadDoneMessage( - uint32_t error = 0 - ) : Message { ID::CaptureThreadDone }, - error { error } - { - } + public: + constexpr CaptureThreadDoneMessage( + uint32_t error = 0) + : Message{ID::CaptureThreadDone}, + error{error} { + } - uint32_t error; + uint32_t error; }; class ReplayThreadDoneMessage : public Message { -public: - constexpr ReplayThreadDoneMessage( - uint32_t return_code = 0 - ) : Message { ID::ReplayThreadDone }, - return_code { return_code } - { - } + public: + constexpr ReplayThreadDoneMessage( + uint32_t return_code = 0) + : Message{ID::ReplayThreadDone}, + return_code{return_code} { + } - uint32_t return_code; + uint32_t return_code; }; class SpectrumPainterBufferConfigureRequestMessage : public Message { -public: - constexpr SpectrumPainterBufferConfigureRequestMessage( - uint16_t width, - uint16_t height, - bool update, - int32_t bw - ) : Message { ID::SpectrumPainterBufferRequestConfigure }, - width { width }, - height { height }, - update { update }, - bw { bw } - { - } - - uint16_t width; - uint16_t height; - bool update; - int32_t bw; + public: + constexpr SpectrumPainterBufferConfigureRequestMessage( + uint16_t width, + uint16_t height, + bool update, + int32_t bw) + : Message{ID::SpectrumPainterBufferRequestConfigure}, + width{width}, + height{height}, + update{update}, + bw{bw} { + } + + uint16_t width; + uint16_t height; + bool update; + int32_t bw; }; using SpectrumPainterFIFO = FIFO>; class SpectrumPainterBufferConfigureResponseMessage : public Message { -public: - static constexpr size_t fifo_k = 2; + public: + static constexpr size_t fifo_k = 2; - constexpr SpectrumPainterBufferConfigureResponseMessage( - SpectrumPainterFIFO* fifo - ) : Message { ID::SpectrumPainterBufferResponseConfigure }, - fifo { fifo } - { - } + constexpr SpectrumPainterBufferConfigureResponseMessage( + SpectrumPainterFIFO* fifo) + : Message{ID::SpectrumPainterBufferResponseConfigure}, + fifo{fifo} { + } - SpectrumPainterFIFO* fifo { nullptr }; + SpectrumPainterFIFO* fifo{nullptr}; }; -#endif/*__MESSAGE_H__*/ +#endif /*__MESSAGE_H__*/ diff --git a/firmware/common/message_queue.cpp b/firmware/common/message_queue.cpp index a7b99b565..90f672e7b 100644 --- a/firmware/common/message_queue.cpp +++ b/firmware/common/message_queue.cpp @@ -26,12 +26,12 @@ using namespace lpc43xx; #if defined(LPC43XX_M0) void MessageQueue::signal() { - creg::m0apptxevent::assert_event(); + creg::m0apptxevent::assert_event(); } #endif #if defined(LPC43XX_M4) void MessageQueue::signal() { - creg::m4txevent::assert_event(); + creg::m4txevent::assert_event(); } #endif diff --git a/firmware/common/message_queue.hpp b/firmware/common/message_queue.hpp index 0a12fb627..57534dc90 100644 --- a/firmware/common/message_queue.hpp +++ b/firmware/common/message_queue.hpp @@ -30,89 +30,89 @@ #include class MessageQueue { -public: - MessageQueue() = delete; - MessageQueue(const MessageQueue&) = delete; - MessageQueue(MessageQueue&&) = delete; - - MessageQueue( - uint8_t* const data, - size_t k - ) : fifo { data, k } - { - chMtxInit(&mutex_write); - } - - template - bool push(const T& message) { - static_assert(sizeof(T) <= Message::MAX_SIZE, "Message::MAX_SIZE too small for message type"); - static_assert(std::is_base_of::value, "type is not based on Message"); - - return push(&message, sizeof(message)); - } - - template - bool push_and_wait(const T& message) { - const bool result = push(message); - if( result ) { - // TODO: More graceful method of waiting for empty? Maybe sleep for a bit? - while( !is_empty() ); - } - return result; - } - - template - void handle(HandlerFn handler) { - std::array message_buffer; - while(Message* const message = peek(message_buffer)) { - handler(message); - skip(); - } - } - - bool is_empty() const { - return fifo.is_empty(); - } - - void reset() { - fifo.reset(); - } - -private: - FIFO fifo; - Mutex mutex_write { }; - - Message* peek(std::array& buf) { - Message* const p = reinterpret_cast(buf.data()); - return fifo.peek_r(buf.data(), buf.size()) ? p : nullptr; - } - - bool skip() { - return fifo.skip(); - } - - Message* pop(std::array& buf) { - Message* const p = reinterpret_cast(buf.data()); - return fifo.out_r(buf.data(), buf.size()) ? p : nullptr; - } - - size_t len() const { - return fifo.len(); - } - - bool push(const void* const buf, const size_t len) { - chMtxLock(&mutex_write); - const auto result = fifo.in_r(buf, len); - chMtxUnlock(); - - const bool success = (result == len); - if( success ) { - signal(); - } - return success; - } - - void signal(); + public: + MessageQueue() = delete; + MessageQueue(const MessageQueue&) = delete; + MessageQueue(MessageQueue&&) = delete; + + MessageQueue( + uint8_t* const data, + size_t k) + : fifo{data, k} { + chMtxInit(&mutex_write); + } + + template + bool push(const T& message) { + static_assert(sizeof(T) <= Message::MAX_SIZE, "Message::MAX_SIZE too small for message type"); + static_assert(std::is_base_of::value, "type is not based on Message"); + + return push(&message, sizeof(message)); + } + + template + bool push_and_wait(const T& message) { + const bool result = push(message); + if (result) { + // TODO: More graceful method of waiting for empty? Maybe sleep for a bit? + while (!is_empty()) + ; + } + return result; + } + + template + void handle(HandlerFn handler) { + std::array message_buffer; + while (Message* const message = peek(message_buffer)) { + handler(message); + skip(); + } + } + + bool is_empty() const { + return fifo.is_empty(); + } + + void reset() { + fifo.reset(); + } + + private: + FIFO fifo; + Mutex mutex_write{}; + + Message* peek(std::array& buf) { + Message* const p = reinterpret_cast(buf.data()); + return fifo.peek_r(buf.data(), buf.size()) ? p : nullptr; + } + + bool skip() { + return fifo.skip(); + } + + Message* pop(std::array& buf) { + Message* const p = reinterpret_cast(buf.data()); + return fifo.out_r(buf.data(), buf.size()) ? p : nullptr; + } + + size_t len() const { + return fifo.len(); + } + + bool push(const void* const buf, const size_t len) { + chMtxLock(&mutex_write); + const auto result = fifo.in_r(buf, len); + chMtxUnlock(); + + const bool success = (result == len); + if (success) { + signal(); + } + return success; + } + + void signal(); }; -#endif/*__MESSAGE_QUEUE_H__*/ +#endif /*__MESSAGE_QUEUE_H__*/ diff --git a/firmware/common/modules.h b/firmware/common/modules.h index 33a0f7e41..f26560495 100644 --- a/firmware/common/modules.h +++ b/firmware/common/modules.h @@ -1,2 +1,36 @@ -const char md5_baseband[16] = {0xb8,0x9e,0x9b,0x08,0x44,0x34,0x04,0x20,0x0b,0xbc,0x60,0x7e,0x67,0x88,0x53,0xf7,}; -const char md5_baseband_tx[16] = {0xd5,0xaf,0x76,0xd5,0xa3,0x32,0x5d,0x9a,0x9d,0x83,0x46,0x37,0x02,0x2d,0xd0,0x57,}; +const char md5_baseband[16] = { + 0xb8, + 0x9e, + 0x9b, + 0x08, + 0x44, + 0x34, + 0x04, + 0x20, + 0x0b, + 0xbc, + 0x60, + 0x7e, + 0x67, + 0x88, + 0x53, + 0xf7, +}; +const char md5_baseband_tx[16] = { + 0xd5, + 0xaf, + 0x76, + 0xd5, + 0xa3, + 0x32, + 0x5d, + 0x9a, + 0x9d, + 0x83, + 0x46, + 0x37, + 0x02, + 0x2d, + 0xd0, + 0x57, +}; diff --git a/firmware/common/morse.cpp b/firmware/common/morse.cpp index bc8ae318e..b91a3a183 100644 --- a/firmware/common/morse.cpp +++ b/firmware/common/morse.cpp @@ -31,61 +31,59 @@ using namespace portapack; namespace morse { // Returns 0 if message is too long -size_t morse_encode(std::string& message, const uint32_t time_unit_ms, - const uint32_t tone, uint32_t * const time_units) { - - size_t i, c; - uint16_t code, code_size; - uint8_t morse_message[256]; - uint32_t delta; - - *time_units = 0; - - i = 0; - for (char& ch : message) { - if (i > 256) return 0; // Message too long - - if ((ch >= 'a') && (ch <= 'z')) // Make uppercase - ch -= 32; - - if ((ch >= '!') && (ch <= '_')) { - code = morse_ITU[ch - '!']; - } else { - code = 0; // Default to space char - } - - if (!code) { - if (i) - morse_message[i - 1] = 4; // Word space - } else { - code_size = code & 7; - - for (c = 0; c < code_size; c++) { - morse_message[i++] = ((code << c) & 0x8000) ? 1 : 0; // Dot/dash - morse_message[i++] = 2; // Symbol space - } - morse_message[i - 1] = 3; // Letter space - } - } - - // Count time units - for (c = 0; c < i; c++) { - *time_units += morse_symbols[morse_message[c]]; - } - - memcpy(shared_memory.bb_data.tones_data.message, morse_message, i); - - // Setup tone "symbols" - for (c = 0; c < 5; c++) { - if (c < 2) - delta = TONES_F2D(tone, TONES_SAMPLERATE); // Dot and dash - else - delta = 0; // Pause - - baseband::set_tone(c, delta, (TONES_SAMPLERATE * morse_symbols[c] * time_unit_ms) / 1000); - } - - return i; +size_t morse_encode(std::string& message, const uint32_t time_unit_ms, const uint32_t tone, uint32_t* const time_units) { + size_t i, c; + uint16_t code, code_size; + uint8_t morse_message[256]; + uint32_t delta; + + *time_units = 0; + + i = 0; + for (char& ch : message) { + if (i > 256) return 0; // Message too long + + if ((ch >= 'a') && (ch <= 'z')) // Make uppercase + ch -= 32; + + if ((ch >= '!') && (ch <= '_')) { + code = morse_ITU[ch - '!']; + } else { + code = 0; // Default to space char + } + + if (!code) { + if (i) + morse_message[i - 1] = 4; // Word space + } else { + code_size = code & 7; + + for (c = 0; c < code_size; c++) { + morse_message[i++] = ((code << c) & 0x8000) ? 1 : 0; // Dot/dash + morse_message[i++] = 2; // Symbol space + } + morse_message[i - 1] = 3; // Letter space + } + } + + // Count time units + for (c = 0; c < i; c++) { + *time_units += morse_symbols[morse_message[c]]; + } + + memcpy(shared_memory.bb_data.tones_data.message, morse_message, i); + + // Setup tone "symbols" + for (c = 0; c < 5; c++) { + if (c < 2) + delta = TONES_F2D(tone, TONES_SAMPLERATE); // Dot and dash + else + delta = 0; // Pause + + baseband::set_tone(c, delta, (TONES_SAMPLERATE * morse_symbols[c] * time_unit_ms) / 1000); + } + + return i; } } /* namespace morse */ diff --git a/firmware/common/morse.hpp b/firmware/common/morse.hpp index 7522a097c..df20b3f28 100644 --- a/firmware/common/morse.hpp +++ b/firmware/common/morse.hpp @@ -33,116 +33,114 @@ #define MORSE_WORD_SPACE 7 namespace morse { - + const uint32_t morse_symbols[5] = { - MORSE_DOT, - MORSE_DASH, - MORSE_SYMBOL_SPACE, - MORSE_LETTER_SPACE, - MORSE_WORD_SPACE -}; + MORSE_DOT, + MORSE_DASH, + MORSE_SYMBOL_SPACE, + MORSE_LETTER_SPACE, + MORSE_WORD_SPACE}; -size_t morse_encode(std::string& message, const uint32_t time_unit_ms, - const uint32_t tone, uint32_t * const time_units); +size_t morse_encode(std::string& message, const uint32_t time_unit_ms, const uint32_t tone, uint32_t* const time_units); constexpr char foxhunt_codes[11][4] = { - { "MOE" }, // -----. - { "MOI" }, // -----.. - { "MOS" }, // -----... - { "MOH" }, // -----.... - { "MO5" }, // -----..... - { "MON" }, // ------. - { "MOD" }, // ------.. - { "MOB" }, // ------... - { "MO6" }, // ------.... - { "MO" }, // ----- - { "S" } // ... + {"MOE"}, // -----. + {"MOI"}, // -----.. + {"MOS"}, // -----... + {"MOH"}, // -----.... + {"MO5"}, // -----..... + {"MON"}, // ------. + {"MOD"}, // ------.. + {"MOB"}, // ------... + {"MO6"}, // ------.... + {"MO"}, // ----- + {"S"} // ... }; // 0=dot 1=dash constexpr uint16_t morse_ITU[63] = { - // Code Size - 0b1010110000000110, // !: 101011- 110 - 0b0100100000000110, // ": 010010- 110 - 0, // # - 0b0001001000000111, // $: 0001001 111 - 0, // % - 0b0100000000000101, // &: 01000-- 101 - 0b0111100000000110, // ': 011110- 110 - 0b1011000000000101, // (: 10110-- 101 - 0b1011010000000110, // ): 101101- 110 - 0, // * - 0b0101000000000101, // +: 01010-- 101 - 0b1100110000000110, // ,: 110011- 110 - 0b1000010000000110, // -: 100001- 110 - 0b0101010000000110, // .: 010101- 110 - 0b1001000000000101, // /: 10010-- 101 - 0b1111100000000101, // 0: 11111-- 101 - 0b0111100000000101, // 1: 01111-- 101 - 0b0011100000000101, // 2: 00111-- 101 - 0b0001100000000101, // 3: 00011-- 101 - 0b0000100000000101, // 4: 00001-- 101 - 0b0000000000000101, // 5: 00000-- 101 - 0b1000000000000101, // 6: 10000-- 101 - 0b1100000000000101, // 7: 11000-- 101 - 0b1110000000000101, // 8: 11100-- 101 - 0b1111000000000101, // 9: 11110-- 101 - 0b1110000000000110, // :: 111000- 110 - 0b1010100000000110, // ;: 101010- 110 - 0, // < - 0b1000100000000101, // =: 10001-- 101 - 0, // > - 0b0011000000000110, // ?: 001100- 110 - 0b0110100000000110, // @: 011010- 110 - 0b0100000000000010, // A: 01----- 010 - 0b1000000000000100, // B: 1000--- 100 - 0b1010000000000100, // C: 1010--- 100 - 0b1000000000000011, // D: 100---- 011 - 0b0000000000000001, // E: 0------ 001 - 0b0010000000000100, // F: 0010--- 100 - 0b1100000000000011, // G: 110---- 011 - 0b0000000000000100, // H: 0000--- 100 - 0b0000000000000010, // I: 00----- 010 - 0b0111000000000100, // J: 0111--- 100 - 0b1010000000000011, // K: 101---- 011 - 0b0100000000000100, // L: 0100--- 100 - 0b1100000000000010, // M: 11----- 010 - 0b1000000000000010, // N: 10----- 010 - 0b1110000000000011, // O: 111---- 011 - 0b0110000000000100, // P: 0110--- 100 .#-###-###.# ##.#-### ##.#-###.# ##.#.# ##.#.#.# = 48 units - 0b1101000000000100, // Q: 1101--- 100 - 0b0100000000000011, // R: 010---- 011 - 0b0000000000000011, // S: 000---- 011 - 0b1000000000000001, // T: 1------ 001 - 0b0010000000000011, // U: 001---- 011 - 0b0001000000000100, // V: 0001--- 100 - 0b0110000000000011, // W: 011---- 011 - 0b1001000000000100, // X: 1001--- 100 - 0b1011000000000100, // Y: 1011--- 100 - 0b1100000000000100, // Z: 1100--- 100 - 0, // [ - 0, // Back-slash - 0, // ] - 0, // ^ - 0b0011010000000110 // _: 001101- 110 + // Code Size + 0b1010110000000110, // !: 101011- 110 + 0b0100100000000110, // ": 010010- 110 + 0, // # + 0b0001001000000111, // $: 0001001 111 + 0, // % + 0b0100000000000101, // &: 01000-- 101 + 0b0111100000000110, // ': 011110- 110 + 0b1011000000000101, // (: 10110-- 101 + 0b1011010000000110, // ): 101101- 110 + 0, // * + 0b0101000000000101, // +: 01010-- 101 + 0b1100110000000110, // ,: 110011- 110 + 0b1000010000000110, // -: 100001- 110 + 0b0101010000000110, // .: 010101- 110 + 0b1001000000000101, // /: 10010-- 101 + 0b1111100000000101, // 0: 11111-- 101 + 0b0111100000000101, // 1: 01111-- 101 + 0b0011100000000101, // 2: 00111-- 101 + 0b0001100000000101, // 3: 00011-- 101 + 0b0000100000000101, // 4: 00001-- 101 + 0b0000000000000101, // 5: 00000-- 101 + 0b1000000000000101, // 6: 10000-- 101 + 0b1100000000000101, // 7: 11000-- 101 + 0b1110000000000101, // 8: 11100-- 101 + 0b1111000000000101, // 9: 11110-- 101 + 0b1110000000000110, // :: 111000- 110 + 0b1010100000000110, // ;: 101010- 110 + 0, // < + 0b1000100000000101, // =: 10001-- 101 + 0, // > + 0b0011000000000110, // ?: 001100- 110 + 0b0110100000000110, // @: 011010- 110 + 0b0100000000000010, // A: 01----- 010 + 0b1000000000000100, // B: 1000--- 100 + 0b1010000000000100, // C: 1010--- 100 + 0b1000000000000011, // D: 100---- 011 + 0b0000000000000001, // E: 0------ 001 + 0b0010000000000100, // F: 0010--- 100 + 0b1100000000000011, // G: 110---- 011 + 0b0000000000000100, // H: 0000--- 100 + 0b0000000000000010, // I: 00----- 010 + 0b0111000000000100, // J: 0111--- 100 + 0b1010000000000011, // K: 101---- 011 + 0b0100000000000100, // L: 0100--- 100 + 0b1100000000000010, // M: 11----- 010 + 0b1000000000000010, // N: 10----- 010 + 0b1110000000000011, // O: 111---- 011 + 0b0110000000000100, // P: 0110--- 100 .#-###-###.# ##.#-### ##.#-###.# ##.#.# ##.#.#.# = 48 units + 0b1101000000000100, // Q: 1101--- 100 + 0b0100000000000011, // R: 010---- 011 + 0b0000000000000011, // S: 000---- 011 + 0b1000000000000001, // T: 1------ 001 + 0b0010000000000011, // U: 001---- 011 + 0b0001000000000100, // V: 0001--- 100 + 0b0110000000000011, // W: 011---- 011 + 0b1001000000000100, // X: 1001--- 100 + 0b1011000000000100, // Y: 1011--- 100 + 0b1100000000000100, // Z: 1100--- 100 + 0, // [ + 0, // Back-slash + 0, // ] + 0, // ^ + 0b0011010000000110 // _: 001101- 110 }; constexpr uint16_t prosigns[12] = { - // Code Size - 0b0001110000001001, // : 000111000 1001 - 0b0101000000000100, // : 0101----- 0100 - 0b0101000000000101, // : 01010---- 0101 - 0b0100000000000101, // : 01000---- 0101 - 0b1000100000000101, // : 10001---- 0101 - 0b1010100000000101, // : 10101---- 0101 - 0b0000000000001000, // : 00000000- 1000 - 0b1010000000000011, // : 101------ 0011 - 0b1011000000000101, // : 10110---- 0101 - 0b1001110000000110, // : 100111--- 0110 - 0b0001010000000110, // : 000101--- 0110 - 0b0001100000000101, // : 00010---- 0101 + // Code Size + 0b0001110000001001, // : 000111000 1001 + 0b0101000000000100, // : 0101----- 0100 + 0b0101000000000101, // : 01010---- 0101 + 0b0100000000000101, // : 01000---- 0101 + 0b1000100000000101, // : 10001---- 0101 + 0b1010100000000101, // : 10101---- 0101 + 0b0000000000001000, // : 00000000- 1000 + 0b1010000000000011, // : 101------ 0011 + 0b1011000000000101, // : 10110---- 0101 + 0b1001110000000110, // : 100111--- 0110 + 0b0001010000000110, // : 000101--- 0110 + 0b0001100000000101, // : 00010---- 0101 }; } /* namespace morse */ -#endif/*__MORSE_H__*/ +#endif /*__MORSE_H__*/ diff --git a/firmware/common/msgpack.cpp b/firmware/common/msgpack.cpp index 2ba4f1a3d..0bcbc1010 100644 --- a/firmware/common/msgpack.cpp +++ b/firmware/common/msgpack.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -22,320 +22,317 @@ #include "msgpack.hpp" -bool MsgPack::get_bool(const void * buffer, const bool inc, bool * value) { - uint8_t v; - - if (seek_ptr >= buffer_size) return false; // End of buffer - - v = ((uint8_t*)buffer)[seek_ptr]; - if (v == MSGPACK_FALSE) - *value = false; - else if (v == MSGPACK_TRUE) - *value = true; - else - return false; // Not a bool - - if (inc) seek_ptr++; - return true; +bool MsgPack::get_bool(const void* buffer, const bool inc, bool* value) { + uint8_t v; + + if (seek_ptr >= buffer_size) return false; // End of buffer + + v = ((uint8_t*)buffer)[seek_ptr]; + if (v == MSGPACK_FALSE) + *value = false; + else if (v == MSGPACK_TRUE) + *value = true; + else + return false; // Not a bool + + if (inc) seek_ptr++; + return true; } -bool MsgPack::get_raw_byte(const void * buffer, const bool inc, uint8_t * byte) { - if (seek_ptr >= buffer_size) return false; // End of buffer - *byte = ((uint8_t*)buffer)[seek_ptr]; - if (inc) seek_ptr++; - return true; +bool MsgPack::get_raw_byte(const void* buffer, const bool inc, uint8_t* byte) { + if (seek_ptr >= buffer_size) return false; // End of buffer + *byte = ((uint8_t*)buffer)[seek_ptr]; + if (inc) seek_ptr++; + return true; } -bool MsgPack::get_raw_word(const void * buffer, const bool inc, uint16_t * word) { - if ((seek_ptr + 1) >= buffer_size) return false; // End of buffer - *word = (((uint8_t*)buffer)[seek_ptr] << 8) | ((uint8_t*)buffer)[seek_ptr + 1]; - if (inc) seek_ptr += 2; - return true; +bool MsgPack::get_raw_word(const void* buffer, const bool inc, uint16_t* word) { + if ((seek_ptr + 1) >= buffer_size) return false; // End of buffer + *word = (((uint8_t*)buffer)[seek_ptr] << 8) | ((uint8_t*)buffer)[seek_ptr + 1]; + if (inc) seek_ptr += 2; + return true; } -bool MsgPack::get_u8(const void * buffer, const bool inc, uint8_t * value) { - uint8_t v; - - if (seek_ptr >= buffer_size) return false; // End of buffer - - v = ((uint8_t*)buffer)[seek_ptr]; - - if (!(v & 0x80)) - *value = ((uint8_t*)buffer)[seek_ptr]; // Fixnum - else if (v == MSGPACK_TYPE_U8) - *value = ((uint8_t*)buffer)[seek_ptr + 1]; // u8 - else - return false; // Value isn't a u8 or fixnum - - if (inc) seek_ptr++; - return true; +bool MsgPack::get_u8(const void* buffer, const bool inc, uint8_t* value) { + uint8_t v; + + if (seek_ptr >= buffer_size) return false; // End of buffer + + v = ((uint8_t*)buffer)[seek_ptr]; + + if (!(v & 0x80)) + *value = ((uint8_t*)buffer)[seek_ptr]; // Fixnum + else if (v == MSGPACK_TYPE_U8) + *value = ((uint8_t*)buffer)[seek_ptr + 1]; // u8 + else + return false; // Value isn't a u8 or fixnum + + if (inc) seek_ptr++; + return true; } // TODO: Typecheck function -bool MsgPack::get_u16(const void * buffer, const bool inc, uint16_t * value) { - uint8_t byte; - - if ((seek_ptr + 1) >= buffer_size) return false; // End of buffer - if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_U16)) return false; // Value isn't a u16 - *value = (((uint8_t*)buffer)[seek_ptr] << 8) | ((uint8_t*)buffer)[seek_ptr + 1]; - if (inc) seek_ptr += 2; - return true; -} +bool MsgPack::get_u16(const void* buffer, const bool inc, uint16_t* value) { + uint8_t byte; -bool MsgPack::get_s32(const void * buffer, const bool inc, int32_t * value) { - uint8_t byte; - - if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer - if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_S32)) return false; // Value isn't a s32 - *value = (((uint8_t*)buffer)[seek_ptr] << 24) | (((uint8_t*)buffer)[seek_ptr + 1] << 16) | - (((uint8_t*)buffer)[seek_ptr + 2] << 8) | ((uint8_t*)buffer)[seek_ptr + 3]; - if (inc) seek_ptr += 4; - return true; + if ((seek_ptr + 1) >= buffer_size) return false; // End of buffer + if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_U16)) return false; // Value isn't a u16 + *value = (((uint8_t*)buffer)[seek_ptr] << 8) | ((uint8_t*)buffer)[seek_ptr + 1]; + if (inc) seek_ptr += 2; + return true; } -bool MsgPack::get_string(const void * buffer, const bool inc, std::string& value) { - size_t length; - uint8_t byte; - - // Todo: Set max length ! - if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer - if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_STR8) - && (byte != MSGPACK_TYPE_STR16)) return false; // Value isn't a str8 or str16 - - if (byte == MSGPACK_TYPE_STR8) { - if (!get_raw_byte(buffer, true, (uint8_t*)&length)) return false; // Couldn't get str8 length - } else if (byte == MSGPACK_TYPE_STR16) { - if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get str16 length - } - - memcpy(&value[0], ((uint8_t*)buffer), length); //std::string( - - if (inc) seek_ptr += length; - return true; +bool MsgPack::get_s32(const void* buffer, const bool inc, int32_t* value) { + uint8_t byte; + + if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer + if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_S32)) return false; // Value isn't a s32 + *value = (((uint8_t*)buffer)[seek_ptr] << 24) | (((uint8_t*)buffer)[seek_ptr + 1] << 16) | + (((uint8_t*)buffer)[seek_ptr + 2] << 8) | ((uint8_t*)buffer)[seek_ptr + 3]; + if (inc) seek_ptr += 4; + return true; } -bool MsgPack::init_search(const void * buffer, const size_t size) { - uint8_t byte; - uint16_t map_size; - - if (!size) return false; - buffer_size = size; - seek_ptr = 0; - if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_MAP16)) return false; // First record isn't a map16 - if (!get_raw_word(buffer, true, &map_size)) return false; // Couldn't get map16 size - if (!map_size) return false; - - return true; +bool MsgPack::get_string(const void* buffer, const bool inc, std::string& value) { + size_t length; + uint8_t byte; + + // Todo: Set max length ! + if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer + if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_STR8) && (byte != MSGPACK_TYPE_STR16)) return false; // Value isn't a str8 or str16 + + if (byte == MSGPACK_TYPE_STR8) { + if (!get_raw_byte(buffer, true, (uint8_t*)&length)) return false; // Couldn't get str8 length + } else if (byte == MSGPACK_TYPE_STR16) { + if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get str16 length + } + + memcpy(&value[0], ((uint8_t*)buffer), length); // std::string( + + if (inc) seek_ptr += length; + return true; } -bool MsgPack::skip(const void * buffer) { - uint8_t byte, c; - size_t length; - - if (!get_raw_byte(buffer, true, &byte)) return false; // Couldn't get type - - if (!(byte & 0x80)) return true; // Positive fixnum, already skipped by get_raw_byte - if ((byte & 0xE0) == 0xE0) return true; // Negative fixnum, already skipped by get_raw_byte - if ((byte & 0xE0) == 0xA0) { // Fixstr - seek_ptr += (byte & 0x1F); - return true; - } - if ((byte & 0xF0) == 0x80) { // Fixmap - length = (byte & 0x0F) * 2; - for (c = 0; c < length; c++) - skip(buffer); - return true; - } - if ((byte & 0xF0) == 0x90) { // Fixarray - length = byte & 0x0F; - for (c = 0; c < length; c++) - skip(buffer); - return true; - } - - switch (byte) { - case MSGPACK_NIL: - case MSGPACK_FALSE: - case MSGPACK_TRUE: // Already skipped by get_raw_byte - break; - case MSGPACK_TYPE_U8: - case MSGPACK_TYPE_S8: - seek_ptr++; - break; - case MSGPACK_TYPE_U16: - case MSGPACK_TYPE_S16: - seek_ptr += 2; - break; - case MSGPACK_TYPE_U32: - case MSGPACK_TYPE_S32: - seek_ptr += 4; - break; - case MSGPACK_TYPE_U64: - case MSGPACK_TYPE_S64: - seek_ptr += 8; - break; - - case MSGPACK_TYPE_STR8: - if (!get_raw_byte(buffer, true, (uint8_t*)&length)) return false; // Couldn't get str8 length - seek_ptr += length; - break; - case MSGPACK_TYPE_STR16: - if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get str16 length - seek_ptr += length; - break; - - case MSGPACK_TYPE_ARR16: - if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get arr16 length - for (c = 0; c < length; c++) - skip(buffer); - break; - - case MSGPACK_TYPE_MAP16: - if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get map16 length - for (c = 0; c < (length * 2); c++) - skip(buffer); - break; - - default: - return false; // Type unsupported - } - - return true; +bool MsgPack::init_search(const void* buffer, const size_t size) { + uint8_t byte; + uint16_t map_size; + + if (!size) return false; + buffer_size = size; + seek_ptr = 0; + if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_MAP16)) return false; // First record isn't a map16 + if (!get_raw_word(buffer, true, &map_size)) return false; // Couldn't get map16 size + if (!map_size) return false; + + return true; } -bool MsgPack::search_key(const void * buffer, const MsgPack::RecID record_id) { - uint8_t byte; - uint16_t key; - - while (get_raw_byte(buffer, false, &byte)) { - if (!get_u16(buffer, true, &key)) return false; // Couldn't get key - if (key == record_id) return true; // Found record - if (!skip(buffer)) return false; // Can't skip to next key - }; - return false; +bool MsgPack::skip(const void* buffer) { + uint8_t byte, c; + size_t length; + + if (!get_raw_byte(buffer, true, &byte)) return false; // Couldn't get type + + if (!(byte & 0x80)) return true; // Positive fixnum, already skipped by get_raw_byte + if ((byte & 0xE0) == 0xE0) return true; // Negative fixnum, already skipped by get_raw_byte + if ((byte & 0xE0) == 0xA0) { // Fixstr + seek_ptr += (byte & 0x1F); + return true; + } + if ((byte & 0xF0) == 0x80) { // Fixmap + length = (byte & 0x0F) * 2; + for (c = 0; c < length; c++) + skip(buffer); + return true; + } + if ((byte & 0xF0) == 0x90) { // Fixarray + length = byte & 0x0F; + for (c = 0; c < length; c++) + skip(buffer); + return true; + } + + switch (byte) { + case MSGPACK_NIL: + case MSGPACK_FALSE: + case MSGPACK_TRUE: // Already skipped by get_raw_byte + break; + case MSGPACK_TYPE_U8: + case MSGPACK_TYPE_S8: + seek_ptr++; + break; + case MSGPACK_TYPE_U16: + case MSGPACK_TYPE_S16: + seek_ptr += 2; + break; + case MSGPACK_TYPE_U32: + case MSGPACK_TYPE_S32: + seek_ptr += 4; + break; + case MSGPACK_TYPE_U64: + case MSGPACK_TYPE_S64: + seek_ptr += 8; + break; + + case MSGPACK_TYPE_STR8: + if (!get_raw_byte(buffer, true, (uint8_t*)&length)) return false; // Couldn't get str8 length + seek_ptr += length; + break; + case MSGPACK_TYPE_STR16: + if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get str16 length + seek_ptr += length; + break; + + case MSGPACK_TYPE_ARR16: + if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get arr16 length + for (c = 0; c < length; c++) + skip(buffer); + break; + + case MSGPACK_TYPE_MAP16: + if (!get_raw_word(buffer, true, (uint16_t*)&length)) return false; // Couldn't get map16 length + for (c = 0; c < (length * 2); c++) + skip(buffer); + break; + + default: + return false; // Type unsupported + } + + return true; } -bool MsgPack::msgpack_get(const void * buffer, const size_t size, const RecID record_id, bool * value) { - init_search(buffer, size); - if (!search_key(buffer, record_id)) return false; // Record not found - if (!get_bool(buffer, false, value)) return false; // Value isn't a bool - - return true; +bool MsgPack::search_key(const void* buffer, const MsgPack::RecID record_id) { + uint8_t byte; + uint16_t key; + + while (get_raw_byte(buffer, false, &byte)) { + if (!get_u16(buffer, true, &key)) return false; // Couldn't get key + if (key == record_id) return true; // Found record + if (!skip(buffer)) return false; // Can't skip to next key + }; + return false; } -bool MsgPack::msgpack_get(const void * buffer, const size_t size, const RecID record_id, uint8_t * value) { - if (!init_search(buffer, size)) return false; - if (!search_key(buffer, record_id)) return false; // Record not found - if (!get_u8(buffer, false, value)) return false; // Value isn't a u8 - - return true; +bool MsgPack::msgpack_get(const void* buffer, const size_t size, const RecID record_id, bool* value) { + init_search(buffer, size); + if (!search_key(buffer, record_id)) return false; // Record not found + if (!get_bool(buffer, false, value)) return false; // Value isn't a bool + + return true; } -bool MsgPack::msgpack_get(const void * buffer, const size_t size, const RecID record_id, int64_t * value) { - uint8_t byte; - - init_search(buffer, size); - if (!search_key(buffer, record_id)) return false; // Record not found - - if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer - if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_S64)) return false; // Value isn't a s64 - *value = ((int64_t)((uint8_t*)buffer)[seek_ptr] << 56) | ((int64_t)((uint8_t*)buffer)[seek_ptr + 1] << 48) | - ((int64_t)((uint8_t*)buffer)[seek_ptr + 2] << 40) | ((int64_t)((uint8_t*)buffer)[seek_ptr + 3] << 32) | - (((uint8_t*)buffer)[seek_ptr + 4] << 24) | (((uint8_t*)buffer)[seek_ptr + 5] << 16) | - (((uint8_t*)buffer)[seek_ptr + 6] << 8) | ((uint8_t*)buffer)[seek_ptr + 7]; - - return true; +bool MsgPack::msgpack_get(const void* buffer, const size_t size, const RecID record_id, uint8_t* value) { + if (!init_search(buffer, size)) return false; + if (!search_key(buffer, record_id)) return false; // Record not found + if (!get_u8(buffer, false, value)) return false; // Value isn't a u8 + + return true; } -bool MsgPack::msgpack_get(const void * buffer, const size_t size, const RecID record_id, std::string& value) { - init_search(buffer, size); - if (!search_key(buffer, record_id)) return false; // Record not found - if (!get_string(buffer, false, value)) return false; // Value isn't a char array - - return true; +bool MsgPack::msgpack_get(const void* buffer, const size_t size, const RecID record_id, int64_t* value) { + uint8_t byte; + + init_search(buffer, size); + if (!search_key(buffer, record_id)) return false; // Record not found + + if ((seek_ptr + 3) >= buffer_size) return false; // End of buffer + if ((get_raw_byte(buffer, true, &byte)) && (byte != MSGPACK_TYPE_S64)) return false; // Value isn't a s64 + *value = ((int64_t)((uint8_t*)buffer)[seek_ptr] << 56) | ((int64_t)((uint8_t*)buffer)[seek_ptr + 1] << 48) | + ((int64_t)((uint8_t*)buffer)[seek_ptr + 2] << 40) | ((int64_t)((uint8_t*)buffer)[seek_ptr + 3] << 32) | + (((uint8_t*)buffer)[seek_ptr + 4] << 24) | (((uint8_t*)buffer)[seek_ptr + 5] << 16) | + (((uint8_t*)buffer)[seek_ptr + 6] << 8) | ((uint8_t*)buffer)[seek_ptr + 7]; + + return true; } +bool MsgPack::msgpack_get(const void* buffer, const size_t size, const RecID record_id, std::string& value) { + init_search(buffer, size); + if (!search_key(buffer, record_id)) return false; // Record not found + if (!get_string(buffer, false, value)) return false; // Value isn't a char array + + return true; +} +void MsgPack::msgpack_init(const void* buffer, size_t* ptr) { + ((uint8_t*)buffer)[0] = MSGPACK_TYPE_MAP16; + ((uint8_t*)buffer)[1] = 0; + ((uint8_t*)buffer)[2] = 0; -void MsgPack::msgpack_init(const void * buffer, size_t * ptr) { - ((uint8_t*)buffer)[0] = MSGPACK_TYPE_MAP16; - ((uint8_t*)buffer)[1] = 0; - ((uint8_t*)buffer)[2] = 0; - - *ptr = 3; + *ptr = 3; } -void MsgPack::add_key(const void * buffer, size_t * ptr, const RecID record_id) { - uint16_t key; - - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_U16; - ((uint8_t*)buffer)[(*ptr)++] = record_id >> 8; - ((uint8_t*)buffer)[(*ptr)++] = record_id & 0xFF; - - // Auto-inc MAP16 size which should be at the beginning of the buffer - - key = (((uint8_t*)buffer)[1] << 8) | ((uint8_t*)buffer)[2]; - key++; - - ((uint8_t*)buffer)[1] = key >> 8; - ((uint8_t*)buffer)[2] = key & 0xFF; +void MsgPack::add_key(const void* buffer, size_t* ptr, const RecID record_id) { + uint16_t key; + + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_U16; + ((uint8_t*)buffer)[(*ptr)++] = record_id >> 8; + ((uint8_t*)buffer)[(*ptr)++] = record_id & 0xFF; + + // Auto-inc MAP16 size which should be at the beginning of the buffer + + key = (((uint8_t*)buffer)[1] << 8) | ((uint8_t*)buffer)[2]; + key++; + + ((uint8_t*)buffer)[1] = key >> 8; + ((uint8_t*)buffer)[2] = key & 0xFF; } -void MsgPack::msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, bool value) { - add_key(buffer, ptr, record_id); - - if (value) - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TRUE; - else - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_FALSE; +void MsgPack::msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, bool value) { + add_key(buffer, ptr, record_id); + + if (value) + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TRUE; + else + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_FALSE; } -void MsgPack::msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, uint8_t value) { - add_key(buffer, ptr, record_id); - - if (value < 128) { - ((uint8_t*)buffer)[(*ptr)++] = value; - } else { - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_U8; - ((uint8_t*)buffer)[(*ptr)++] = value; - } +void MsgPack::msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, uint8_t value) { + add_key(buffer, ptr, record_id); + + if (value < 128) { + ((uint8_t*)buffer)[(*ptr)++] = value; + } else { + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_U8; + ((uint8_t*)buffer)[(*ptr)++] = value; + } } -void MsgPack::msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, int64_t value) { - uint8_t c; - - add_key(buffer, ptr, record_id); - - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_S64; - - for (c = 0; c < 8; c++) - ((uint8_t*)buffer)[(*ptr)++] = (value >> (8 * (7 - c))) & 0xFF; +void MsgPack::msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, int64_t value) { + uint8_t c; + + add_key(buffer, ptr, record_id); + + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_S64; + + for (c = 0; c < 8; c++) + ((uint8_t*)buffer)[(*ptr)++] = (value >> (8 * (7 - c))) & 0xFF; } -bool MsgPack::msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, std::string value) { - uint8_t c; - size_t length; - - add_key(buffer, ptr, record_id); - - length = value.size(); - - if (length < 32) { - ((uint8_t*)buffer)[(*ptr)++] = length | 0xA0; // Fixstr - } else if ((length >= 32) && (length < 256)) { - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_STR8; - ((uint8_t*)buffer)[(*ptr)++] = length; - } else if ((length >= 256) && (length < 65536)) { - ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_STR16; - ((uint8_t*)buffer)[(*ptr)++] = length >> 8; - ((uint8_t*)buffer)[(*ptr)++] = length & 0xFF; - } else { - return false; - } - - for (c = 0; c < length; c++) - ((uint8_t*)buffer)[(*ptr)++] = value[c]; - - return true; +bool MsgPack::msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, std::string value) { + uint8_t c; + size_t length; + + add_key(buffer, ptr, record_id); + + length = value.size(); + + if (length < 32) { + ((uint8_t*)buffer)[(*ptr)++] = length | 0xA0; // Fixstr + } else if ((length >= 32) && (length < 256)) { + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_STR8; + ((uint8_t*)buffer)[(*ptr)++] = length; + } else if ((length >= 256) && (length < 65536)) { + ((uint8_t*)buffer)[(*ptr)++] = MSGPACK_TYPE_STR16; + ((uint8_t*)buffer)[(*ptr)++] = length >> 8; + ((uint8_t*)buffer)[(*ptr)++] = length & 0xFF; + } else { + return false; + } + + for (c = 0; c < length; c++) + ((uint8_t*)buffer)[(*ptr)++] = value[c]; + + return true; } diff --git a/firmware/common/msgpack.hpp b/firmware/common/msgpack.hpp index a857819ef..064e6d2cd 100644 --- a/firmware/common/msgpack.hpp +++ b/firmware/common/msgpack.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -27,75 +27,74 @@ #include #include -#define MSGPACK_NIL 0xC0 +#define MSGPACK_NIL 0xC0 -#define MSGPACK_FALSE 0xC2 -#define MSGPACK_TRUE 0xC3 +#define MSGPACK_FALSE 0xC2 +#define MSGPACK_TRUE 0xC3 -#define MSGPACK_TYPE_F32 0xCA -#define MSGPACK_TYPE_F64 0xCB +#define MSGPACK_TYPE_F32 0xCA +#define MSGPACK_TYPE_F64 0xCB -#define MSGPACK_TYPE_U8 0xCC -#define MSGPACK_TYPE_U16 0xCD -#define MSGPACK_TYPE_U32 0xCE -#define MSGPACK_TYPE_U64 0xCF +#define MSGPACK_TYPE_U8 0xCC +#define MSGPACK_TYPE_U16 0xCD +#define MSGPACK_TYPE_U32 0xCE +#define MSGPACK_TYPE_U64 0xCF -#define MSGPACK_TYPE_S8 0xD0 -#define MSGPACK_TYPE_S16 0xD1 -#define MSGPACK_TYPE_S32 0xD2 -#define MSGPACK_TYPE_S64 0xD3 +#define MSGPACK_TYPE_S8 0xD0 +#define MSGPACK_TYPE_S16 0xD1 +#define MSGPACK_TYPE_S32 0xD2 +#define MSGPACK_TYPE_S64 0xD3 -#define MSGPACK_TYPE_STR8 0xD9 -#define MSGPACK_TYPE_STR16 0xDA -#define MSGPACK_TYPE_STR32 0xDB +#define MSGPACK_TYPE_STR8 0xD9 +#define MSGPACK_TYPE_STR16 0xDA +#define MSGPACK_TYPE_STR32 0xDB -#define MSGPACK_TYPE_ARR16 0xDC -#define MSGPACK_TYPE_ARR32 0xDD +#define MSGPACK_TYPE_ARR16 0xDC +#define MSGPACK_TYPE_ARR32 0xDD -#define MSGPACK_TYPE_MAP16 0xDE -#define MSGPACK_TYPE_MAP32 0xDF +#define MSGPACK_TYPE_MAP16 0xDE +#define MSGPACK_TYPE_MAP32 0xDF class MsgPack { -public: - - enum RecID { - TestListA = 0, - TestListB = 1, - TestListC = 2, - TestListD = 3, - TestListE = 4 - }; - - // Read - bool msgpack_get(const void * buffer, const size_t size, const RecID record_id, bool * value); - bool msgpack_get(const void * buffer, const size_t size, const RecID record_id, uint8_t * value); - bool msgpack_get(const void * buffer, const size_t size, const RecID record_id, int64_t * value); - bool msgpack_get(const void * buffer, const size_t size, const RecID record_id, std::string& value); - - // Write - void msgpack_init(const void * buffer, size_t * ptr); - void msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, bool value); - void msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, uint8_t value); - void msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, int64_t value); - bool msgpack_add(const void * buffer, size_t * ptr, const RecID record_id, std::string value); - -private: - bool get_raw_byte(const void * buffer, const bool inc, uint8_t * byte); - bool get_raw_word(const void * buffer, const bool inc, uint16_t * word); - bool get_bool(const void * buffer, const bool inc, bool * value); - bool get_u8(const void * buffer, const bool inc, uint8_t * value); - bool get_u16(const void * buffer, const bool inc, uint16_t * value); - bool get_s32(const void * buffer, const bool inc, int32_t * value); - bool get_string(const void * buffer, const bool inc, std::string& value); - - void add_key(const void * buffer, size_t * ptr, const RecID record_id); - - bool init_search(const void * buffer, const size_t size); - bool search_key(const void * buffer, const RecID record_id); - bool skip(const void * buffer); - - size_t seek_ptr = 0; - size_t buffer_size; + public: + enum RecID { + TestListA = 0, + TestListB = 1, + TestListC = 2, + TestListD = 3, + TestListE = 4 + }; + + // Read + bool msgpack_get(const void* buffer, const size_t size, const RecID record_id, bool* value); + bool msgpack_get(const void* buffer, const size_t size, const RecID record_id, uint8_t* value); + bool msgpack_get(const void* buffer, const size_t size, const RecID record_id, int64_t* value); + bool msgpack_get(const void* buffer, const size_t size, const RecID record_id, std::string& value); + + // Write + void msgpack_init(const void* buffer, size_t* ptr); + void msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, bool value); + void msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, uint8_t value); + void msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, int64_t value); + bool msgpack_add(const void* buffer, size_t* ptr, const RecID record_id, std::string value); + + private: + bool get_raw_byte(const void* buffer, const bool inc, uint8_t* byte); + bool get_raw_word(const void* buffer, const bool inc, uint16_t* word); + bool get_bool(const void* buffer, const bool inc, bool* value); + bool get_u8(const void* buffer, const bool inc, uint8_t* value); + bool get_u16(const void* buffer, const bool inc, uint16_t* value); + bool get_s32(const void* buffer, const bool inc, int32_t* value); + bool get_string(const void* buffer, const bool inc, std::string& value); + + void add_key(const void* buffer, size_t* ptr, const RecID record_id); + + bool init_search(const void* buffer, const size_t size); + bool search_key(const void* buffer, const RecID record_id); + bool skip(const void* buffer); + + size_t seek_ptr = 0; + size_t buffer_size; }; #endif diff --git a/firmware/common/optional.hpp b/firmware/common/optional.hpp index a6ca3ecf6..efe03c9de 100644 --- a/firmware/common/optional.hpp +++ b/firmware/common/optional.hpp @@ -24,19 +24,22 @@ #include -template +template class Optional { -public: - constexpr Optional() : value_ { }, valid_ { false } { }; - constexpr Optional(const T& value) : value_ { value }, valid_ { true } { }; - constexpr Optional(T&& value) : value_ { std::move(value) }, valid_ { true } { }; + public: + constexpr Optional() + : value_{}, valid_{false} {}; + constexpr Optional(const T& value) + : value_{value}, valid_{true} {}; + constexpr Optional(T&& value) + : value_{std::move(value)}, valid_{true} {}; - bool is_valid() const { return valid_; }; - T value() const { return value_; }; + bool is_valid() const { return valid_; }; + T value() const { return value_; }; -private: - T value_; - bool valid_; + private: + T value_; + bool valid_; }; -#endif/*__OPTIONAL_H__*/ +#endif /*__OPTIONAL_H__*/ diff --git a/firmware/common/performance_counter.cpp b/firmware/common/performance_counter.cpp index d5de2ebcb..aca456f41 100644 --- a/firmware/common/performance_counter.cpp +++ b/firmware/common/performance_counter.cpp @@ -23,35 +23,35 @@ #include "ch.h" uint8_t get_cpu_utilisation_in_percent() { - static systime_t last_time = 0; - static systime_t last_idle_ticks = 0; + static systime_t last_time = 0; + static systime_t last_idle_ticks = 0; - auto now = chTimeNow(); - auto idle_ticks = chThdGetTicks(chSysGetIdleThread()); + auto now = chTimeNow(); + auto idle_ticks = chThdGetTicks(chSysGetIdleThread()); - if (last_time == 0) { - last_time = now; - last_idle_ticks = idle_ticks; + if (last_time == 0) { + last_time = now; + last_idle_ticks = idle_ticks; - return 0; - } + return 0; + } - int32_t time_elapsed = now - last_time; - int32_t idle_elapsed = idle_ticks - last_idle_ticks; + int32_t time_elapsed = now - last_time; + int32_t idle_elapsed = idle_ticks - last_idle_ticks; - int32_t working_ticks = time_elapsed - idle_elapsed; + int32_t working_ticks = time_elapsed - idle_elapsed; - if (working_ticks < 0) - working_ticks = 0; + if (working_ticks < 0) + working_ticks = 0; - auto utilisation = working_ticks * 100 / time_elapsed; + auto utilisation = working_ticks * 100 / time_elapsed; - last_time = now; - last_idle_ticks = idle_ticks; + last_time = now; + last_idle_ticks = idle_ticks; - if (utilisation > 100) { - return 100; - } + if (utilisation > 100) { + return 100; + } - return (uint8_t) utilisation; + return (uint8_t)utilisation; } diff --git a/firmware/common/pins.hpp b/firmware/common/pins.hpp index bbb74d017..ca45d1bea 100644 --- a/firmware/common/pins.hpp +++ b/firmware/common/pins.hpp @@ -27,207 +27,353 @@ namespace lpc43xx { enum Pins { - P0_0, P0_1, - P1_0, P1_1, P1_2, P1_3, P1_4, P1_5, P1_6, P1_7, P1_8, P1_9, P1_10, P1_11, P1_12, P1_13, P1_14, P1_15, P1_16, P1_17, P1_18, P1_19, P1_20, - P2_0, P2_1, P2_2, P2_3, P2_4, P2_5, P2_6, P2_7, P2_8, P2_9, P2_10, P2_11, P2_12, P2_13, - P3_0, P3_1, P3_2, - P4_0, P4_1, P4_2, P4_3, P4_4, P4_5, P4_6, P4_7, P4_8, P4_9, P4_10, - P5_0, P5_1, P5_2, P5_3, P5_4, P5_5, P5_6, P5_7, - P6_0, P6_1, P6_2, P6_3, P6_4, P6_5, P6_6, P6_7, P6_8, P6_9, P6_10, P6_11, P6_12, - P7_0, P7_1, P7_2, P7_3, P7_4, P7_5, P7_6, P7_7, - P9_5, P9_6, - PF_4, - CLK0, CLK2, + P0_0, + P0_1, + P1_0, + P1_1, + P1_2, + P1_3, + P1_4, + P1_5, + P1_6, + P1_7, + P1_8, + P1_9, + P1_10, + P1_11, + P1_12, + P1_13, + P1_14, + P1_15, + P1_16, + P1_17, + P1_18, + P1_19, + P1_20, + P2_0, + P2_1, + P2_2, + P2_3, + P2_4, + P2_5, + P2_6, + P2_7, + P2_8, + P2_9, + P2_10, + P2_11, + P2_12, + P2_13, + P3_0, + P3_1, + P3_2, + P4_0, + P4_1, + P4_2, + P4_3, + P4_4, + P4_5, + P4_6, + P4_7, + P4_8, + P4_9, + P4_10, + P5_0, + P5_1, + P5_2, + P5_3, + P5_4, + P5_5, + P5_6, + P5_7, + P6_0, + P6_1, + P6_2, + P6_3, + P6_4, + P6_5, + P6_6, + P6_7, + P6_8, + P6_9, + P6_10, + P6_11, + P6_12, + P7_0, + P7_1, + P7_2, + P7_3, + P7_4, + P7_5, + P7_6, + P7_7, + P9_5, + P9_6, + PF_4, + CLK0, + CLK2, }; -constexpr Pin pins[] { - [P0_0] = { 0, 0}, - [P0_1] = { 0, 1}, - [P1_0] = { 1, 0}, - [P1_1] = { 1, 1}, - [P1_2] = { 1, 2}, - [P1_3] = { 1, 3}, - [P1_4] = { 1, 4}, - [P1_5] = { 1, 5}, - [P1_6] = { 1, 6}, - [P1_7] = { 1, 7}, - [P1_8] = { 1, 8}, - [P1_9] = { 1, 9}, - [P1_10] = { 1, 10}, - [P1_11] = { 1, 11}, - [P1_12] = { 1, 12}, - [P1_13] = { 1, 13}, - [P1_14] = { 1, 14}, - [P1_15] = { 1, 15}, - [P1_16] = { 1, 16}, - [P1_17] = { 1, 17}, - [P1_18] = { 1, 18}, - [P1_19] = { 1, 19}, - [P1_20] = { 1, 20}, - [P2_0] = { 2, 0}, - [P2_1] = { 2, 1}, - [P2_2] = { 2, 2}, - [P2_3] = { 2, 3}, - [P2_4] = { 2, 4}, - [P2_5] = { 2, 5}, - [P2_6] = { 2, 6}, - [P2_7] = { 2, 7}, - [P2_8] = { 2, 8}, - [P2_9] = { 2, 9}, - [P2_10] = { 2, 10}, - [P2_11] = { 2, 11}, - [P2_12] = { 2, 12}, - [P2_13] = { 2, 13}, - [P3_0] = { 3, 0}, - [P3_1] = { 3, 1}, - [P3_2] = { 3, 2}, - [P4_0] = { 4, 0}, - [P4_1] = { 4, 1}, - [P4_2] = { 4, 2}, - [P4_3] = { 4, 3}, - [P4_4] = { 4, 4}, - [P4_5] = { 4, 5}, - [P4_6] = { 4, 6}, - [P4_7] = { 4, 7}, - [P4_8] = { 4, 8}, - [P4_9] = { 4, 9}, - [P4_10] = { 4, 10}, - [P5_0] = { 5, 0}, - [P5_1] = { 5, 1}, - [P5_2] = { 5, 2}, - [P5_3] = { 5, 3}, - [P5_4] = { 5, 4}, - [P5_5] = { 5, 5}, - [P5_6] = { 5, 6}, - [P5_7] = { 5, 7}, - [P6_0] = { 6, 0}, - [P6_1] = { 6, 1}, - [P6_2] = { 6, 2}, - [P6_3] = { 6, 3}, - [P6_4] = { 6, 4}, - [P6_5] = { 6, 5}, - [P6_6] = { 6, 6}, - [P6_7] = { 6, 7}, - [P6_8] = { 6, 8}, - [P6_9] = { 6, 9}, - [P6_10] = { 6, 10}, - [P6_11] = { 6, 11}, - [P6_12] = { 6, 12}, - [P7_0] = { 7, 0}, - [P7_1] = { 7, 1}, - [P7_2] = { 7, 2}, - [P7_3] = { 7, 3}, - [P7_4] = { 7, 4}, - [P7_5] = { 7, 5}, - [P7_6] = { 7, 6}, - [P7_7] = { 7, 7}, - [P9_5] = { 9, 5}, - [P9_6] = { 9, 6}, - [PF_4] = { 15, 4}, - [CLK0] = { 24, 0}, - [CLK2] = { 24, 2}, +constexpr Pin pins[]{ + [P0_0] = {0, 0}, + [P0_1] = {0, 1}, + [P1_0] = {1, 0}, + [P1_1] = {1, 1}, + [P1_2] = {1, 2}, + [P1_3] = {1, 3}, + [P1_4] = {1, 4}, + [P1_5] = {1, 5}, + [P1_6] = {1, 6}, + [P1_7] = {1, 7}, + [P1_8] = {1, 8}, + [P1_9] = {1, 9}, + [P1_10] = {1, 10}, + [P1_11] = {1, 11}, + [P1_12] = {1, 12}, + [P1_13] = {1, 13}, + [P1_14] = {1, 14}, + [P1_15] = {1, 15}, + [P1_16] = {1, 16}, + [P1_17] = {1, 17}, + [P1_18] = {1, 18}, + [P1_19] = {1, 19}, + [P1_20] = {1, 20}, + [P2_0] = {2, 0}, + [P2_1] = {2, 1}, + [P2_2] = {2, 2}, + [P2_3] = {2, 3}, + [P2_4] = {2, 4}, + [P2_5] = {2, 5}, + [P2_6] = {2, 6}, + [P2_7] = {2, 7}, + [P2_8] = {2, 8}, + [P2_9] = {2, 9}, + [P2_10] = {2, 10}, + [P2_11] = {2, 11}, + [P2_12] = {2, 12}, + [P2_13] = {2, 13}, + [P3_0] = {3, 0}, + [P3_1] = {3, 1}, + [P3_2] = {3, 2}, + [P4_0] = {4, 0}, + [P4_1] = {4, 1}, + [P4_2] = {4, 2}, + [P4_3] = {4, 3}, + [P4_4] = {4, 4}, + [P4_5] = {4, 5}, + [P4_6] = {4, 6}, + [P4_7] = {4, 7}, + [P4_8] = {4, 8}, + [P4_9] = {4, 9}, + [P4_10] = {4, 10}, + [P5_0] = {5, 0}, + [P5_1] = {5, 1}, + [P5_2] = {5, 2}, + [P5_3] = {5, 3}, + [P5_4] = {5, 4}, + [P5_5] = {5, 5}, + [P5_6] = {5, 6}, + [P5_7] = {5, 7}, + [P6_0] = {6, 0}, + [P6_1] = {6, 1}, + [P6_2] = {6, 2}, + [P6_3] = {6, 3}, + [P6_4] = {6, 4}, + [P6_5] = {6, 5}, + [P6_6] = {6, 6}, + [P6_7] = {6, 7}, + [P6_8] = {6, 8}, + [P6_9] = {6, 9}, + [P6_10] = {6, 10}, + [P6_11] = {6, 11}, + [P6_12] = {6, 12}, + [P7_0] = {7, 0}, + [P7_1] = {7, 1}, + [P7_2] = {7, 2}, + [P7_3] = {7, 3}, + [P7_4] = {7, 4}, + [P7_5] = {7, 5}, + [P7_6] = {7, 6}, + [P7_7] = {7, 7}, + [P9_5] = {9, 5}, + [P9_6] = {9, 6}, + [PF_4] = {15, 4}, + [CLK0] = {24, 0}, + [CLK2] = {24, 2}, }; enum GPIOs { - GPIO0_0, GPIO0_1, GPIO0_2, GPIO0_3, GPIO0_4, GPIO0_5, /*GPIO0_6,*/ GPIO0_7, GPIO0_8, GPIO0_9, GPIO0_10, GPIO0_11, GPIO0_12, GPIO0_13, GPIO0_14, GPIO0_15, - GPIO1_0, GPIO1_1, GPIO1_2, GPIO1_3, GPIO1_4, GPIO1_5, GPIO1_6, GPIO1_7, GPIO1_8, GPIO1_9, GPIO1_10, GPIO1_11, GPIO1_12, GPIO1_13, /*GPIO1_14, GPIO1_15,*/ - GPIO2_0, GPIO2_1, GPIO2_2, GPIO2_3, GPIO2_4, GPIO2_5, GPIO2_6, GPIO2_7, GPIO2_8, GPIO2_9, GPIO2_10, GPIO2_11, GPIO2_12, GPIO2_13, GPIO2_14, GPIO2_15, - GPIO3_0, GPIO3_1, GPIO3_2, GPIO3_3, GPIO3_4, GPIO3_5, GPIO3_6, GPIO3_7, GPIO3_8, GPIO3_9, GPIO3_10, GPIO3_11, GPIO3_12, GPIO3_13, GPIO3_14, GPIO3_15, - GPIO4_11, - GPIO5_0, GPIO5_1, GPIO5_2, GPIO5_3, GPIO5_4, GPIO5_5, GPIO5_6, GPIO5_7, GPIO5_8, GPIO5_9, /*GPIO5_10, GPIO5_11,*/ GPIO5_12, GPIO5_13, GPIO5_14, GPIO5_15, GPIO5_16, GPIO5_18, + GPIO0_0, + GPIO0_1, + GPIO0_2, + GPIO0_3, + GPIO0_4, + GPIO0_5, + /*GPIO0_6,*/ GPIO0_7, + GPIO0_8, + GPIO0_9, + GPIO0_10, + GPIO0_11, + GPIO0_12, + GPIO0_13, + GPIO0_14, + GPIO0_15, + GPIO1_0, + GPIO1_1, + GPIO1_2, + GPIO1_3, + GPIO1_4, + GPIO1_5, + GPIO1_6, + GPIO1_7, + GPIO1_8, + GPIO1_9, + GPIO1_10, + GPIO1_11, + GPIO1_12, + GPIO1_13, /*GPIO1_14, GPIO1_15,*/ + GPIO2_0, + GPIO2_1, + GPIO2_2, + GPIO2_3, + GPIO2_4, + GPIO2_5, + GPIO2_6, + GPIO2_7, + GPIO2_8, + GPIO2_9, + GPIO2_10, + GPIO2_11, + GPIO2_12, + GPIO2_13, + GPIO2_14, + GPIO2_15, + GPIO3_0, + GPIO3_1, + GPIO3_2, + GPIO3_3, + GPIO3_4, + GPIO3_5, + GPIO3_6, + GPIO3_7, + GPIO3_8, + GPIO3_9, + GPIO3_10, + GPIO3_11, + GPIO3_12, + GPIO3_13, + GPIO3_14, + GPIO3_15, + GPIO4_11, + GPIO5_0, + GPIO5_1, + GPIO5_2, + GPIO5_3, + GPIO5_4, + GPIO5_5, + GPIO5_6, + GPIO5_7, + GPIO5_8, + GPIO5_9, + /*GPIO5_10, GPIO5_11,*/ GPIO5_12, + GPIO5_13, + GPIO5_14, + GPIO5_15, + GPIO5_16, + GPIO5_18, }; constexpr GPIO gpio[] = { - [GPIO0_0] = { pins[P0_0], 0, 0, 0 }, - [GPIO0_1] = { pins[P0_1], 0, 1, 0 }, - [GPIO0_2] = { pins[P1_15], 0, 2, 0 }, - [GPIO0_3] = { pins[P1_16], 0, 3, 0 }, - [GPIO0_4] = { pins[P1_0], 0, 4, 0 }, - [GPIO0_5] = { pins[P6_6], 0, 5, 0 }, - //[GPIO0_6] = { pins[P3_6], 0, 6, 0 }, - [GPIO0_7] = { pins[P2_7], 0, 7, 0 }, - [GPIO0_8] = { pins[P1_1], 0, 8, 0 }, - [GPIO0_9] = { pins[P1_2], 0, 9, 0 }, - [GPIO0_10] = { pins[P1_3], 0, 10, 0 }, - [GPIO0_11] = { pins[P1_4], 0, 11, 0 }, - [GPIO0_12] = { pins[P1_17], 0, 12, 0 }, - [GPIO0_13] = { pins[P1_18], 0, 13, 0 }, - [GPIO0_14] = { pins[P2_10], 0, 14, 0 }, - [GPIO0_15] = { pins[P1_20], 0, 15, 0 }, + [GPIO0_0] = {pins[P0_0], 0, 0, 0}, + [GPIO0_1] = {pins[P0_1], 0, 1, 0}, + [GPIO0_2] = {pins[P1_15], 0, 2, 0}, + [GPIO0_3] = {pins[P1_16], 0, 3, 0}, + [GPIO0_4] = {pins[P1_0], 0, 4, 0}, + [GPIO0_5] = {pins[P6_6], 0, 5, 0}, + //[GPIO0_6] = { pins[P3_6], 0, 6, 0 }, + [GPIO0_7] = {pins[P2_7], 0, 7, 0}, + [GPIO0_8] = {pins[P1_1], 0, 8, 0}, + [GPIO0_9] = {pins[P1_2], 0, 9, 0}, + [GPIO0_10] = {pins[P1_3], 0, 10, 0}, + [GPIO0_11] = {pins[P1_4], 0, 11, 0}, + [GPIO0_12] = {pins[P1_17], 0, 12, 0}, + [GPIO0_13] = {pins[P1_18], 0, 13, 0}, + [GPIO0_14] = {pins[P2_10], 0, 14, 0}, + [GPIO0_15] = {pins[P1_20], 0, 15, 0}, - [GPIO1_0] = { pins[P1_7], 1, 0, 0 }, - [GPIO1_1] = { pins[P1_8], 1, 1, 0 }, - [GPIO1_2] = { pins[P1_9], 1, 2, 0 }, - [GPIO1_3] = { pins[P1_10], 1, 3, 0 }, - [GPIO1_4] = { pins[P1_11], 1, 4, 0 }, - [GPIO1_5] = { pins[P1_12], 1, 5, 0 }, - [GPIO1_6] = { pins[P1_13], 1, 6, 0 }, - [GPIO1_7] = { pins[P1_14], 1, 7, 0 }, - [GPIO1_8] = { pins[P1_5], 1, 8, 0 }, - [GPIO1_9] = { pins[P1_6], 1, 9, 0 }, - [GPIO1_10] = { pins[P2_9], 1, 10, 0 }, - [GPIO1_11] = { pins[P2_11], 1, 11, 0 }, - [GPIO1_12] = { pins[P2_12], 1, 12, 0 }, - [GPIO1_13] = { pins[P2_13], 1, 13, 0 }, - //[GPIO1_14] = { pins[P3_4], 1, 14, 0 }, - //[GPIO1_15] = { pins[P3_5], 1, 15, 0 }, + [GPIO1_0] = {pins[P1_7], 1, 0, 0}, + [GPIO1_1] = {pins[P1_8], 1, 1, 0}, + [GPIO1_2] = {pins[P1_9], 1, 2, 0}, + [GPIO1_3] = {pins[P1_10], 1, 3, 0}, + [GPIO1_4] = {pins[P1_11], 1, 4, 0}, + [GPIO1_5] = {pins[P1_12], 1, 5, 0}, + [GPIO1_6] = {pins[P1_13], 1, 6, 0}, + [GPIO1_7] = {pins[P1_14], 1, 7, 0}, + [GPIO1_8] = {pins[P1_5], 1, 8, 0}, + [GPIO1_9] = {pins[P1_6], 1, 9, 0}, + [GPIO1_10] = {pins[P2_9], 1, 10, 0}, + [GPIO1_11] = {pins[P2_11], 1, 11, 0}, + [GPIO1_12] = {pins[P2_12], 1, 12, 0}, + [GPIO1_13] = {pins[P2_13], 1, 13, 0}, + //[GPIO1_14] = { pins[P3_4], 1, 14, 0 }, + //[GPIO1_15] = { pins[P3_5], 1, 15, 0 }, - [GPIO2_0] = { pins[P4_0], 2, 0, 0 }, - [GPIO2_1] = { pins[P4_1], 2, 1, 0 }, - [GPIO2_2] = { pins[P4_2], 2, 2, 0 }, - [GPIO2_3] = { pins[P4_3], 2, 3, 0 }, - [GPIO2_4] = { pins[P4_4], 2, 4, 0 }, - [GPIO2_5] = { pins[P4_5], 2, 5, 0 }, - [GPIO2_6] = { pins[P4_6], 2, 6, 0 }, - [GPIO2_7] = { pins[P5_7], 2, 7, 0 }, - [GPIO2_8] = { pins[P6_12], 2, 8, 0 }, - [GPIO2_9] = { pins[P5_0], 2, 9, 0 }, - [GPIO2_10] = { pins[P5_1], 2, 10, 0 }, - [GPIO2_11] = { pins[P5_2], 2, 11, 0 }, - [GPIO2_12] = { pins[P5_3], 2, 12, 0 }, - [GPIO2_13] = { pins[P5_4], 2, 13, 0 }, - [GPIO2_14] = { pins[P5_5], 2, 14, 0 }, - [GPIO2_15] = { pins[P5_6], 2, 15, 0 }, + [GPIO2_0] = {pins[P4_0], 2, 0, 0}, + [GPIO2_1] = {pins[P4_1], 2, 1, 0}, + [GPIO2_2] = {pins[P4_2], 2, 2, 0}, + [GPIO2_3] = {pins[P4_3], 2, 3, 0}, + [GPIO2_4] = {pins[P4_4], 2, 4, 0}, + [GPIO2_5] = {pins[P4_5], 2, 5, 0}, + [GPIO2_6] = {pins[P4_6], 2, 6, 0}, + [GPIO2_7] = {pins[P5_7], 2, 7, 0}, + [GPIO2_8] = {pins[P6_12], 2, 8, 0}, + [GPIO2_9] = {pins[P5_0], 2, 9, 0}, + [GPIO2_10] = {pins[P5_1], 2, 10, 0}, + [GPIO2_11] = {pins[P5_2], 2, 11, 0}, + [GPIO2_12] = {pins[P5_3], 2, 12, 0}, + [GPIO2_13] = {pins[P5_4], 2, 13, 0}, + [GPIO2_14] = {pins[P5_5], 2, 14, 0}, + [GPIO2_15] = {pins[P5_6], 2, 15, 0}, - [GPIO3_0] = { pins[P6_1], 3, 0, 0 }, - [GPIO3_1] = { pins[P6_2], 3, 1, 0 }, - [GPIO3_2] = { pins[P6_3], 3, 2, 0 }, - [GPIO3_3] = { pins[P6_4], 3, 3, 0 }, - [GPIO3_4] = { pins[P6_5], 3, 4, 0 }, - [GPIO3_5] = { pins[P6_9], 3, 5, 0 }, - [GPIO3_6] = { pins[P6_10], 3, 6, 0 }, - [GPIO3_7] = { pins[P6_11], 3, 7, 0 }, - [GPIO3_8] = { pins[P7_0], 3, 8, 0 }, - [GPIO3_9] = { pins[P7_1], 3, 9, 0 }, - [GPIO3_10] = { pins[P7_2], 3, 10, 0 }, - [GPIO3_11] = { pins[P7_3], 3, 11, 0 }, - [GPIO3_12] = { pins[P7_4], 3, 12, 0 }, - [GPIO3_13] = { pins[P7_5], 3, 13, 0 }, - [GPIO3_14] = { pins[P7_6], 3, 14, 0 }, - [GPIO3_15] = { pins[P7_7], 3, 15, 0 }, + [GPIO3_0] = {pins[P6_1], 3, 0, 0}, + [GPIO3_1] = {pins[P6_2], 3, 1, 0}, + [GPIO3_2] = {pins[P6_3], 3, 2, 0}, + [GPIO3_3] = {pins[P6_4], 3, 3, 0}, + [GPIO3_4] = {pins[P6_5], 3, 4, 0}, + [GPIO3_5] = {pins[P6_9], 3, 5, 0}, + [GPIO3_6] = {pins[P6_10], 3, 6, 0}, + [GPIO3_7] = {pins[P6_11], 3, 7, 0}, + [GPIO3_8] = {pins[P7_0], 3, 8, 0}, + [GPIO3_9] = {pins[P7_1], 3, 9, 0}, + [GPIO3_10] = {pins[P7_2], 3, 10, 0}, + [GPIO3_11] = {pins[P7_3], 3, 11, 0}, + [GPIO3_12] = {pins[P7_4], 3, 12, 0}, + [GPIO3_13] = {pins[P7_5], 3, 13, 0}, + [GPIO3_14] = {pins[P7_6], 3, 14, 0}, + [GPIO3_15] = {pins[P7_7], 3, 15, 0}, - [GPIO4_11] = { pins[P9_6], 4, 11, 0 }, + [GPIO4_11] = {pins[P9_6], 4, 11, 0}, - [GPIO5_0] = { pins[P2_0], 5, 0, 4 }, - [GPIO5_1] = { pins[P2_1], 5, 1, 4 }, - [GPIO5_2] = { pins[P2_2], 5, 2, 4 }, - [GPIO5_3] = { pins[P2_3], 5, 3, 4 }, - [GPIO5_4] = { pins[P2_4], 5, 4, 4 }, - [GPIO5_5] = { pins[P2_5], 5, 5, 4 }, - [GPIO5_6] = { pins[P2_6], 5, 6, 4 }, - [GPIO5_7] = { pins[P2_8], 5, 7, 4 }, - [GPIO5_8] = { pins[P3_1], 5, 8, 4 }, - [GPIO5_9] = { pins[P3_2], 5, 9, 4 }, - //[GPIO5_10] = { pins[P3_7], 5, 10, 4 }, - //[GPIO5_11] = { pins[P3_8], 5, 11, 4 }, - [GPIO5_12] = { pins[P4_8], 5, 12, 4 }, - [GPIO5_13] = { pins[P4_9], 5, 13, 4 }, - [GPIO5_14] = { pins[P4_10], 5, 14, 4 }, - [GPIO5_15] = { pins[P6_7], 5, 15, 4 }, - [GPIO5_16] = { pins[P6_8], 5, 16, 4 }, - [GPIO5_18] = { pins[P9_5], 5, 18, 4 }, + [GPIO5_0] = {pins[P2_0], 5, 0, 4}, + [GPIO5_1] = {pins[P2_1], 5, 1, 4}, + [GPIO5_2] = {pins[P2_2], 5, 2, 4}, + [GPIO5_3] = {pins[P2_3], 5, 3, 4}, + [GPIO5_4] = {pins[P2_4], 5, 4, 4}, + [GPIO5_5] = {pins[P2_5], 5, 5, 4}, + [GPIO5_6] = {pins[P2_6], 5, 6, 4}, + [GPIO5_7] = {pins[P2_8], 5, 7, 4}, + [GPIO5_8] = {pins[P3_1], 5, 8, 4}, + [GPIO5_9] = {pins[P3_2], 5, 9, 4}, + //[GPIO5_10] = { pins[P3_7], 5, 10, 4 }, + //[GPIO5_11] = { pins[P3_8], 5, 11, 4 }, + [GPIO5_12] = {pins[P4_8], 5, 12, 4}, + [GPIO5_13] = {pins[P4_9], 5, 13, 4}, + [GPIO5_14] = {pins[P4_10], 5, 14, 4}, + [GPIO5_15] = {pins[P6_7], 5, 15, 4}, + [GPIO5_16] = {pins[P6_8], 5, 16, 4}, + [GPIO5_18] = {pins[P9_5], 5, 18, 4}, }; -} +} // namespace lpc43xx -#endif/*__PINS_H__*/ +#endif /*__PINS_H__*/ diff --git a/firmware/common/png_writer.cpp b/firmware/common/png_writer.cpp index b714e322a..bc5aeb64c 100644 --- a/firmware/common/png_writer.cpp +++ b/firmware/common/png_writer.cpp @@ -21,112 +21,115 @@ #include "png_writer.hpp" -static constexpr std::array png_file_header { { - 0x89, 0x50, 0x4e, 0x47, - 0x0d, 0x0a, 0x1a, 0x0a, -} }; - -static constexpr std::array png_ihdr_screen_capture { { - 0x00, 0x00, 0x00, 0x0d, // IHDR length - 0x49, 0x48, 0x44, 0x52, // IHDR type - 0x00, 0x00, 0x00, 0xf0, // width = 240 - 0x00, 0x00, 0x01, 0x40, // height = 320 - 0x08, // bit_depth = 8 - 0x02, // color_type = 2 - 0x00, // compression_method = 0 - 0x00, // filter_method = 0 - 0x00, // interlace_method = 0 - 0x0d, 0x8a, 0x66, 0x04, // CRC -} }; - -static constexpr std::array png_idat_chunk_type { { - 0x49, 0x44, 0x41, 0x54, // IDAT type -} }; - -static constexpr std::array png_iend { { - 0x00, 0x00, 0x00, 0x00, // IEND length - 0x49, 0x45, 0x4e, 0x44, // IEND type - 0xae, 0x42, 0x60, 0x82, // CRC -} }; +static constexpr std::array png_file_header{{ + 0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, +}}; + +static constexpr std::array png_ihdr_screen_capture{{ + 0x00, 0x00, 0x00, 0x0d, // IHDR length + 0x49, 0x48, 0x44, 0x52, // IHDR type + 0x00, 0x00, 0x00, 0xf0, // width = 240 + 0x00, 0x00, 0x01, 0x40, // height = 320 + 0x08, // bit_depth = 8 + 0x02, // color_type = 2 + 0x00, // compression_method = 0 + 0x00, // filter_method = 0 + 0x00, // interlace_method = 0 + 0x0d, 0x8a, 0x66, 0x04, // CRC +}}; + +static constexpr std::array png_idat_chunk_type{{ + 0x49, 0x44, 0x41, 0x54, // IDAT type +}}; + +static constexpr std::array png_iend{{ + 0x00, 0x00, 0x00, 0x00, // IEND length + 0x49, 0x45, 0x4e, 0x44, // IEND type + 0xae, 0x42, 0x60, 0x82, // CRC +}}; Optional PNGWriter::create( - const std::filesystem::path& filename -) { - const auto create_error = file.create(filename); - if( create_error.is_valid() ) { - return create_error; - } - - file.write(png_file_header); - file.write(png_ihdr_screen_capture); - - write_chunk_header( - 2 + height * (5 + 1 + width * 3) + 4, - png_idat_chunk_type - ); - - constexpr std::array zlib_header { 0x78, 0x01 }; // Zlib CM, CINFO, FLG. - write_chunk_content(zlib_header); - - return { }; + const std::filesystem::path& filename) { + const auto create_error = file.create(filename); + if (create_error.is_valid()) { + return create_error; + } + + file.write(png_file_header); + file.write(png_ihdr_screen_capture); + + write_chunk_header( + 2 + height * (5 + 1 + width * 3) + 4, + png_idat_chunk_type); + + constexpr std::array zlib_header{0x78, 0x01}; // Zlib CM, CINFO, FLG. + write_chunk_content(zlib_header); + + return {}; } PNGWriter::~PNGWriter() { - write_chunk_content(adler_32.bytes()); - write_chunk_crc(); + write_chunk_content(adler_32.bytes()); + write_chunk_crc(); - file.write(png_iend); + file.write(png_iend); } void PNGWriter::write_scanline(const std::array& scanline) { - constexpr uint8_t scanline_filter_type = 0; - constexpr uint32_t deflate_block_length = 1 + sizeof(scanline); - - const std::array deflate_and_scanline_header { - static_cast((scanline_count == (height - 1)) ? 0x01 : 0x00), // DEFLATE header bits, bfinal=0, btype=00 - static_cast((deflate_block_length >> 0) & 0xff), // Length LSB - static_cast((deflate_block_length >> 8) & 0xff), // Length MSB - static_cast((deflate_block_length >> 0) & 0xff) ^ 0xff, // ~Length LSB - static_cast((deflate_block_length >> 8) & 0xff) ^ 0xff, // ~Length MSB - scanline_filter_type, - }; - write_chunk_content(deflate_and_scanline_header); - - adler_32.feed(scanline_filter_type); - adler_32.feed(scanline); - - // Small writes to avoid some sort of large-transfer plus block - // boundary FatFs or SDC driver bug? - write_chunk_content(&scanline[ 0], 80 * sizeof(ui::ColorRGB888)); - write_chunk_content(&scanline[ 80], 80 * sizeof(ui::ColorRGB888)); - write_chunk_content(&scanline[160], 80 * sizeof(ui::ColorRGB888)); - - scanline_count++; + constexpr uint8_t scanline_filter_type = 0; + constexpr uint32_t deflate_block_length = 1 + sizeof(scanline); + + const std::array deflate_and_scanline_header{ + static_cast((scanline_count == (height - 1)) ? 0x01 : 0x00), // DEFLATE header bits, bfinal=0, btype=00 + static_cast((deflate_block_length >> 0) & 0xff), // Length LSB + static_cast((deflate_block_length >> 8) & 0xff), // Length MSB + static_cast((deflate_block_length >> 0) & 0xff) ^ 0xff, // ~Length LSB + static_cast((deflate_block_length >> 8) & 0xff) ^ 0xff, // ~Length MSB + scanline_filter_type, + }; + write_chunk_content(deflate_and_scanline_header); + + adler_32.feed(scanline_filter_type); + adler_32.feed(scanline); + + // Small writes to avoid some sort of large-transfer plus block + // boundary FatFs or SDC driver bug? + write_chunk_content(&scanline[0], 80 * sizeof(ui::ColorRGB888)); + write_chunk_content(&scanline[80], 80 * sizeof(ui::ColorRGB888)); + write_chunk_content(&scanline[160], 80 * sizeof(ui::ColorRGB888)); + + scanline_count++; } void PNGWriter::write_chunk_header( - const size_t length, - const std::array& type -) { - write_uint32_be(length); - crc.reset(); - write_chunk_content(type); + const size_t length, + const std::array& type) { + write_uint32_be(length); + crc.reset(); + write_chunk_content(type); } void PNGWriter::write_chunk_content(const void* const p, const size_t count) { - file.write(p, count); - crc.process_bytes(p, count); + file.write(p, count); + crc.process_bytes(p, count); } void PNGWriter::write_chunk_crc() { - write_uint32_be(crc.checksum()); + write_uint32_be(crc.checksum()); } void PNGWriter::write_uint32_be(const uint32_t v) { - file.write(std::array { { - static_cast((v >> 24) & 0xff), - static_cast((v >> 16) & 0xff), - static_cast((v >> 8) & 0xff), - static_cast((v >> 0) & 0xff), - } }); + file.write(std::array{{ + static_cast((v >> 24) & 0xff), + static_cast((v >> 16) & 0xff), + static_cast((v >> 8) & 0xff), + static_cast((v >> 0) & 0xff), + }}); } diff --git a/firmware/common/png_writer.hpp b/firmware/common/png_writer.hpp index bf0fcc8a2..348784f9f 100644 --- a/firmware/common/png_writer.hpp +++ b/firmware/common/png_writer.hpp @@ -32,33 +32,33 @@ #include "crc.hpp" class PNGWriter { -public: - ~PNGWriter(); + public: + ~PNGWriter(); - Optional create(const std::filesystem::path& filename); - - void write_scanline(const std::array& scanline); + Optional create(const std::filesystem::path& filename); -private: - // TODO: These constants are baked in a few places, do not change blithely. - static constexpr int width { 240 }; - static constexpr int height { 320 }; + void write_scanline(const std::array& scanline); - File file { }; - int scanline_count { 0 }; - CRC<32, true, true> crc { 0x04c11db7, 0xffffffff, 0xffffffff }; - Adler32 adler_32 { }; + private: + // TODO: These constants are baked in a few places, do not change blithely. + static constexpr int width{240}; + static constexpr int height{320}; - void write_chunk_header(const size_t length, const std::array& type); - void write_chunk_content(const void* const p, const size_t count); + File file{}; + int scanline_count{0}; + CRC<32, true, true> crc{0x04c11db7, 0xffffffff, 0xffffffff}; + Adler32 adler_32{}; - template - void write_chunk_content(const std::array& data) { - write_chunk_content(data.data(), sizeof(data)); - } + void write_chunk_header(const size_t length, const std::array& type); + void write_chunk_content(const void* const p, const size_t count); - void write_chunk_crc(); - void write_uint32_be(const uint32_t v); + template + void write_chunk_content(const std::array& data) { + write_chunk_content(data.data(), sizeof(data)); + } + + void write_chunk_crc(); + void write_uint32_be(const uint32_t v); }; -#endif/*__PNG_WRITER_H__*/ +#endif /*__PNG_WRITER_H__*/ diff --git a/firmware/common/pocsag.cpp b/firmware/common/pocsag.cpp index 2fb87660c..a5e5d3c60 100644 --- a/firmware/common/pocsag.cpp +++ b/firmware/common/pocsag.cpp @@ -32,437 +32,423 @@ using namespace portapack; namespace pocsag { std::string bitrate_str(BitRate bitrate) { - switch (bitrate) { - case BitRate::FSK512: return "512bps "; - case BitRate::FSK1200: return "1200bps"; - case BitRate::FSK2400: return "2400bps"; - case BitRate::FSK3200: return "3200bps"; - default: return "????"; - } + switch (bitrate) { + case BitRate::FSK512: + return "512bps "; + case BitRate::FSK1200: + return "1200bps"; + case BitRate::FSK2400: + return "2400bps"; + case BitRate::FSK3200: + return "3200bps"; + default: + return "????"; + } } std::string flag_str(PacketFlag packetflag) { - switch (packetflag) { - case PacketFlag::NORMAL: return "OK"; - case PacketFlag::TIMED_OUT: return "TIMED OUT"; - default: return ""; - } + switch (packetflag) { + case PacketFlag::NORMAL: + return "OK"; + case PacketFlag::TIMED_OUT: + return "TIMED OUT"; + default: + return ""; + } } -void insert_BCH(BCHCode& BCH_code, uint32_t * codeword) { - uint32_t parity = 0; - int data[21]; - int bit; - int * bb; - size_t c; - - for (c = 0; c < 21; c++) { - bit = (((*codeword) << c) & 0x80000000U) ? 1 : 0; - if (bit) parity++; - data[c] = bit; - } - - bb = BCH_code.encode(data); - - // Make sure ECC bits are cleared - (*codeword) &= 0xFFFFF801; - - for (c = 0; c < 10; c++) { - bit = bb[c]; - (*codeword) |= (bit << (10 - c)); - if (bit) parity++; - } - - // Even parity - (*codeword) |= (parity & 1); +void insert_BCH(BCHCode& BCH_code, uint32_t* codeword) { + uint32_t parity = 0; + int data[21]; + int bit; + int* bb; + size_t c; + + for (c = 0; c < 21; c++) { + bit = (((*codeword) << c) & 0x80000000U) ? 1 : 0; + if (bit) parity++; + data[c] = bit; + } + + bb = BCH_code.encode(data); + + // Make sure ECC bits are cleared + (*codeword) &= 0xFFFFF801; + + for (c = 0; c < 10; c++) { + bit = bb[c]; + (*codeword) |= (bit << (10 - c)); + if (bit) parity++; + } + + // Even parity + (*codeword) |= (parity & 1); } uint32_t get_digit_code(char code) { - if ((code >= '0') && (code <= '9')) { - code -= '0'; - } else { - if (code == 'S') - code = 10; - else if (code == 'U') - code = 11; - else if (code == ' ') - code = 12; - else if (code == '-') - code = 13; - else if (code == ']') - code = 14; - else if (code == '[') - code = 15; - else - code = 12; - } - - code = ((code & 0x0C) >> 2) | ((code & 0x03) << 2); // ----3210 -> ----1032 - code = ((code & 0x0A) >> 1) | ((code & 0x05) << 1); // ----1032 -> ----0123 - - return code; -} - -void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t function, const std::string message, const uint32_t address, - std::vector& codewords) { - - size_t b, c, address_slot; - size_t bit_idx, char_idx = 0; - uint32_t codeword, digit_code; - char ascii_char = 0; - - size_t message_size = message.size(); - - // Preamble - for (b = 0; b < (POCSAG_PREAMBLE_LENGTH / 32); b++) { - codewords.push_back(0xAAAAAAAA); - } - - // Address - codeword = (address & 0x1FFFF8U) << 10; - address_slot = (address & 7) * 2; - // Function - codeword |= (function << 11); - - insert_BCH(BCH_code, &codeword); - - // Address batch - codewords.push_back(POCSAG_SYNCWORD); - for (c = 0; c < 16; c++) { - if (c == address_slot) { - codewords.push_back(codeword); - if (type != MessageType::ADDRESS_ONLY) break; - } else - codewords.push_back(POCSAG_IDLEWORD); - } - - if (type == MessageType::ADDRESS_ONLY) return; // Done. - - c++; - codeword = 0; - bit_idx = 20 + 11; - - // Messages batch(es) - do { - if (c == 0) codewords.push_back(POCSAG_SYNCWORD); - - for ( ; c < 16; c++) { - - // Fill up 20 bits - if (type == MessageType::ALPHANUMERIC) { - if ((char_idx < message_size) || (ascii_char)) { - do { - bit_idx -= 7; - - if (char_idx < message_size) - ascii_char = message[char_idx] & 0x7F; - else - ascii_char = 0; // Codeword padding - - // Bottom's up - ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4; // *6543210 -> 3210*654 - ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2; // 3210*654 -> 103254*6 - ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55); // 103254*6 -> *0123456 - - codeword |= (ascii_char << bit_idx); - - char_idx++; - - } while (bit_idx > 11); - - codeword &= 0x7FFFF800; // Trim data - codeword |= 0x80000000; // Message type - insert_BCH(BCH_code, &codeword); - - codewords.push_back(codeword); - - if (bit_idx != 11) { - bit_idx = 20 + bit_idx; - codeword = ascii_char << bit_idx; - } else { - bit_idx = 20 + 11; - codeword = 0; - } - } else { - codewords.push_back(POCSAG_IDLEWORD); // Batch padding - } - } else if (type == MessageType::NUMERIC_ONLY) { - if (char_idx < message_size) { - do { - bit_idx -= 4; - - if (char_idx < message_size) - digit_code = get_digit_code(message[char_idx]); - else - digit_code = 3; // Space (codeword padding) - - codeword |= (digit_code << bit_idx); - - char_idx++; - - } while (bit_idx > 11); - - codeword |= 0x80000000; // Message type - insert_BCH(BCH_code, &codeword); - - codewords.push_back(codeword); - - bit_idx = 20 + 11; - codeword = 0; - } else { - codewords.push_back(POCSAG_IDLEWORD); // Batch padding - } - } - } - - c = 0; - - } while (char_idx < message_size); + if ((code >= '0') && (code <= '9')) { + code -= '0'; + } else { + if (code == 'S') + code = 10; + else if (code == 'U') + code = 11; + else if (code == ' ') + code = 12; + else if (code == '-') + code = 13; + else if (code == ']') + code = 14; + else if (code == '[') + code = 15; + else + code = 12; + } + + code = ((code & 0x0C) >> 2) | ((code & 0x03) << 2); // ----3210 -> ----1032 + code = ((code & 0x0A) >> 1) | ((code & 0x05) << 1); // ----1032 -> ----0123 + + return code; } +void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t function, const std::string message, const uint32_t address, std::vector& codewords) { + size_t b, c, address_slot; + size_t bit_idx, char_idx = 0; + uint32_t codeword, digit_code; + char ascii_char = 0; + + size_t message_size = message.size(); + + // Preamble + for (b = 0; b < (POCSAG_PREAMBLE_LENGTH / 32); b++) { + codewords.push_back(0xAAAAAAAA); + } + + // Address + codeword = (address & 0x1FFFF8U) << 10; + address_slot = (address & 7) * 2; + // Function + codeword |= (function << 11); + + insert_BCH(BCH_code, &codeword); + + // Address batch + codewords.push_back(POCSAG_SYNCWORD); + for (c = 0; c < 16; c++) { + if (c == address_slot) { + codewords.push_back(codeword); + if (type != MessageType::ADDRESS_ONLY) break; + } else + codewords.push_back(POCSAG_IDLEWORD); + } + + if (type == MessageType::ADDRESS_ONLY) return; // Done. + + c++; + codeword = 0; + bit_idx = 20 + 11; + + // Messages batch(es) + do { + if (c == 0) codewords.push_back(POCSAG_SYNCWORD); + + for (; c < 16; c++) { + // Fill up 20 bits + if (type == MessageType::ALPHANUMERIC) { + if ((char_idx < message_size) || (ascii_char)) { + do { + bit_idx -= 7; + + if (char_idx < message_size) + ascii_char = message[char_idx] & 0x7F; + else + ascii_char = 0; // Codeword padding + + // Bottom's up + ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4; // *6543210 -> 3210*654 + ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2; // 3210*654 -> 103254*6 + ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55); // 103254*6 -> *0123456 + + codeword |= (ascii_char << bit_idx); + + char_idx++; + + } while (bit_idx > 11); + + codeword &= 0x7FFFF800; // Trim data + codeword |= 0x80000000; // Message type + insert_BCH(BCH_code, &codeword); + + codewords.push_back(codeword); + + if (bit_idx != 11) { + bit_idx = 20 + bit_idx; + codeword = ascii_char << bit_idx; + } else { + bit_idx = 20 + 11; + codeword = 0; + } + } else { + codewords.push_back(POCSAG_IDLEWORD); // Batch padding + } + } else if (type == MessageType::NUMERIC_ONLY) { + if (char_idx < message_size) { + do { + bit_idx -= 4; + + if (char_idx < message_size) + digit_code = get_digit_code(message[char_idx]); + else + digit_code = 3; // Space (codeword padding) + + codeword |= (digit_code << bit_idx); + + char_idx++; + + } while (bit_idx > 11); + + codeword |= 0x80000000; // Message type + insert_BCH(BCH_code, &codeword); + + codewords.push_back(codeword); + + bit_idx = 20 + 11; + codeword = 0; + } else { + codewords.push_back(POCSAG_IDLEWORD); // Batch padding + } + } + } + + c = 0; + + } while (char_idx < message_size); +} // ------------------------------------------------------------------------------- // ------------------------------------------------------------------------------- -inline int bitsDiff(unsigned long left, unsigned long right) -{ - unsigned long xord = left ^ right; - int count = 0; - for (int i = 0; i<32; i++) - { - if ((xord & 0x01) != 0) ++count; - xord = xord >> 1; - } - return(count); - +inline int bitsDiff(unsigned long left, unsigned long right) { + unsigned long xord = left ^ right; + int count = 0; + for (int i = 0; i < 32; i++) { + if ((xord & 0x01) != 0) ++count; + xord = xord >> 1; + } + return (count); } // ------------------------------------------------------------------------------- // ------------------------------------------------------------------------------- -static uint32_t ecs[32]; /* error correction sequence */ +static uint32_t ecs[32]; /* error correction sequence */ static uint32_t bch[1025]; static int eccSetup = 0; // ------------------------------------------------------------------------------- // ------------------------------------------------------------------------------- -void setupecc() -{ - unsigned int srr = 0x3b4; - unsigned int i, n, j, k; - - /* calculate all information needed to implement error correction */ - // Note : this is only for 31,21 code used in pocsag & flex - // one should probably also make use of 32nd parity bit - for (i = 0; i <= 20; i++) - { - ecs[i] = srr; - if ((srr & 0x01) != 0) srr = (srr >> 1) ^ 0x3B4; else srr = srr >> 1; - } - - /* bch holds a syndrome look-up table telling which bits to correct */ - // first 5 bits hold location of first error; next 5 bits hold location - // of second error; bits 12 & 13 tell how many bits are bad - for (i = 0; i<1024; i++) bch[i] = 0; - - /* two errors in data */ - for (n = 0; n <= 20; n++) - { - for (i = 0; i <= 20; i++) - { - j = (i << 5) + n; - k = ecs[n] ^ ecs[i]; - bch[k] = j + 0x2000; - } - } - - /* one error in data */ - for (n = 0; n <= 20; n++) - { - k = ecs[n]; - j = n + (0x1f << 5); - bch[k] = j + 0x1000; - } - - /* one error in data and one error in ecc portion */ - for (n = 0; n <= 20; n++) - { - for (i = 0; i<10; i++) /* ecc screwed up bit */ - { - k = ecs[n] ^ (1 << i); - j = n + (0x1f << 5); - bch[k] = j + 0x2000; - } - } - - /* one error in ecc */ - for (n = 0; n<10; n++) - { - k = 1 << n; - bch[k] = 0x3ff + 0x1000; - } - - /* two errors in ecc */ - for (n = 0; n<10; n++) - { - for (i = 0; i<10; i++) - { - if (i != n) - { - k = (1 << n) ^ (1 << i); - bch[k] = 0x3ff + 0x2000; - } - } - } +void setupecc() { + unsigned int srr = 0x3b4; + unsigned int i, n, j, k; + + /* calculate all information needed to implement error correction */ + // Note : this is only for 31,21 code used in pocsag & flex + // one should probably also make use of 32nd parity bit + for (i = 0; i <= 20; i++) { + ecs[i] = srr; + if ((srr & 0x01) != 0) + srr = (srr >> 1) ^ 0x3B4; + else + srr = srr >> 1; + } + + /* bch holds a syndrome look-up table telling which bits to correct */ + // first 5 bits hold location of first error; next 5 bits hold location + // of second error; bits 12 & 13 tell how many bits are bad + for (i = 0; i < 1024; i++) bch[i] = 0; + + /* two errors in data */ + for (n = 0; n <= 20; n++) { + for (i = 0; i <= 20; i++) { + j = (i << 5) + n; + k = ecs[n] ^ ecs[i]; + bch[k] = j + 0x2000; + } + } + + /* one error in data */ + for (n = 0; n <= 20; n++) { + k = ecs[n]; + j = n + (0x1f << 5); + bch[k] = j + 0x1000; + } + + /* one error in data and one error in ecc portion */ + for (n = 0; n <= 20; n++) { + for (i = 0; i < 10; i++) /* ecc screwed up bit */ + { + k = ecs[n] ^ (1 << i); + j = n + (0x1f << 5); + bch[k] = j + 0x2000; + } + } + + /* one error in ecc */ + for (n = 0; n < 10; n++) { + k = 1 << n; + bch[k] = 0x3ff + 0x1000; + } + + /* two errors in ecc */ + for (n = 0; n < 10; n++) { + for (i = 0; i < 10; i++) { + if (i != n) { + k = (1 << n) ^ (1 << i); + bch[k] = 0x3ff + 0x2000; + } + } + } } // ------------------------------------------------------------------------------- // ------------------------------------------------------------------------------- -inline int errorCorrection(uint32_t * val) -{ - // Set up the tables the first time - if (eccSetup == 0) - { - setupecc(); - eccSetup = 1; - } - - int i, synd, errl, acc, pari, ecc, b1, b2; - - errl = 0; - pari = 0; - - /* run through error detection and correction routine */ - - // for (i=0; i<=20; i++) - ecc = 0; - for (i = 31; i >= 11; --i) - { - if ((*val&(1 << i))) { ecc = ecc ^ ecs[31 - i]; pari = pari ^ 0x01; } - } - - // for (i=21; i<=30; i++) - acc = 0; - for (i = 10; i >= 1; --i) - { - acc = acc << 1; - if ((*val&(1 << i))) { acc = acc ^ 0x01; } - } - - synd = ecc ^ acc; - - errl = 0; - - if (synd != 0) /* if nonzero syndrome we have error */ - { - - if (bch[synd] != 0) /* check for correctable error */ - { - b1 = bch[synd] & 0x1f; - b2 = bch[synd] >> 5; - b2 = b2 & 0x1f; - - if (b2 != 0x1f) - { - *val ^= 0x01 << (31 - b2); - ecc = ecc ^ ecs[b2]; - } - - if (b1 != 0x1f) - { - *val ^= 0x01 << (31 - b1); - ecc = ecc ^ ecs[b1]; - } - - errl = bch[synd] >> 12; - } - else - { - errl = 3; - } - - if (errl == 1) pari = pari ^ 0x01; - } - - if (errl == 4) errl = 3; - - return errl; +inline int errorCorrection(uint32_t* val) { + // Set up the tables the first time + if (eccSetup == 0) { + setupecc(); + eccSetup = 1; + } + + int i, synd, errl, acc, pari, ecc, b1, b2; + + errl = 0; + pari = 0; + + /* run through error detection and correction routine */ + + // for (i=0; i<=20; i++) + ecc = 0; + for (i = 31; i >= 11; --i) { + if ((*val & (1 << i))) { + ecc = ecc ^ ecs[31 - i]; + pari = pari ^ 0x01; + } + } + + // for (i=21; i<=30; i++) + acc = 0; + for (i = 10; i >= 1; --i) { + acc = acc << 1; + if ((*val & (1 << i))) { + acc = acc ^ 0x01; + } + } + + synd = ecc ^ acc; + + errl = 0; + + if (synd != 0) /* if nonzero syndrome we have error */ + { + if (bch[synd] != 0) /* check for correctable error */ + { + b1 = bch[synd] & 0x1f; + b2 = bch[synd] >> 5; + b2 = b2 & 0x1f; + + if (b2 != 0x1f) { + *val ^= 0x01 << (31 - b2); + ecc = ecc ^ ecs[b2]; + } + + if (b1 != 0x1f) { + *val ^= 0x01 << (31 - b1); + ecc = ecc ^ ecs[b1]; + } + + errl = bch[synd] >> 12; + } else { + errl = 3; + } + + if (errl == 1) pari = pari ^ 0x01; + } + + if (errl == 4) errl = 3; + + return errl; } - -void pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state) { - int errors = 0; - uint32_t codeword; - char ascii_char; - std::string output_text = ""; - - state->out_type = EMPTY; - - // For each codeword... - for (size_t i = 0; i < 16; i++) { - codeword = batch[i]; - - errorCorrection(&codeword); - errors = errorCorrection(&codeword); - - if (!(codeword & 0x80000000U)) { - // Address codeword - if (state->mode == STATE_CLEAR) { - //if (codeword != POCSAG_IDLEWORD) { - if (! (bitsDiff(codeword, POCSAG_IDLEWORD) < 1)){ - - state->function = (codeword >> 11) & 3; - state->address = (codeword >> 10) & 0x1FFFF8U; // 18 MSBs are transmitted - state->mode = STATE_HAVE_ADDRESS; - state->out_type = ADDRESS; - state->errors = errors; - - state->ascii_idx = 0; - state->ascii_data = 0; - } - } else { - state->mode = STATE_CLEAR; // New address = new message - } - } else { - state->errors += errors; - // Message codeword - if (state->mode == STATE_HAVE_ADDRESS) { - // First message codeword: complete address - state->address |= (i >> 1); // Add in the 3 LSBs (frame #) - state->mode = STATE_GETTING_MSG; - } - - state->out_type = MESSAGE; - - state->ascii_data |= ((codeword >> 11) & 0xFFFFF); // Get 20 message bits - state->ascii_idx += 20; - - // Raw 20 bits to 7 bit reversed ASCII - while (state->ascii_idx >= 7) { - state->ascii_idx -= 7; - ascii_char = ((state->ascii_data) >> (state->ascii_idx)) & 0x7F; - - // Bottom's up - ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4; // 01234567 -> 45670123 - ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2; // 45670123 -> 67452301 - ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55); // 67452301 -> *7654321 - - // Translate non-printable chars - if ((ascii_char < 32) || (ascii_char > 126)) - { - //output_text += "[" + to_string_dec_uint(ascii_char) + "]"; - output_text += "."; - } - else - output_text += ascii_char; - } - - state->ascii_data <<= 20; // Remaining bits are for next time... - } - } - - state->output = output_text; - - if (state->mode == STATE_HAVE_ADDRESS) - state->mode = STATE_CLEAR; +void pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState* const state) { + int errors = 0; + uint32_t codeword; + char ascii_char; + std::string output_text = ""; + + state->out_type = EMPTY; + + // For each codeword... + for (size_t i = 0; i < 16; i++) { + codeword = batch[i]; + + errorCorrection(&codeword); + errors = errorCorrection(&codeword); + + if (!(codeword & 0x80000000U)) { + // Address codeword + if (state->mode == STATE_CLEAR) { + // if (codeword != POCSAG_IDLEWORD) { + if (!(bitsDiff(codeword, POCSAG_IDLEWORD) < 1)) { + state->function = (codeword >> 11) & 3; + state->address = (codeword >> 10) & 0x1FFFF8U; // 18 MSBs are transmitted + state->mode = STATE_HAVE_ADDRESS; + state->out_type = ADDRESS; + state->errors = errors; + + state->ascii_idx = 0; + state->ascii_data = 0; + } + } else { + state->mode = STATE_CLEAR; // New address = new message + } + } else { + state->errors += errors; + // Message codeword + if (state->mode == STATE_HAVE_ADDRESS) { + // First message codeword: complete address + state->address |= (i >> 1); // Add in the 3 LSBs (frame #) + state->mode = STATE_GETTING_MSG; + } + + state->out_type = MESSAGE; + + state->ascii_data |= ((codeword >> 11) & 0xFFFFF); // Get 20 message bits + state->ascii_idx += 20; + + // Raw 20 bits to 7 bit reversed ASCII + while (state->ascii_idx >= 7) { + state->ascii_idx -= 7; + ascii_char = ((state->ascii_data) >> (state->ascii_idx)) & 0x7F; + + // Bottom's up + ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4; // 01234567 -> 45670123 + ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2; // 45670123 -> 67452301 + ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55); // 67452301 -> *7654321 + + // Translate non-printable chars + if ((ascii_char < 32) || (ascii_char > 126)) { + // output_text += "[" + to_string_dec_uint(ascii_char) + "]"; + output_text += "."; + } else + output_text += ascii_char; + } + + state->ascii_data <<= 20; // Remaining bits are for next time... + } + } + + state->output = output_text; + + if (state->mode == STATE_HAVE_ADDRESS) + state->mode = STATE_CLEAR; } } /* namespace pocsag */ diff --git a/firmware/common/pocsag.hpp b/firmware/common/pocsag.hpp index 6c86f8f3f..0316a9c84 100644 --- a/firmware/common/pocsag.hpp +++ b/firmware/common/pocsag.hpp @@ -24,7 +24,7 @@ #define __POCSAG_H__ #define POCSAG_PREAMBLE_LENGTH 576 -#define POCSAG_TIMEOUT (576 * 2) // Preamble length * 2 +#define POCSAG_TIMEOUT (576 * 2) // Preamble length * 2 #define POCSAG_SYNCWORD 0x7CD215D8 #define POCSAG_IDLEWORD 0x7A89C197 #define POCSAG_AUDIO_RATE 24000 @@ -38,50 +38,48 @@ namespace pocsag { // Todo: these enums suck, make a better decode_batch enum Mode : uint32_t { - STATE_CLEAR, - STATE_HAVE_ADDRESS, - STATE_GETTING_MSG + STATE_CLEAR, + STATE_HAVE_ADDRESS, + STATE_GETTING_MSG }; enum OutputType : uint32_t { - EMPTY, - ADDRESS, - MESSAGE + EMPTY, + ADDRESS, + MESSAGE }; enum MessageType : uint32_t { - ADDRESS_ONLY, - NUMERIC_ONLY, - ALPHANUMERIC + ADDRESS_ONLY, + NUMERIC_ONLY, + ALPHANUMERIC }; struct POCSAGState { - uint32_t function; - uint32_t address; - Mode mode = STATE_CLEAR; - OutputType out_type = EMPTY; - uint32_t ascii_data; - uint32_t ascii_idx; - uint32_t errors; - std::string output; + uint32_t function; + uint32_t address; + Mode mode = STATE_CLEAR; + OutputType out_type = EMPTY; + uint32_t ascii_data; + uint32_t ascii_idx; + uint32_t errors; + std::string output; }; const pocsag::BitRate pocsag_bitrates[4] = { - pocsag::BitRate::FSK512, - pocsag::BitRate::FSK1200, - pocsag::BitRate::FSK2400, - pocsag::BitRate::FSK3200 -}; + pocsag::BitRate::FSK512, + pocsag::BitRate::FSK1200, + pocsag::BitRate::FSK2400, + pocsag::BitRate::FSK3200}; std::string bitrate_str(BitRate bitrate); std::string flag_str(PacketFlag packetflag); -void insert_BCH(BCHCode& BCH_code, uint32_t * codeword); +void insert_BCH(BCHCode& BCH_code, uint32_t* codeword); uint32_t get_digit_code(char code); -void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t function, const std::string message, - const uint32_t address, std::vector& codewords); -void pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state); +void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t function, const std::string message, const uint32_t address, std::vector& codewords); +void pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState* const state); } /* namespace pocsag */ -#endif/*__POCSAG_H__*/ +#endif /*__POCSAG_H__*/ diff --git a/firmware/common/pocsag_packet.hpp b/firmware/common/pocsag_packet.hpp index 3d6f73cda..c0a48ec41 100644 --- a/firmware/common/pocsag_packet.hpp +++ b/firmware/common/pocsag_packet.hpp @@ -31,67 +31,67 @@ namespace pocsag { enum BitRate : uint32_t { - UNKNOWN, - FSK512 = 512, - FSK1200 = 1200, - FSK2400 = 2400, - FSK3200 = 3200 + UNKNOWN, + FSK512 = 512, + FSK1200 = 1200, + FSK2400 = 2400, + FSK3200 = 3200 }; enum PacketFlag : uint32_t { - NORMAL, - TIMED_OUT, - TOO_LONG + NORMAL, + TIMED_OUT, + TOO_LONG }; class POCSAGPacket { -public: - void set_timestamp(const Timestamp& value) { - timestamp_ = value; - } - - Timestamp timestamp() const { - return timestamp_; - } - - void set(const size_t index, const uint32_t data) { - if (index < 16) - codewords[index] = data; - } - - uint32_t operator[](const size_t index) const { - return (index < 16) ? codewords[index] : 0; - } - - void set_bitrate(const uint16_t bitrate) { - bitrate_ = bitrate; - } - - uint16_t bitrate() const { - return bitrate_; - } - - void set_flag(const PacketFlag flag) { - flag_ = flag; - } - - PacketFlag flag() const { - return flag_; - } - - void clear() { - codewords.fill(0); - bitrate_ = 0u; - flag_ = NORMAL; - } - -private: - uint16_t bitrate_ { 0 }; - PacketFlag flag_ { NORMAL }; - std::array codewords { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; - Timestamp timestamp_ { }; + public: + void set_timestamp(const Timestamp& value) { + timestamp_ = value; + } + + Timestamp timestamp() const { + return timestamp_; + } + + void set(const size_t index, const uint32_t data) { + if (index < 16) + codewords[index] = data; + } + + uint32_t operator[](const size_t index) const { + return (index < 16) ? codewords[index] : 0; + } + + void set_bitrate(const uint16_t bitrate) { + bitrate_ = bitrate; + } + + uint16_t bitrate() const { + return bitrate_; + } + + void set_flag(const PacketFlag flag) { + flag_ = flag; + } + + PacketFlag flag() const { + return flag_; + } + + void clear() { + codewords.fill(0); + bitrate_ = 0u; + flag_ = NORMAL; + } + + private: + uint16_t bitrate_{0}; + PacketFlag flag_{NORMAL}; + std::array codewords{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Timestamp timestamp_{}; }; } /* namespace pocsag */ -#endif/*__POCSAG_PACKET_H__*/ +#endif /*__POCSAG_PACKET_H__*/ diff --git a/firmware/common/portapack_adc.hpp b/firmware/common/portapack_adc.hpp index 89e1753a6..4f0892419 100644 --- a/firmware/common/portapack_adc.hpp +++ b/firmware/common/portapack_adc.hpp @@ -39,4 +39,4 @@ constexpr size_t adc1_rssi_input = 1; } /* namespace portapack */ -#endif/*__PORTAPACK_ADC_H__*/ +#endif /*__PORTAPACK_ADC_H__*/ diff --git a/firmware/common/portapack_cpld_data.hpp b/firmware/common/portapack_cpld_data.hpp index ad4dfd2f9..04165344a 100644 --- a/firmware/common/portapack_cpld_data.hpp +++ b/firmware/common/portapack_cpld_data.hpp @@ -38,7 +38,7 @@ namespace rev_20150901 { extern const std::array block_0; extern const std::array block_1; -const Config config { block_0, block_1 }; +const Config config{block_0, block_1}; } /* namespace rev_20150901 */ @@ -47,11 +47,11 @@ namespace rev_20170522 { extern const std::array block_0; extern const std::array block_1; -const Config config { block_0, block_1 }; +const Config config{block_0, block_1}; } /* namespace rev_20170522 */ } /* namespace cpld */ } /* namespace portapack */ -#endif/*__PORTAPACK_CPLD_DATA_H__*/ +#endif /*__PORTAPACK_CPLD_DATA_H__*/ diff --git a/firmware/common/portapack_dma.hpp b/firmware/common/portapack_dma.hpp index 4dda170c4..5e03a4fc6 100644 --- a/firmware/common/portapack_dma.hpp +++ b/firmware/common/portapack_dma.hpp @@ -43,25 +43,25 @@ constexpr size_t i2s0_rx_gpdma_channel_number = 3; constexpr size_t adc1_gpdma_channel_number = 4; constexpr size_t adc0_gpdma_channel_number = 5; -constexpr gpdma::mux::MUX gpdma_mux { - .peripheral_0 = gpdma::mux::Peripheral0::SGPIO14, - .peripheral_1 = gpdma::mux::Peripheral1::TIMER0_MATCH_0, - .peripheral_2 = gpdma::mux::Peripheral2::TIMER0_MATCH_1, - .peripheral_3 = gpdma::mux::Peripheral3::TIMER1_MATCH_0, - .peripheral_4 = gpdma::mux::Peripheral4::TIMER1_MATCH_1, - .peripheral_5 = gpdma::mux::Peripheral5::TIMER2_MATCH_0, - .peripheral_6 = gpdma::mux::Peripheral6::TIMER2_MATCH_1, - .peripheral_7 = gpdma::mux::Peripheral7::TIMER3_MATCH_0, - .peripheral_8 = gpdma::mux::Peripheral8::TIMER3_MATCH_1, - .peripheral_9 = gpdma::mux::Peripheral9::I2S0_DMAREQ_1, - .peripheral_10 = gpdma::mux::Peripheral10::I2S0_DMAREQ_2, - .peripheral_11 = gpdma::mux::Peripheral11::SSP1_RX, - .peripheral_12 = gpdma::mux::Peripheral12::SSP1_TX, - .peripheral_13 = gpdma::mux::Peripheral13::ADC0, - .peripheral_14 = gpdma::mux::Peripheral14::ADC1, - .peripheral_15 = gpdma::mux::Peripheral15::DAC, +constexpr gpdma::mux::MUX gpdma_mux{ + .peripheral_0 = gpdma::mux::Peripheral0::SGPIO14, + .peripheral_1 = gpdma::mux::Peripheral1::TIMER0_MATCH_0, + .peripheral_2 = gpdma::mux::Peripheral2::TIMER0_MATCH_1, + .peripheral_3 = gpdma::mux::Peripheral3::TIMER1_MATCH_0, + .peripheral_4 = gpdma::mux::Peripheral4::TIMER1_MATCH_1, + .peripheral_5 = gpdma::mux::Peripheral5::TIMER2_MATCH_0, + .peripheral_6 = gpdma::mux::Peripheral6::TIMER2_MATCH_1, + .peripheral_7 = gpdma::mux::Peripheral7::TIMER3_MATCH_0, + .peripheral_8 = gpdma::mux::Peripheral8::TIMER3_MATCH_1, + .peripheral_9 = gpdma::mux::Peripheral9::I2S0_DMAREQ_1, + .peripheral_10 = gpdma::mux::Peripheral10::I2S0_DMAREQ_2, + .peripheral_11 = gpdma::mux::Peripheral11::SSP1_RX, + .peripheral_12 = gpdma::mux::Peripheral12::SSP1_TX, + .peripheral_13 = gpdma::mux::Peripheral13::ADC0, + .peripheral_14 = gpdma::mux::Peripheral14::ADC1, + .peripheral_15 = gpdma::mux::Peripheral15::DAC, }; } /* namespace portapack */ -#endif/*__PORTAPACK_DMA_H__*/ +#endif /*__PORTAPACK_DMA_H__*/ diff --git a/firmware/common/portapack_hal.hpp b/firmware/common/portapack_hal.hpp index 8605646d4..d8047ef3e 100644 --- a/firmware/common/portapack_hal.hpp +++ b/firmware/common/portapack_hal.hpp @@ -33,35 +33,35 @@ namespace portapack { /* TODO: Make these GPIOs private and expose via appropriate functions. */ -constexpr GPIO gpio_io_stbx = gpio[GPIO5_0]; /* P2_0 */ -constexpr GPIO gpio_addr = gpio[GPIO5_1]; /* P2_1 */ -constexpr GPIO gpio_lcd_te = gpio[GPIO5_3]; /* P2_3 */ -constexpr GPIO gpio_dfu = gpio[GPIO5_7]; /* P2_8 */ -constexpr GPIO gpio_lcd_rdx = gpio[GPIO5_4]; /* P2_4 */ -constexpr GPIO gpio_lcd_wrx = gpio[GPIO1_10]; /* P2_9 */ -constexpr GPIO gpio_dir = gpio[GPIO1_13]; /* P2_13 */ -constexpr std::array gpios_data { - gpio[GPIO3_8], - gpio[GPIO3_9], - gpio[GPIO3_10], - gpio[GPIO3_11], - gpio[GPIO3_12], - gpio[GPIO3_13], - gpio[GPIO3_14], - gpio[GPIO3_15], +constexpr GPIO gpio_io_stbx = gpio[GPIO5_0]; /* P2_0 */ +constexpr GPIO gpio_addr = gpio[GPIO5_1]; /* P2_1 */ +constexpr GPIO gpio_lcd_te = gpio[GPIO5_3]; /* P2_3 */ +constexpr GPIO gpio_dfu = gpio[GPIO5_7]; /* P2_8 */ +constexpr GPIO gpio_lcd_rdx = gpio[GPIO5_4]; /* P2_4 */ +constexpr GPIO gpio_lcd_wrx = gpio[GPIO1_10]; /* P2_9 */ +constexpr GPIO gpio_dir = gpio[GPIO1_13]; /* P2_13 */ +constexpr std::array gpios_data{ + gpio[GPIO3_8], + gpio[GPIO3_9], + gpio[GPIO3_10], + gpio[GPIO3_11], + gpio[GPIO3_12], + gpio[GPIO3_13], + gpio[GPIO3_14], + gpio[GPIO3_15], }; -constexpr GPIO gpio_cpld_tms = gpio[GPIO1_1]; // P1_8 -constexpr GPIO gpio_cpld_tdo = gpio[GPIO1_8]; // P1_5 -constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0]; // P6_1 -constexpr GPIO gpio_cpld_tdi = gpio[GPIO3_1]; // P6_2 +constexpr GPIO gpio_cpld_tms = gpio[GPIO1_1]; // P1_8 +constexpr GPIO gpio_cpld_tdo = gpio[GPIO1_8]; // P1_5 +constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0]; // P6_1 +constexpr GPIO gpio_cpld_tdi = gpio[GPIO3_1]; // P6_2 -constexpr auto pin_i2s0_mclk = pins[CLK2]; -constexpr auto pin_i2s0_sck = pins[P3_0]; -constexpr auto pin_i2s0_ws = pins[P3_1]; +constexpr auto pin_i2s0_mclk = pins[CLK2]; +constexpr auto pin_i2s0_sck = pins[P3_0]; +constexpr auto pin_i2s0_ws = pins[P3_1]; constexpr auto pin_i2s0_tx_sda = pins[P3_2]; constexpr auto pin_i2s0_rx_sda = pins[P6_2]; } /* namespace portapack */ -#endif/*__PORTAPACK_HAL_H__*/ +#endif /*__PORTAPACK_HAL_H__*/ diff --git a/firmware/common/portapack_io.cpp b/firmware/common/portapack_io.cpp index ad9397392..c1200e7bc 100644 --- a/firmware/common/portapack_io.cpp +++ b/firmware/common/portapack_io.cpp @@ -33,85 +33,85 @@ using namespace lpc43xx; namespace portapack { void IO::init() { - data_mask_set(); - data_write_high(0); - - dir_read(); - lcd_rd_deassert(); - lcd_wr_deassert(); - io_stb_deassert(); - addr(0); - - gpio_dir.output(); - gpio_lcd_rdx.output(); - gpio_lcd_wrx.output(); - gpio_io_stbx.output(); - gpio_addr.output(); - gpio_rot_a.input(); - gpio_rot_b.input(); + data_mask_set(); + data_write_high(0); + + dir_read(); + lcd_rd_deassert(); + lcd_wr_deassert(); + io_stb_deassert(); + addr(0); + + gpio_dir.output(); + gpio_lcd_rdx.output(); + gpio_lcd_wrx.output(); + gpio_io_stbx.output(); + gpio_addr.output(); + gpio_rot_a.input(); + gpio_rot_b.input(); } void IO::lcd_backlight(const bool value) { - io_reg = (io_reg & 0x7f) | ((value ? 1 : 0) << 7); - io_write(1, io_reg); + io_reg = (io_reg & 0x7f) | ((value ? 1 : 0) << 7); + io_write(1, io_reg); } void IO::lcd_reset_state(const bool active) { - io_reg = (io_reg & 0xfe) | ((active ? 1 : 0) << 0); - io_write(1, io_reg); + io_reg = (io_reg & 0xfe) | ((active ? 1 : 0) << 0); + io_write(1, io_reg); } void IO::audio_reset_state(const bool active) { - /* NOTE: This overwrites the contents of the IO register, which for now - * have no significance. But someday...? - */ - io_reg = (io_reg & 0xfd) | ((active ? 1 : 0) << 1); - io_write(1, io_reg); + /* NOTE: This overwrites the contents of the IO register, which for now + * have no significance. But someday...? + */ + io_reg = (io_reg & 0xfd) | ((active ? 1 : 0) << 1); + io_write(1, io_reg); } void IO::reference_oscillator(const bool enable) { - const uint8_t mask = 1 << 6; - io_reg = (io_reg & ~mask) | (enable ? mask : 0); - io_write(1, io_reg); + const uint8_t mask = 1 << 6; + io_reg = (io_reg & ~mask) | (enable ? mask : 0); + io_write(1, io_reg); } uint32_t IO::io_update(const TouchPinsConfig write_value) { - /* Very touchy code to save context of PortaPack data bus while the - * resistive touch pin drive is changed. Order of operations is - * important to prevent latching spurious data into the LCD or IO - * registers. - */ - - const auto save_data = data_read(); - const auto addr = gpio_addr.read(); - const auto dir = gpio_dir.read(); - - io_stb_assert(); - - /* Switch to read */ - dir_read(); - addr_0(); - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - const auto switches_raw = data_read(); - - /* Switch to write */ - data_write_low(toUType(write_value)); - dir_write(); - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - io_stb_deassert(); - - data_write_low(save_data); - if( dir ) { /* 0 (write) -> 1 (read) */ - dir_read(); - } - gpio_addr.write(addr); - - auto dfu_btn = portapack::io.dfu_read() & 0x01; - return (switches_raw & 0x7f) | (dfu_btn << 7); + /* Very touchy code to save context of PortaPack data bus while the + * resistive touch pin drive is changed. Order of operations is + * important to prevent latching spurious data into the LCD or IO + * registers. + */ + + const auto save_data = data_read(); + const auto addr = gpio_addr.read(); + const auto dir = gpio_dir.read(); + + io_stb_assert(); + + /* Switch to read */ + dir_read(); + addr_0(); + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + const auto switches_raw = data_read(); + + /* Switch to write */ + data_write_low(toUType(write_value)); + dir_write(); + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + io_stb_deassert(); + + data_write_low(save_data); + if (dir) { /* 0 (write) -> 1 (read) */ + dir_read(); + } + gpio_addr.write(addr); + + auto dfu_btn = portapack::io.dfu_read() & 0x01; + return (switches_raw & 0x7f) | (dfu_btn << 7); } -} +} // namespace portapack diff --git a/firmware/common/portapack_io.hpp b/firmware/common/portapack_io.hpp index eee394cd5..4533dff62 100644 --- a/firmware/common/portapack_io.hpp +++ b/firmware/common/portapack_io.hpp @@ -33,350 +33,345 @@ namespace portapack { class IO { -public: - enum class TouchPinsConfig : uint8_t { - XN_BIT = (1 << 0), - XP_BIT = (1 << 1), - YN_BIT = (1 << 2), - YP_BIT = (1 << 3), - - XN_OE = (1 << 4), - XP_OE = (1 << 5), - YN_OE = (1 << 6), - YP_OE = (1 << 7), - - XN_IN = XN_BIT, - XN_OUT_1 = XN_OE | XN_BIT, - XN_OUT_0 = XN_OE, - - XP_IN = XP_BIT, - XP_OUT_1 = XP_OE | XP_BIT, - XP_OUT_0 = XP_OE, - - YN_IN = YN_BIT, - YN_OUT_1 = YN_OE | YN_BIT, - YN_OUT_0 = YN_OE, - - YP_IN = YP_BIT, - YP_OUT_1 = YP_OE | YP_BIT, - YP_OUT_0 = YP_OE, - - /* Allow pins to be pulled up by CPLD pull-ups. */ - Float = XP_IN | XN_IN | YP_IN | YN_IN, - - /* Drive one plane to 0V, other plane is pulled up. Watch for when pulled-up - * plane falls to ~0V. - */ - WaitTouch = XP_OUT_0 | XN_OUT_0 | YP_IN | YN_IN, - - /* Create a voltage divider between X plane, touch resistance, Y plane. */ - SensePressure = XP_IN | XN_OUT_0 | YP_OUT_1 | YN_IN, - - /* Create a voltage divider across X plane, read voltage from Y plane. */ - SenseX = XP_OUT_1 | XN_OUT_0 | YP_IN | YN_IN, - - /* Create a voltage divider across Y plane, read voltage from X plane. */ - SenseY = XP_IN | XN_IN | YP_OUT_1 | YN_OUT_0, - }; - - constexpr IO( - GPIO gpio_dir, - GPIO gpio_lcd_rdx, - GPIO gpio_lcd_wrx, - GPIO gpio_io_stbx, - GPIO gpio_addr, - GPIO gpio_rot_a, - GPIO gpio_rot_b - ) : gpio_dir { gpio_dir }, - gpio_lcd_rdx { gpio_lcd_rdx }, - gpio_lcd_wrx { gpio_lcd_wrx }, - gpio_io_stbx { gpio_io_stbx }, - gpio_addr { gpio_addr }, - gpio_rot_a { gpio_rot_a }, - gpio_rot_b { gpio_rot_b } - { - }; - - void init(); - - void lcd_backlight(const bool value); - void lcd_reset_state(const bool active); - void audio_reset_state(const bool active); - void reference_oscillator(const bool enable); - - void lcd_data_write_command_and_data( - const uint_fast8_t command, - const uint8_t* data, - const size_t data_count - ) { - lcd_command(command); - for(size_t i=0; i& data - ) { - lcd_command(command); - for(const auto d : data) { - lcd_write_data(d); - } - } - - void lcd_data_read_command_and_data( - const uint_fast8_t command, - uint16_t* const data, - const size_t data_count - ) { - lcd_command(command); - for(size_t i=0; i>= 3; - while(n--) { - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - lcd_write_data(v); - } - } - - void lcd_write_pixels(const ui::Color* const pixels, size_t n) { - for(size_t i=0; i> 8; - *(byte++) = word >> 0; - word_count--; - } - if( byte_count & 1 ) { - const auto word = lcd_read_data(); - *(byte++) = word >> 8; - } - } - - uint32_t io_read() { - io_stb_assert(); - dir_read(); - addr_0(); - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - const auto switches_raw = data_read(); - io_stb_deassert(); - - return switches_raw; - } - - uint32_t io_update(const TouchPinsConfig write_value); - - uint32_t lcd_te() { - return gpio_rot_a.read(); - } - - uint32_t dfu_read() { - return gpio_rot_b.read(); - } - -private: - const GPIO gpio_dir; - const GPIO gpio_lcd_rdx; - const GPIO gpio_lcd_wrx; - const GPIO gpio_io_stbx; - const GPIO gpio_addr; - const GPIO gpio_rot_a; - const GPIO gpio_rot_b; - - static constexpr ioportid_t gpio_data_port_id = 3; - static constexpr size_t gpio_data_shift = 8; - static constexpr ioportmask_t gpio_data_mask = 0xffU << gpio_data_shift; - - uint8_t io_reg { 0x03 }; - - void lcd_rd_assert() { - gpio_lcd_rdx.clear(); - } - - void lcd_rd_deassert() { - gpio_lcd_rdx.set(); - } - - void lcd_wr_assert() { - gpio_lcd_wrx.clear(); - } - - void lcd_wr_deassert() { - gpio_lcd_wrx.set(); - } - - void io_stb_assert() { - gpio_io_stbx.clear(); - } - - void io_stb_deassert() { - gpio_io_stbx.set(); - } - - void addr(const bool value) { - gpio_addr.write(value); - } - - void addr_1() { - gpio_addr.set(); - } - - void addr_0() { - gpio_addr.clear(); - } - - void data_mask_set() { - LPC_GPIO->MASK[gpio_data_port_id] = ~gpio_data_mask; - } - - void dir_write() { - gpio_dir.clear(); - LPC_GPIO->DIR[gpio_data_port_id] |= gpio_data_mask; - /* TODO: Manipulating DIR[3] makes me queasy. The RFFC5072 DATA pin - * is also on port 3, and switches direction periodically... - * Time to resort to bit-banding to enforce atomicity? But then, how - * to change direction on eight bits efficiently? Or do I care, since - * the PortaPack data bus shouldn't change direction too frequently? - */ - } - - void dir_read() { - LPC_GPIO->DIR[gpio_data_port_id] &= ~gpio_data_mask; - gpio_dir.set(); - } - - void data_write_low(const uint32_t value) { - LPC_GPIO->MPIN[gpio_data_port_id] = (value << gpio_data_shift); - } - - void data_write_high(const uint32_t value) { - LPC_GPIO->MPIN[gpio_data_port_id] = value; - } - - uint32_t data_read() { - return (LPC_GPIO->MPIN[gpio_data_port_id] >> gpio_data_shift) & 0xffU; - } - - void lcd_command(const uint32_t value) { - data_write_high(0); /* Drive high byte (with zero -- don't care) */ - dir_write(); /* Turn around data bus, MCU->CPLD */ - addr(0); /* Indicate command */ - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - lcd_wr_assert(); /* Latch high byte */ - - data_write_low(value); /* Drive low byte (pass-through) */ - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - lcd_wr_deassert(); /* Complete write operation */ - - addr(1); /* Set up for data phase (most likely after a command) */ - } - - void lcd_write_data(const uint32_t value) __attribute__((always_inline)) { - // NOTE: Assumes and DIR=0 and ADDR=1 from command phase. - data_write_high(value); /* Drive high byte */ - __asm__("nop"); - lcd_wr_assert(); /* Latch high byte */ - - data_write_low(value); /* Drive low byte (pass-through) */ - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - lcd_wr_deassert(); /* Complete write operation */ - } - - uint32_t lcd_read_data() { - // NOTE: Assumes ADDR=1 from command phase. - dir_read(); - - /* Start read operation */ - lcd_rd_assert(); - /* Wait for passthrough data(15:8) to settle -- ~16ns (3 cycles) typical */ - /* Wait for read control L duration (355ns) */ - halPolledDelay(71); // 355ns - const auto value_high = data_read(); - - /* Latch data[7:0] */ - lcd_rd_deassert(); - /* Wait for latched data[7:0] to settle -- ~26ns (5 cycles) typical */ - /* Wait for read control H duration (90ns) */ - halPolledDelay(18); // 90ns - - const auto value_low = data_read(); - return (value_high << 8) | value_low; - } - - void io_write(const bool address, const uint_fast16_t value) { - data_write_low(value); - dir_write(); - addr(address); - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - io_stb_assert(); - __asm__("nop"); - __asm__("nop"); - __asm__("nop"); - io_stb_deassert(); - } -/* - void lcd_data_write_command_and_data( - const uint_fast16_t command, - const uint8_t* const data, - const size_t count - ) { - lcd_data_write_command(command); - for(size_t i=0; i& data) { + lcd_command(command); + for (const auto d : data) { + lcd_write_data(d); + } + } + + void lcd_data_read_command_and_data( + const uint_fast8_t command, + uint16_t* const data, + const size_t data_count) { + lcd_command(command); + for (size_t i = 0; i < data_count; i++) { + data[i] = lcd_read_data(); + } + } + + void lcd_write_word(const uint32_t w) { + lcd_write_data(w); + } + + void lcd_write_words(const uint16_t* const w, size_t n) { + for (size_t i = 0; i < n; i++) { + lcd_write_data(w[i]); + } + } + + void lcd_write_pixel(const ui::Color pixel) { + lcd_write_data(pixel.v); + } + + uint32_t lcd_read_word() { + return lcd_read_data(); + } + + void lcd_write_pixels(const ui::Color pixel, size_t n) { + while (n--) { + lcd_write_data(pixel.v); + } + } + + void lcd_write_pixels_unrolled8(const ui::Color pixel, size_t n) { + auto v = pixel.v; + n >>= 3; + while (n--) { + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + } + } + + void lcd_write_pixels(const ui::Color* const pixels, size_t n) { + for (size_t i = 0; i < n; i++) { + lcd_write_pixel(pixels[i]); + } + } + + void lcd_read_bytes(uint8_t* byte, size_t byte_count) { + size_t word_count = byte_count / 2; + while (word_count) { + const auto word = lcd_read_data(); + *(byte++) = word >> 8; + *(byte++) = word >> 0; + word_count--; + } + if (byte_count & 1) { + const auto word = lcd_read_data(); + *(byte++) = word >> 8; + } + } + + uint32_t io_read() { + io_stb_assert(); + dir_read(); + addr_0(); + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + const auto switches_raw = data_read(); + io_stb_deassert(); + + return switches_raw; + } + + uint32_t io_update(const TouchPinsConfig write_value); + + uint32_t lcd_te() { + return gpio_rot_a.read(); + } + + uint32_t dfu_read() { + return gpio_rot_b.read(); + } + + private: + const GPIO gpio_dir; + const GPIO gpio_lcd_rdx; + const GPIO gpio_lcd_wrx; + const GPIO gpio_io_stbx; + const GPIO gpio_addr; + const GPIO gpio_rot_a; + const GPIO gpio_rot_b; + + static constexpr ioportid_t gpio_data_port_id = 3; + static constexpr size_t gpio_data_shift = 8; + static constexpr ioportmask_t gpio_data_mask = 0xffU << gpio_data_shift; + + uint8_t io_reg{0x03}; + + void lcd_rd_assert() { + gpio_lcd_rdx.clear(); + } + + void lcd_rd_deassert() { + gpio_lcd_rdx.set(); + } + + void lcd_wr_assert() { + gpio_lcd_wrx.clear(); + } + + void lcd_wr_deassert() { + gpio_lcd_wrx.set(); + } + + void io_stb_assert() { + gpio_io_stbx.clear(); + } + + void io_stb_deassert() { + gpio_io_stbx.set(); + } + + void addr(const bool value) { + gpio_addr.write(value); + } + + void addr_1() { + gpio_addr.set(); + } + + void addr_0() { + gpio_addr.clear(); + } + + void data_mask_set() { + LPC_GPIO->MASK[gpio_data_port_id] = ~gpio_data_mask; + } + + void dir_write() { + gpio_dir.clear(); + LPC_GPIO->DIR[gpio_data_port_id] |= gpio_data_mask; + /* TODO: Manipulating DIR[3] makes me queasy. The RFFC5072 DATA pin + * is also on port 3, and switches direction periodically... + * Time to resort to bit-banding to enforce atomicity? But then, how + * to change direction on eight bits efficiently? Or do I care, since + * the PortaPack data bus shouldn't change direction too frequently? + */ + } + + void dir_read() { + LPC_GPIO->DIR[gpio_data_port_id] &= ~gpio_data_mask; + gpio_dir.set(); + } + + void data_write_low(const uint32_t value) { + LPC_GPIO->MPIN[gpio_data_port_id] = (value << gpio_data_shift); + } + + void data_write_high(const uint32_t value) { + LPC_GPIO->MPIN[gpio_data_port_id] = value; + } + + uint32_t data_read() { + return (LPC_GPIO->MPIN[gpio_data_port_id] >> gpio_data_shift) & 0xffU; + } + + void lcd_command(const uint32_t value) { + data_write_high(0); /* Drive high byte (with zero -- don't care) */ + dir_write(); /* Turn around data bus, MCU->CPLD */ + addr(0); /* Indicate command */ + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + lcd_wr_assert(); /* Latch high byte */ + + data_write_low(value); /* Drive low byte (pass-through) */ + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + lcd_wr_deassert(); /* Complete write operation */ + + addr(1); /* Set up for data phase (most likely after a command) */ + } + + void lcd_write_data(const uint32_t value) __attribute__((always_inline)) { + // NOTE: Assumes and DIR=0 and ADDR=1 from command phase. + data_write_high(value); /* Drive high byte */ + __asm__("nop"); + lcd_wr_assert(); /* Latch high byte */ + + data_write_low(value); /* Drive low byte (pass-through) */ + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + lcd_wr_deassert(); /* Complete write operation */ + } + + uint32_t lcd_read_data() { + // NOTE: Assumes ADDR=1 from command phase. + dir_read(); + + /* Start read operation */ + lcd_rd_assert(); + /* Wait for passthrough data(15:8) to settle -- ~16ns (3 cycles) typical */ + /* Wait for read control L duration (355ns) */ + halPolledDelay(71); // 355ns + const auto value_high = data_read(); + + /* Latch data[7:0] */ + lcd_rd_deassert(); + /* Wait for latched data[7:0] to settle -- ~26ns (5 cycles) typical */ + /* Wait for read control H duration (90ns) */ + halPolledDelay(18); // 90ns + + const auto value_low = data_read(); + return (value_high << 8) | value_low; + } + + void io_write(const bool address, const uint_fast16_t value) { + data_write_low(value); + dir_write(); + addr(address); + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + io_stb_assert(); + __asm__("nop"); + __asm__("nop"); + __asm__("nop"); + io_stb_deassert(); + } + /* + void lcd_data_write_command_and_data( + const uint_fast16_t command, + const uint8_t* const data, + const size_t count + ) { + lcd_data_write_command(command); + for(size_t i=0; i; - constexpr ppb_range_t ppb_range { -99000, 99000 }; - constexpr ppb_t ppb_reset_value { 0 }; - - using tone_mix_range_t = range_t; - constexpr tone_mix_range_t tone_mix_range { 10, 99 }; - constexpr int32_t tone_mix_reset_value { 20 }; - - using afsk_freq_range_t = range_t; - constexpr afsk_freq_range_t afsk_freq_range { 1, 4000 }; - constexpr int32_t afsk_mark_reset_value { 1200 }; - constexpr int32_t afsk_space_reset_value { 2200 }; - - using modem_baudrate_range_t = range_t; - constexpr modem_baudrate_range_t modem_baudrate_range { 50, 9600 }; - constexpr int32_t modem_baudrate_reset_value { 1200 }; - - /*using modem_bw_range_t = range_t; - constexpr modem_bw_range_t modem_bw_range { 1000, 50000 }; - constexpr int32_t modem_bw_reset_value { 15000 };*/ - - using modem_repeat_range_t = range_t; - constexpr modem_repeat_range_t modem_repeat_range { 1, 99 }; - constexpr int32_t modem_repeat_reset_value { 5 }; - - using clkout_freq_range_t = range_t; - constexpr clkout_freq_range_t clkout_freq_range { 10, 60000 }; - constexpr uint32_t clkout_freq_reset_value { 10000 }; - - enum data_structure_version_enum : uint32_t { - VERSION_CURRENT = 0x10000002, - }; - - static const uint32_t TOUCH_CALIBRATION_MAGIC = 0x074af82f; - - struct ui_config_t { - private: - enum bits_t { - BacklightTimeoutLSB = 0, - BacklightTimeoutEnable = 3, - ClkoutFreqLSB = 4, - ShowGUIReturnIcon = 20, - LoadAppSettings = 21, - SaveAppSettings = 22, - ShowBiggerQRCode = 23, - DisableTouchscreen = 24, - HideClock = 25, - ClockWithDate = 26, - ClkOutEnabled = 27, - ConfigSpeaker = 28, - StealthMode = 29, - ConfigLogin = 30, - ConfigSplash = 31, - }; - - enum bits_mask_t : uint32_t { - BacklightTimeoutMask = ((1 << 3) - 1) << bits_t::BacklightTimeoutLSB, - ClkoutFreqMask = ((1 << 16) - 1) << bits_t::ClkoutFreqLSB, - }; - - uint32_t values; - - constexpr bool bit_read(const bits_t n) const { - return ((values >> n) & 1) != 0; - } - - constexpr void bit_write(const bits_t n, const bool v) { - if(bit_read(n) != v) { - values ^= 1 << n; - } - } - - public: - backlight_config_t config_backlight_timer() { - const auto timeout_enum = (backlight_timeout_t)((values & bits_mask_t::BacklightTimeoutMask) >> bits_t::BacklightTimeoutLSB); - const bool timeout_enabled = bit_read(bits_t::BacklightTimeoutEnable); - return backlight_config_t(timeout_enum, timeout_enabled); - } - - void set_config_backlight_timer(const backlight_config_t& new_value) { - values = (values & ~bits_mask_t::BacklightTimeoutMask) - | ((new_value.timeout_enum() << bits_t::BacklightTimeoutLSB) & bits_mask_t::BacklightTimeoutMask); - bit_write(bits_t::BacklightTimeoutEnable, new_value.timeout_enabled()); - } - - constexpr uint32_t clkout_freq() { - uint32_t freq = (values & bits_mask_t::ClkoutFreqMask) >> bits_t::ClkoutFreqLSB; - if(freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) { - values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_reset_value << bits_t::ClkoutFreqLSB); - return clkout_freq_reset_value; - } - else { - return freq; - } - } - - constexpr void set_clkout_freq(uint32_t freq) { - values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_range.clip(freq) << bits_t::ClkoutFreqLSB); - } - - // ui_config is an uint32_t var storing information bitwise - // bits 0-2 store the backlight timer - // bits 4-19 (16 bits) store the clkout frequency - // bits 21-31 store the different single bit configs depicted below - // bit 20 store the display state of the gui return icon, hidden (0) or shown (1) - - constexpr bool show_gui_return_icon() const { // add return icon in touchscreen menue - return bit_read(bits_t::ShowGUIReturnIcon); - } - - constexpr void set_gui_return_icon(bool v) { - bit_write(bits_t::ShowGUIReturnIcon, v); - } - - constexpr bool load_app_settings() const { // load (last saved) app settings on startup of app - return bit_read(bits_t::LoadAppSettings); - } - - constexpr void set_load_app_settings(bool v) { - bit_write(bits_t::LoadAppSettings, v); - } - - constexpr bool save_app_settings() const { // save app settings when closing app - return bit_read(bits_t::SaveAppSettings); - } - - constexpr void set_save_app_settings(bool v) { - bit_write(bits_t::SaveAppSettings, v); - } - - constexpr bool show_bigger_qr_code() const { // show bigger QR code - return bit_read(bits_t::ShowBiggerQRCode); - } - - constexpr void set_show_bigger_qr_code(bool v) { - bit_write(bits_t::ShowBiggerQRCode, v); - } - - constexpr bool disable_touchscreen() const { // Option to disable touch screen - return bit_read(bits_t::DisableTouchscreen); - } - - constexpr void set_disable_touchscreen(bool v) { - bit_write(bits_t::DisableTouchscreen, v); - } - - constexpr bool hide_clock() const { // clock hidden from main menu - return bit_read(bits_t::HideClock); - } - - constexpr void set_clock_hidden(bool v) { - bit_write(bits_t::HideClock, v); - } - - constexpr bool clock_with_date() const { // show clock with date, if not hidden - return bit_read(bits_t::ClockWithDate); - } - - constexpr void set_clock_with_date(bool v) { - bit_write(bits_t::ClockWithDate, v); - } - - constexpr bool clkout_enabled() const { - return bit_read(bits_t::ClkOutEnabled); - } - - constexpr void set_clkout_enabled(bool v) { - bit_write(bits_t::ClkOutEnabled, v); - } - - constexpr bool config_speaker() const { - return bit_read(bits_t::ConfigSpeaker); - } - - constexpr void set_config_speaker(bool v) { - bit_write(bits_t::ConfigSpeaker, v); - } - - constexpr bool stealth_mode() const { - return bit_read(bits_t::StealthMode); - } - - constexpr void set_stealth_mode(bool v) { - bit_write(bits_t::StealthMode, v); - } - - constexpr bool config_login() const { - return bit_read(bits_t::ConfigLogin); - } - - constexpr void set_config_login(bool v) { - bit_write(bits_t::ConfigLogin, v); - } - - constexpr bool config_splash() const { - return bit_read(bits_t::ConfigSplash); - } - - constexpr void set_config_splash(bool v) { - bit_write(bits_t::ConfigSplash, v); - } - - constexpr ui_config_t() : - values( - (1 << ConfigSplash) - | (1 << ConfigSpeaker) - | (clkout_freq_reset_value << ClkoutFreqLSB) - | (7 << BacklightTimeoutLSB) - ) - { - } - }; - - /* struct must pack the same way on M4 and M0 cores. */ - struct data_t { - data_structure_version_enum structure_version; - int64_t tuned_frequency; - int32_t correction_ppb; - uint32_t touch_calibration_magic; - touch::Calibration touch_calibration; - - // Modem - uint32_t modem_def_index; - serial_format_t serial_format; - int32_t modem_bw; - int32_t afsk_mark_freq; - int32_t afsk_space_freq; - int32_t modem_baudrate; - int32_t modem_repeat; - - // Play dead unlock - uint32_t playdead_magic; - uint32_t playing_dead; - uint32_t playdead_sequence; - - // UI - ui_config_t ui_config; - - uint32_t pocsag_last_address; - uint32_t pocsag_ignore_address; - - int32_t tone_mix; - - // Hardware - uint32_t hardware_config; - - // Recon App - uint64_t recon_config; - - // converter: show or hide icon. Hiding cause auto disable to avoid mistakes - bool hide_converter ; - // enable or disable converter - bool converter ; - // set up converter (false) or down converter (true) converter - bool updown_converter ; - // up/down converter offset - int64_t converter_frequency_offset ; - - constexpr data_t() : - structure_version(data_structure_version_enum::VERSION_CURRENT), - tuned_frequency(tuned_frequency_reset_value), - correction_ppb(ppb_reset_value), - touch_calibration_magic(TOUCH_CALIBRATION_MAGIC), - touch_calibration(touch::Calibration()), - - modem_def_index(0), // TODO: Unused? - serial_format(), - modem_bw(15000), // TODO: Unused? - afsk_mark_freq(afsk_mark_reset_value), - afsk_space_freq(afsk_space_reset_value), - modem_baudrate(modem_baudrate_reset_value), - modem_repeat(modem_repeat_reset_value), - - playdead_magic(), // TODO: Unused? - playing_dead(), // TODO: Unused? - playdead_sequence(), // TODO: Unused? - - ui_config(), - - pocsag_last_address(0), // TODO: A better default? - pocsag_ignore_address(0), // TODO: A better default? - - tone_mix(tone_mix_reset_value), - - hardware_config(0), - recon_config(0), - hide_converter(0), - converter(0), - updown_converter(0), - converter_frequency_offset(0) - { - } - }; - - struct backup_ram_t { - private: - uint32_t regfile[63]; - uint32_t check_value; - - static void copy(const backup_ram_t& src, backup_ram_t& dst) { - for(size_t i=0; i<63; i++) { - dst.regfile[i] = src.regfile[i]; - } - dst.check_value = src.check_value; - } - - static void copy_from_data_t(const data_t& src, backup_ram_t& dst) { - const uint32_t* const src_words = (uint32_t*)&src; - const size_t word_count = (sizeof(data_t) + 3) / 4; - for(size_t i=0; i<63; i++) { - if(i crc { 0x04c11db7, 0xffffffff, 0xffffffff }; - for(size_t i=0; i<63; i++) { - const auto word = regfile[i]; - crc.process_byte((word >> 0) & 0xff); - crc.process_byte((word >> 8) & 0xff); - crc.process_byte((word >> 16) & 0xff); - crc.process_byte((word >> 24) & 0xff); - } - return crc.checksum(); - } - - public: - /* default constructor */ - backup_ram_t() : - check_value(0) - { - const data_t defaults = data_t(); - copy_from_data_t(defaults, *this); - } - - /* copy-assignment operator */ - backup_ram_t& operator=(const backup_ram_t& src) { - copy(src, *this); - return *this; - } - - /* Calculate a check value from `this`, and check against - * the stored value. - */ - bool is_valid() { - return compute_check_value() == check_value; - } - - /* Assuming `this` contains valid data, update the checksum - * and copy to the destination. - */ - void persist_to(backup_ram_t& dst) { - check_value = compute_check_value(); - copy(*this, dst); - } - }; - - static_assert(sizeof(backup_ram_t) == memory::map::backup_ram.size()); - static_assert(sizeof(data_t) <= sizeof(backup_ram_t) - sizeof(uint32_t)); - - static backup_ram_t* const backup_ram = reinterpret_cast(memory::map::backup_ram.base()); - - static backup_ram_t cached_backup_ram; - static data_t* data = reinterpret_cast(&cached_backup_ram); - - namespace cache { - - void defaults() { - cached_backup_ram = backup_ram_t(); - - // defaults values for recon app - set_recon_autosave_freqs( false ); - set_recon_autostart_recon( true ); - set_recon_continuous( true ); - set_recon_clear_output( false ); - set_recon_load_freqs( true ); - set_recon_load_ranges( true ); - set_recon_update_ranges_when_recon( true ); - set_recon_load_hamradios( true ); - set_recon_match_mode( 0 ); - - } - - void init() { - if(backup_ram->is_valid()) { - // Copy valid persistent data into cache. - cached_backup_ram = *backup_ram; - - // Check that structure data we copied into cache is the expected - // version. If not, initialize cache to defaults. - if(data->structure_version != data_structure_version_enum::VERSION_CURRENT) { - // TODO: Can provide version-to-version upgrade functions here, - // if we want to be fancy. - defaults(); - } - } else { - // Copy defaults into cache. - defaults(); - } - } - - void persist() { - cached_backup_ram.persist_to(*backup_ram); - } - - } /* namespace cache */ - - rf::Frequency tuned_frequency() { - rf::tuning_range.reset_if_outside(data->tuned_frequency, tuned_frequency_reset_value); - return data->tuned_frequency; - } - - void set_tuned_frequency(const rf::Frequency new_value) { - data->tuned_frequency = rf::tuning_range.clip(new_value); - } - - ppb_t correction_ppb() { - ppb_range.reset_if_outside(data->correction_ppb, ppb_reset_value); - return data->correction_ppb; - } - - void set_correction_ppb(const ppb_t new_value) { - const auto clipped_value = ppb_range.clip(new_value); - data->correction_ppb = clipped_value; - portapack::clock_manager.set_reference_ppb(clipped_value); - } - - void set_touch_calibration(const touch::Calibration& new_value) { - data->touch_calibration = new_value; - data->touch_calibration_magic = TOUCH_CALIBRATION_MAGIC; - } - - const touch::Calibration& touch_calibration() { - if( data->touch_calibration_magic != TOUCH_CALIBRATION_MAGIC ) { - set_touch_calibration(touch::Calibration()); - } - return data->touch_calibration; - } - - int32_t tone_mix() { - tone_mix_range.reset_if_outside(data->tone_mix, tone_mix_reset_value); - return data->tone_mix; - } - - void set_tone_mix(const int32_t new_value) { - data->tone_mix = tone_mix_range.clip(new_value); - } - - int32_t afsk_mark_freq() { - afsk_freq_range.reset_if_outside(data->afsk_mark_freq, afsk_mark_reset_value); - return data->afsk_mark_freq; - } - - void set_afsk_mark(const int32_t new_value) { - data->afsk_mark_freq = afsk_freq_range.clip(new_value); - } - - int32_t afsk_space_freq() { - afsk_freq_range.reset_if_outside(data->afsk_space_freq, afsk_space_reset_value); - return data->afsk_space_freq; - } - - void set_afsk_space(const int32_t new_value) { - data->afsk_space_freq = afsk_freq_range.clip(new_value); - } - - int32_t modem_baudrate() { - modem_baudrate_range.reset_if_outside(data->modem_baudrate, modem_baudrate_reset_value); - return data->modem_baudrate; - } - - void set_modem_baudrate(const int32_t new_value) { - data->modem_baudrate = modem_baudrate_range.clip(new_value); - } - - /*int32_t modem_bw() { - modem_bw_range.reset_if_outside(data->modem_bw, modem_bw_reset_value); - return data->modem_bw; - } - - void set_modem_bw(const int32_t new_value) { - data->modem_bw = modem_bw_range.clip(new_value); - }*/ - - uint8_t modem_repeat() { - modem_repeat_range.reset_if_outside(data->modem_repeat, modem_repeat_reset_value); - return data->modem_repeat; - } - - void set_modem_repeat(const uint32_t new_value) { - data->modem_repeat = modem_repeat_range.clip(new_value); - } - - serial_format_t serial_format() { - return data->serial_format; - } - - void set_serial_format(const serial_format_t new_value) { - data->serial_format = new_value; - } - - bool show_gui_return_icon() { // add return icon in touchscreen menue - return data->ui_config.show_gui_return_icon(); - } - - bool load_app_settings() { // load (last saved) app settings on startup of app - return data->ui_config.load_app_settings(); - } - - bool save_app_settings() { // save app settings when closing app - return data->ui_config.save_app_settings(); - } - - bool show_bigger_qr_code() { // show bigger QR code - return data->ui_config.show_bigger_qr_code(); - } - - bool disable_touchscreen() { // Option to disable touch screen - return data->ui_config.disable_touchscreen(); - } - - bool hide_clock() { // clock hidden from main menu - return data->ui_config.hide_clock(); - } - - bool clock_with_date() { // show clock with date, if not hidden - return data->ui_config.clock_with_date(); - } - - bool clkout_enabled() { - return data->ui_config.clkout_enabled(); - } - - bool config_speaker() { - return data->ui_config.config_speaker(); - } - - bool stealth_mode() { - return data->ui_config.stealth_mode(); - } - - bool config_login() { - return data->ui_config.config_login(); - } - - bool config_splash() { - return data->ui_config.config_splash(); - } - - uint8_t config_cpld() { - return data->hardware_config; - } - - backlight_config_t config_backlight_timer() { - return data->ui_config.config_backlight_timer(); - } - - void set_gui_return_icon(bool v) { - data->ui_config.set_gui_return_icon(v); - } - - void set_load_app_settings(bool v) { - data->ui_config.set_load_app_settings(v); - } - - void set_save_app_settings(bool v) { - data->ui_config.set_save_app_settings(v); - } - - void set_show_bigger_qr_code(bool v) { - data->ui_config.set_show_bigger_qr_code(v); - } - - void set_disable_touchscreen(bool v) { - data->ui_config.set_disable_touchscreen(v); - } - - void set_clock_hidden(bool v) { - data->ui_config.set_clock_hidden(v); - } - - void set_clock_with_date(bool v) { - data->ui_config.set_clock_with_date(v); - } - - void set_clkout_enabled(bool v) { - data->ui_config.set_clkout_enabled(v); - } - - void set_config_speaker(bool v) { - data->ui_config.set_config_speaker(v); - } - - void set_stealth_mode(bool v) { - data->ui_config.set_stealth_mode(v); - } - - void set_config_login(bool v) { - data->ui_config.set_config_login(v); - } - - void set_config_splash(bool v) { - data->ui_config.set_config_splash(v); - } - - void set_config_cpld(uint8_t i) { - data->hardware_config = i; - } - - void set_config_backlight_timer(const backlight_config_t& new_value) { - data->ui_config.set_config_backlight_timer(new_value); - } - - /*void set_config_textentry(uint8_t new_value) { - data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2); - } - - uint8_t ui_config_textentry() { - return ((data->ui_config >> 2) & 1); - }*/ - - /*void set_ui_config(const uint32_t new_value) { - data->ui_config = new_value; - }*/ - - uint32_t pocsag_last_address() { - return data->pocsag_last_address; - } - - void set_pocsag_last_address(uint32_t address) { - data->pocsag_last_address = address; - } - - uint32_t pocsag_ignore_address() { - return data->pocsag_ignore_address; - } - - void set_pocsag_ignore_address(uint32_t address) { - data->pocsag_ignore_address = address; - } - - uint32_t clkout_freq() { - return data->ui_config.clkout_freq(); - } - - void set_clkout_freq(uint32_t freq) { - data->ui_config.set_clkout_freq(freq); - } - - bool recon_autosave_freqs() { - return (data->recon_config & 0x80000000UL) ? true : false; - } - bool recon_autostart_recon() { - return (data->recon_config & 0x40000000UL) ? true : false; - } - bool recon_continuous() { - return (data->recon_config & 0x20000000UL) ? true : false; - } - bool recon_clear_output() { - return (data->recon_config & 0x10000000UL) ? true : false; - } - bool recon_load_freqs() { - return (data->recon_config & 0x08000000UL) ? true : false; - } - bool recon_load_ranges() { - return (data->recon_config & 0x04000000UL) ? true : false; - } - bool recon_update_ranges_when_recon() { - return (data->recon_config & 0x02000000UL) ? true : false; - } - bool recon_load_hamradios() { - return (data->recon_config & 0x01000000UL) ? true : false; - } - bool recon_match_mode() { - return (data->recon_config & 0x00800000UL) ? true : false; - } - - void set_recon_autosave_freqs(const bool v ){ - data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31); - } - void set_recon_autostart_recon(const bool v ){ - data->recon_config = (data->recon_config & ~0x40000000UL) | (v << 30); - } - void set_recon_continuous(const bool v ){ - data->recon_config = (data->recon_config & ~0x20000000UL) | (v << 29); - } - void set_recon_clear_output(const bool v ){ - data->recon_config = (data->recon_config & ~0x10000000UL) | (v << 28); - } - void set_recon_load_freqs(const bool v ){ - data->recon_config = (data->recon_config & ~0x08000000UL) | (v << 27); - } - void set_recon_load_ranges(const bool v ){ - data->recon_config = (data->recon_config & ~0x04000000UL) | (v << 26); - } - void set_recon_update_ranges_when_recon(const bool v ){ - data->recon_config = (data->recon_config & ~0x02000000UL) | (v << 25); - } - void set_recon_load_hamradios(const bool v ){ - data->recon_config = (data->recon_config & ~0x01000000UL) | (v << 24); - } - void set_recon_match_mode(const bool v ) { - data->recon_config = (data->recon_config & ~0x00800000UL) | (v << 23); - } - bool config_hide_converter() { - return data->hide_converter; - } - bool config_converter() { - return data->converter; - } - bool config_updown_converter() { - return data->updown_converter; - } - int64_t config_converter_freq() { - return data->converter_frequency_offset ; - } - void set_config_hide_converter(const bool v ){ - data-> hide_converter = v ; - if( v ) - { - data -> converter = false ; - } - } - void set_config_converter(const bool v ){ - data-> converter = v ; - } - void set_config_updown_converter(const bool v){ - data-> updown_converter = v ; - } - void set_config_converter_freq(const int64_t v ){ - data-> converter_frequency_offset = v ; - } - - // sd persisting settings - int save_persistent_settings_to_file( std::string filename ) - { - delete_file( filename ); - File outfile ; - auto result = outfile.create(filename); - if( result.is_valid() ) - { - return false ; - } - outfile.write( reinterpret_cast(&cached_backup_ram), sizeof(backup_ram_t) ); - return true ; - } - - int load_persistent_settings_from_file( std::string filename ) - { - File infile ; - auto result = infile.open(filename); - if( !result.is_valid() ) - { - infile.read( reinterpret_cast(&cached_backup_ram), sizeof(backup_ram_t) ); - return true ; - } - return false ; - } - - } /* namespace persistent_memory */ +namespace persistent_memory { + +constexpr rf::Frequency tuned_frequency_reset_value{100000000}; + +using ppb_range_t = range_t; +constexpr ppb_range_t ppb_range{-99000, 99000}; +constexpr ppb_t ppb_reset_value{0}; + +using tone_mix_range_t = range_t; +constexpr tone_mix_range_t tone_mix_range{10, 99}; +constexpr int32_t tone_mix_reset_value{20}; + +using afsk_freq_range_t = range_t; +constexpr afsk_freq_range_t afsk_freq_range{1, 4000}; +constexpr int32_t afsk_mark_reset_value{1200}; +constexpr int32_t afsk_space_reset_value{2200}; + +using modem_baudrate_range_t = range_t; +constexpr modem_baudrate_range_t modem_baudrate_range{50, 9600}; +constexpr int32_t modem_baudrate_reset_value{1200}; + +/*using modem_bw_range_t = range_t; + constexpr modem_bw_range_t modem_bw_range { 1000, 50000 }; + constexpr int32_t modem_bw_reset_value { 15000 };*/ + +using modem_repeat_range_t = range_t; +constexpr modem_repeat_range_t modem_repeat_range{1, 99}; +constexpr int32_t modem_repeat_reset_value{5}; + +using clkout_freq_range_t = range_t; +constexpr clkout_freq_range_t clkout_freq_range{10, 60000}; +constexpr uint32_t clkout_freq_reset_value{10000}; + +enum data_structure_version_enum : uint32_t { + VERSION_CURRENT = 0x10000002, +}; + +static const uint32_t TOUCH_CALIBRATION_MAGIC = 0x074af82f; + +struct ui_config_t { + private: + enum bits_t { + BacklightTimeoutLSB = 0, + BacklightTimeoutEnable = 3, + ClkoutFreqLSB = 4, + ShowGUIReturnIcon = 20, + LoadAppSettings = 21, + SaveAppSettings = 22, + ShowBiggerQRCode = 23, + DisableTouchscreen = 24, + HideClock = 25, + ClockWithDate = 26, + ClkOutEnabled = 27, + ConfigSpeaker = 28, + StealthMode = 29, + ConfigLogin = 30, + ConfigSplash = 31, + }; + + enum bits_mask_t : uint32_t { + BacklightTimeoutMask = ((1 << 3) - 1) << bits_t::BacklightTimeoutLSB, + ClkoutFreqMask = ((1 << 16) - 1) << bits_t::ClkoutFreqLSB, + }; + + uint32_t values; + + constexpr bool bit_read(const bits_t n) const { + return ((values >> n) & 1) != 0; + } + + constexpr void bit_write(const bits_t n, const bool v) { + if (bit_read(n) != v) { + values ^= 1 << n; + } + } + + public: + backlight_config_t config_backlight_timer() { + const auto timeout_enum = (backlight_timeout_t)((values & bits_mask_t::BacklightTimeoutMask) >> bits_t::BacklightTimeoutLSB); + const bool timeout_enabled = bit_read(bits_t::BacklightTimeoutEnable); + return backlight_config_t(timeout_enum, timeout_enabled); + } + + void set_config_backlight_timer(const backlight_config_t& new_value) { + values = (values & ~bits_mask_t::BacklightTimeoutMask) | ((new_value.timeout_enum() << bits_t::BacklightTimeoutLSB) & bits_mask_t::BacklightTimeoutMask); + bit_write(bits_t::BacklightTimeoutEnable, new_value.timeout_enabled()); + } + + constexpr uint32_t clkout_freq() { + uint32_t freq = (values & bits_mask_t::ClkoutFreqMask) >> bits_t::ClkoutFreqLSB; + if (freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) { + values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_reset_value << bits_t::ClkoutFreqLSB); + return clkout_freq_reset_value; + } else { + return freq; + } + } + + constexpr void set_clkout_freq(uint32_t freq) { + values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_range.clip(freq) << bits_t::ClkoutFreqLSB); + } + + // ui_config is an uint32_t var storing information bitwise + // bits 0-2 store the backlight timer + // bits 4-19 (16 bits) store the clkout frequency + // bits 21-31 store the different single bit configs depicted below + // bit 20 store the display state of the gui return icon, hidden (0) or shown (1) + + constexpr bool show_gui_return_icon() const { // add return icon in touchscreen menue + return bit_read(bits_t::ShowGUIReturnIcon); + } + + constexpr void set_gui_return_icon(bool v) { + bit_write(bits_t::ShowGUIReturnIcon, v); + } + + constexpr bool load_app_settings() const { // load (last saved) app settings on startup of app + return bit_read(bits_t::LoadAppSettings); + } + + constexpr void set_load_app_settings(bool v) { + bit_write(bits_t::LoadAppSettings, v); + } + + constexpr bool save_app_settings() const { // save app settings when closing app + return bit_read(bits_t::SaveAppSettings); + } + + constexpr void set_save_app_settings(bool v) { + bit_write(bits_t::SaveAppSettings, v); + } + + constexpr bool show_bigger_qr_code() const { // show bigger QR code + return bit_read(bits_t::ShowBiggerQRCode); + } + + constexpr void set_show_bigger_qr_code(bool v) { + bit_write(bits_t::ShowBiggerQRCode, v); + } + + constexpr bool disable_touchscreen() const { // Option to disable touch screen + return bit_read(bits_t::DisableTouchscreen); + } + + constexpr void set_disable_touchscreen(bool v) { + bit_write(bits_t::DisableTouchscreen, v); + } + + constexpr bool hide_clock() const { // clock hidden from main menu + return bit_read(bits_t::HideClock); + } + + constexpr void set_clock_hidden(bool v) { + bit_write(bits_t::HideClock, v); + } + + constexpr bool clock_with_date() const { // show clock with date, if not hidden + return bit_read(bits_t::ClockWithDate); + } + + constexpr void set_clock_with_date(bool v) { + bit_write(bits_t::ClockWithDate, v); + } + + constexpr bool clkout_enabled() const { + return bit_read(bits_t::ClkOutEnabled); + } + + constexpr void set_clkout_enabled(bool v) { + bit_write(bits_t::ClkOutEnabled, v); + } + + constexpr bool config_speaker() const { + return bit_read(bits_t::ConfigSpeaker); + } + + constexpr void set_config_speaker(bool v) { + bit_write(bits_t::ConfigSpeaker, v); + } + + constexpr bool stealth_mode() const { + return bit_read(bits_t::StealthMode); + } + + constexpr void set_stealth_mode(bool v) { + bit_write(bits_t::StealthMode, v); + } + + constexpr bool config_login() const { + return bit_read(bits_t::ConfigLogin); + } + + constexpr void set_config_login(bool v) { + bit_write(bits_t::ConfigLogin, v); + } + + constexpr bool config_splash() const { + return bit_read(bits_t::ConfigSplash); + } + + constexpr void set_config_splash(bool v) { + bit_write(bits_t::ConfigSplash, v); + } + + constexpr ui_config_t() + : values( + (1 << ConfigSplash) | (1 << ConfigSpeaker) | (clkout_freq_reset_value << ClkoutFreqLSB) | (7 << BacklightTimeoutLSB)) { + } +}; + +/* struct must pack the same way on M4 and M0 cores. */ +struct data_t { + data_structure_version_enum structure_version; + int64_t tuned_frequency; + int32_t correction_ppb; + uint32_t touch_calibration_magic; + touch::Calibration touch_calibration; + + // Modem + uint32_t modem_def_index; + serial_format_t serial_format; + int32_t modem_bw; + int32_t afsk_mark_freq; + int32_t afsk_space_freq; + int32_t modem_baudrate; + int32_t modem_repeat; + + // Play dead unlock + uint32_t playdead_magic; + uint32_t playing_dead; + uint32_t playdead_sequence; + + // UI + ui_config_t ui_config; + + uint32_t pocsag_last_address; + uint32_t pocsag_ignore_address; + + int32_t tone_mix; + + // Hardware + uint32_t hardware_config; + + // Recon App + uint64_t recon_config; + + // converter: show or hide icon. Hiding cause auto disable to avoid mistakes + bool hide_converter; + // enable or disable converter + bool converter; + // set up converter (false) or down converter (true) converter + bool updown_converter; + // up/down converter offset + int64_t converter_frequency_offset; + + constexpr data_t() + : structure_version(data_structure_version_enum::VERSION_CURRENT), + tuned_frequency(tuned_frequency_reset_value), + correction_ppb(ppb_reset_value), + touch_calibration_magic(TOUCH_CALIBRATION_MAGIC), + touch_calibration(touch::Calibration()), + + modem_def_index(0), // TODO: Unused? + serial_format(), + modem_bw(15000), // TODO: Unused? + afsk_mark_freq(afsk_mark_reset_value), + afsk_space_freq(afsk_space_reset_value), + modem_baudrate(modem_baudrate_reset_value), + modem_repeat(modem_repeat_reset_value), + + playdead_magic(), // TODO: Unused? + playing_dead(), // TODO: Unused? + playdead_sequence(), // TODO: Unused? + + ui_config(), + + pocsag_last_address(0), // TODO: A better default? + pocsag_ignore_address(0), // TODO: A better default? + + tone_mix(tone_mix_reset_value), + + hardware_config(0), + recon_config(0), + hide_converter(0), + converter(0), + updown_converter(0), + converter_frequency_offset(0) { + } +}; + +struct backup_ram_t { + private: + uint32_t regfile[63]; + uint32_t check_value; + + static void copy(const backup_ram_t& src, backup_ram_t& dst) { + for (size_t i = 0; i < 63; i++) { + dst.regfile[i] = src.regfile[i]; + } + dst.check_value = src.check_value; + } + + static void copy_from_data_t(const data_t& src, backup_ram_t& dst) { + const uint32_t* const src_words = (uint32_t*)&src; + const size_t word_count = (sizeof(data_t) + 3) / 4; + for (size_t i = 0; i < 63; i++) { + if (i < word_count) { + dst.regfile[i] = src_words[i]; + } else { + dst.regfile[i] = 0; + } + } + } + + uint32_t compute_check_value() { + CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff}; + for (size_t i = 0; i < 63; i++) { + const auto word = regfile[i]; + crc.process_byte((word >> 0) & 0xff); + crc.process_byte((word >> 8) & 0xff); + crc.process_byte((word >> 16) & 0xff); + crc.process_byte((word >> 24) & 0xff); + } + return crc.checksum(); + } + + public: + /* default constructor */ + backup_ram_t() + : check_value(0) { + const data_t defaults = data_t(); + copy_from_data_t(defaults, *this); + } + + /* copy-assignment operator */ + backup_ram_t& operator=(const backup_ram_t& src) { + copy(src, *this); + return *this; + } + + /* Calculate a check value from `this`, and check against + * the stored value. + */ + bool is_valid() { + return compute_check_value() == check_value; + } + + /* Assuming `this` contains valid data, update the checksum + * and copy to the destination. + */ + void persist_to(backup_ram_t& dst) { + check_value = compute_check_value(); + copy(*this, dst); + } +}; + +static_assert(sizeof(backup_ram_t) == memory::map::backup_ram.size()); +static_assert(sizeof(data_t) <= sizeof(backup_ram_t) - sizeof(uint32_t)); + +static backup_ram_t* const backup_ram = reinterpret_cast(memory::map::backup_ram.base()); + +static backup_ram_t cached_backup_ram; +static data_t* data = reinterpret_cast(&cached_backup_ram); + +namespace cache { + +void defaults() { + cached_backup_ram = backup_ram_t(); + + // defaults values for recon app + set_recon_autosave_freqs(false); + set_recon_autostart_recon(true); + set_recon_continuous(true); + set_recon_clear_output(false); + set_recon_load_freqs(true); + set_recon_load_ranges(true); + set_recon_update_ranges_when_recon(true); + set_recon_load_hamradios(true); + set_recon_match_mode(0); +} + +void init() { + if (backup_ram->is_valid()) { + // Copy valid persistent data into cache. + cached_backup_ram = *backup_ram; + + // Check that structure data we copied into cache is the expected + // version. If not, initialize cache to defaults. + if (data->structure_version != data_structure_version_enum::VERSION_CURRENT) { + // TODO: Can provide version-to-version upgrade functions here, + // if we want to be fancy. + defaults(); + } + } else { + // Copy defaults into cache. + defaults(); + } +} + +void persist() { + cached_backup_ram.persist_to(*backup_ram); +} + +} /* namespace cache */ + +rf::Frequency tuned_frequency() { + rf::tuning_range.reset_if_outside(data->tuned_frequency, tuned_frequency_reset_value); + return data->tuned_frequency; +} + +void set_tuned_frequency(const rf::Frequency new_value) { + data->tuned_frequency = rf::tuning_range.clip(new_value); +} + +ppb_t correction_ppb() { + ppb_range.reset_if_outside(data->correction_ppb, ppb_reset_value); + return data->correction_ppb; +} + +void set_correction_ppb(const ppb_t new_value) { + const auto clipped_value = ppb_range.clip(new_value); + data->correction_ppb = clipped_value; + portapack::clock_manager.set_reference_ppb(clipped_value); +} + +void set_touch_calibration(const touch::Calibration& new_value) { + data->touch_calibration = new_value; + data->touch_calibration_magic = TOUCH_CALIBRATION_MAGIC; +} + +const touch::Calibration& touch_calibration() { + if (data->touch_calibration_magic != TOUCH_CALIBRATION_MAGIC) { + set_touch_calibration(touch::Calibration()); + } + return data->touch_calibration; +} + +int32_t tone_mix() { + tone_mix_range.reset_if_outside(data->tone_mix, tone_mix_reset_value); + return data->tone_mix; +} + +void set_tone_mix(const int32_t new_value) { + data->tone_mix = tone_mix_range.clip(new_value); +} + +int32_t afsk_mark_freq() { + afsk_freq_range.reset_if_outside(data->afsk_mark_freq, afsk_mark_reset_value); + return data->afsk_mark_freq; +} + +void set_afsk_mark(const int32_t new_value) { + data->afsk_mark_freq = afsk_freq_range.clip(new_value); +} + +int32_t afsk_space_freq() { + afsk_freq_range.reset_if_outside(data->afsk_space_freq, afsk_space_reset_value); + return data->afsk_space_freq; +} + +void set_afsk_space(const int32_t new_value) { + data->afsk_space_freq = afsk_freq_range.clip(new_value); +} + +int32_t modem_baudrate() { + modem_baudrate_range.reset_if_outside(data->modem_baudrate, modem_baudrate_reset_value); + return data->modem_baudrate; +} + +void set_modem_baudrate(const int32_t new_value) { + data->modem_baudrate = modem_baudrate_range.clip(new_value); +} + +/*int32_t modem_bw() { + modem_bw_range.reset_if_outside(data->modem_bw, modem_bw_reset_value); + return data->modem_bw; + } + + void set_modem_bw(const int32_t new_value) { + data->modem_bw = modem_bw_range.clip(new_value); + }*/ + +uint8_t modem_repeat() { + modem_repeat_range.reset_if_outside(data->modem_repeat, modem_repeat_reset_value); + return data->modem_repeat; +} + +void set_modem_repeat(const uint32_t new_value) { + data->modem_repeat = modem_repeat_range.clip(new_value); +} + +serial_format_t serial_format() { + return data->serial_format; +} + +void set_serial_format(const serial_format_t new_value) { + data->serial_format = new_value; +} + +bool show_gui_return_icon() { // add return icon in touchscreen menue + return data->ui_config.show_gui_return_icon(); +} + +bool load_app_settings() { // load (last saved) app settings on startup of app + return data->ui_config.load_app_settings(); +} + +bool save_app_settings() { // save app settings when closing app + return data->ui_config.save_app_settings(); +} + +bool show_bigger_qr_code() { // show bigger QR code + return data->ui_config.show_bigger_qr_code(); +} + +bool disable_touchscreen() { // Option to disable touch screen + return data->ui_config.disable_touchscreen(); +} + +bool hide_clock() { // clock hidden from main menu + return data->ui_config.hide_clock(); +} + +bool clock_with_date() { // show clock with date, if not hidden + return data->ui_config.clock_with_date(); +} + +bool clkout_enabled() { + return data->ui_config.clkout_enabled(); +} + +bool config_speaker() { + return data->ui_config.config_speaker(); +} + +bool stealth_mode() { + return data->ui_config.stealth_mode(); +} + +bool config_login() { + return data->ui_config.config_login(); +} + +bool config_splash() { + return data->ui_config.config_splash(); +} + +uint8_t config_cpld() { + return data->hardware_config; +} + +backlight_config_t config_backlight_timer() { + return data->ui_config.config_backlight_timer(); +} + +void set_gui_return_icon(bool v) { + data->ui_config.set_gui_return_icon(v); +} + +void set_load_app_settings(bool v) { + data->ui_config.set_load_app_settings(v); +} + +void set_save_app_settings(bool v) { + data->ui_config.set_save_app_settings(v); +} + +void set_show_bigger_qr_code(bool v) { + data->ui_config.set_show_bigger_qr_code(v); +} + +void set_disable_touchscreen(bool v) { + data->ui_config.set_disable_touchscreen(v); +} + +void set_clock_hidden(bool v) { + data->ui_config.set_clock_hidden(v); +} + +void set_clock_with_date(bool v) { + data->ui_config.set_clock_with_date(v); +} + +void set_clkout_enabled(bool v) { + data->ui_config.set_clkout_enabled(v); +} + +void set_config_speaker(bool v) { + data->ui_config.set_config_speaker(v); +} + +void set_stealth_mode(bool v) { + data->ui_config.set_stealth_mode(v); +} + +void set_config_login(bool v) { + data->ui_config.set_config_login(v); +} + +void set_config_splash(bool v) { + data->ui_config.set_config_splash(v); +} + +void set_config_cpld(uint8_t i) { + data->hardware_config = i; +} + +void set_config_backlight_timer(const backlight_config_t& new_value) { + data->ui_config.set_config_backlight_timer(new_value); +} + +/*void set_config_textentry(uint8_t new_value) { + data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2); + } + + uint8_t ui_config_textentry() { + return ((data->ui_config >> 2) & 1); + }*/ + +/*void set_ui_config(const uint32_t new_value) { + data->ui_config = new_value; + }*/ + +uint32_t pocsag_last_address() { + return data->pocsag_last_address; +} + +void set_pocsag_last_address(uint32_t address) { + data->pocsag_last_address = address; +} + +uint32_t pocsag_ignore_address() { + return data->pocsag_ignore_address; +} + +void set_pocsag_ignore_address(uint32_t address) { + data->pocsag_ignore_address = address; +} + +uint32_t clkout_freq() { + return data->ui_config.clkout_freq(); +} + +void set_clkout_freq(uint32_t freq) { + data->ui_config.set_clkout_freq(freq); +} + +bool recon_autosave_freqs() { + return (data->recon_config & 0x80000000UL) ? true : false; +} +bool recon_autostart_recon() { + return (data->recon_config & 0x40000000UL) ? true : false; +} +bool recon_continuous() { + return (data->recon_config & 0x20000000UL) ? true : false; +} +bool recon_clear_output() { + return (data->recon_config & 0x10000000UL) ? true : false; +} +bool recon_load_freqs() { + return (data->recon_config & 0x08000000UL) ? true : false; +} +bool recon_load_ranges() { + return (data->recon_config & 0x04000000UL) ? true : false; +} +bool recon_update_ranges_when_recon() { + return (data->recon_config & 0x02000000UL) ? true : false; +} +bool recon_load_hamradios() { + return (data->recon_config & 0x01000000UL) ? true : false; +} +bool recon_match_mode() { + return (data->recon_config & 0x00800000UL) ? true : false; +} + +void set_recon_autosave_freqs(const bool v) { + data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31); +} +void set_recon_autostart_recon(const bool v) { + data->recon_config = (data->recon_config & ~0x40000000UL) | (v << 30); +} +void set_recon_continuous(const bool v) { + data->recon_config = (data->recon_config & ~0x20000000UL) | (v << 29); +} +void set_recon_clear_output(const bool v) { + data->recon_config = (data->recon_config & ~0x10000000UL) | (v << 28); +} +void set_recon_load_freqs(const bool v) { + data->recon_config = (data->recon_config & ~0x08000000UL) | (v << 27); +} +void set_recon_load_ranges(const bool v) { + data->recon_config = (data->recon_config & ~0x04000000UL) | (v << 26); +} +void set_recon_update_ranges_when_recon(const bool v) { + data->recon_config = (data->recon_config & ~0x02000000UL) | (v << 25); +} +void set_recon_load_hamradios(const bool v) { + data->recon_config = (data->recon_config & ~0x01000000UL) | (v << 24); +} +void set_recon_match_mode(const bool v) { + data->recon_config = (data->recon_config & ~0x00800000UL) | (v << 23); +} +bool config_hide_converter() { + return data->hide_converter; +} +bool config_converter() { + return data->converter; +} +bool config_updown_converter() { + return data->updown_converter; +} +int64_t config_converter_freq() { + return data->converter_frequency_offset; +} +void set_config_hide_converter(const bool v) { + data->hide_converter = v; + if (v) { + data->converter = false; + } +} +void set_config_converter(const bool v) { + data->converter = v; +} +void set_config_updown_converter(const bool v) { + data->updown_converter = v; +} +void set_config_converter_freq(const int64_t v) { + data->converter_frequency_offset = v; +} + +// sd persisting settings +int save_persistent_settings_to_file(std::string filename) { + delete_file(filename); + File outfile; + auto result = outfile.create(filename); + if (result.is_valid()) { + return false; + } + outfile.write(reinterpret_cast(&cached_backup_ram), sizeof(backup_ram_t)); + return true; +} + +int load_persistent_settings_from_file(std::string filename) { + File infile; + auto result = infile.open(filename); + if (!result.is_valid()) { + infile.read(reinterpret_cast(&cached_backup_ram), sizeof(backup_ram_t)); + return true; + } + return false; +} + +} /* namespace persistent_memory */ } /* namespace portapack */ diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index c9770f373..24739f82e 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -37,192 +37,197 @@ using namespace serializer; namespace portapack { - namespace persistent_memory { - - enum backlight_timeout_t { - Timeout5Sec = 0, - Timeout15Sec = 1, - Timeout30Sec = 2, - Timeout60Sec = 3, - Timeout180Sec = 4, - Timeout300Sec = 5, - Timeout600Sec = 6, - Timeout3600Sec = 7, - }; - - struct backlight_config_t { - public: - backlight_config_t() : - _timeout_enum(backlight_timeout_t::Timeout600Sec), - _timeout_enabled(false) - { - } - - backlight_config_t( - backlight_timeout_t timeout_enum, - bool timeout_enabled - ) : - _timeout_enum(timeout_enum), - _timeout_enabled(timeout_enabled) - { - } - - bool timeout_enabled() const { - return _timeout_enabled; - } - - backlight_timeout_t timeout_enum() const { - return _timeout_enum; - } - - uint32_t timeout_seconds() const { - switch(timeout_enum()) { - case Timeout5Sec: return 5; - case Timeout15Sec: return 15; - case Timeout30Sec: return 30; - case Timeout60Sec: return 60; - case Timeout180Sec: return 180; - case Timeout300Sec: return 300; - default: - case Timeout600Sec: return 600; - case Timeout3600Sec: return 3600; - } - } - - private: - backlight_timeout_t _timeout_enum; - bool _timeout_enabled; - }; - - namespace cache { - - /* Set values in cache to sensible defaults. */ - void defaults(); - - /* Load cached settings from values in persistent RAM, replacing with defaults - * if persistent RAM contents appear to be invalid. */ - void init(); - - /* Calculate a check value for cached settings, and copy the check value and - * settings into persistent RAM. Intended to be called periodically to update - * persistent settings with current settings. */ - void persist(); - - } /* namespace cache */ - - using ppb_t = int32_t; - - rf::Frequency tuned_frequency(); - void set_tuned_frequency(const rf::Frequency new_value); - - ppb_t correction_ppb(); - void set_correction_ppb(const ppb_t new_value); - - void set_touch_calibration(const touch::Calibration& new_value); - const touch::Calibration& touch_calibration(); - - serial_format_t serial_format(); - void set_serial_format(const serial_format_t new_value); - - int32_t tone_mix(); - void set_tone_mix(const int32_t new_value); - - int32_t afsk_mark_freq(); - void set_afsk_mark(const int32_t new_value); - - int32_t afsk_space_freq(); - void set_afsk_space(const int32_t new_value); - - int32_t modem_baudrate(); - void set_modem_baudrate(const int32_t new_value); - - uint8_t modem_repeat(); - void set_modem_repeat(const uint32_t new_value); - - uint32_t playing_dead(); - void set_playing_dead(const uint32_t new_value); - - uint32_t playdead_sequence(); - void set_playdead_sequence(const uint32_t new_value); - - bool stealth_mode(); - void set_stealth_mode(const bool v); - - uint8_t config_cpld(); - void set_config_cpld(uint8_t i); - - bool config_splash(); - bool config_hide_converter(); - bool config_converter(); - bool config_updown_converter(); - int64_t config_converter_freq(); - bool show_gui_return_icon(); - bool load_app_settings(); - bool save_app_settings(); - bool show_bigger_qr_code(); - bool hide_clock(); - bool clock_with_date(); - bool config_login(); - bool config_speaker(); - backlight_config_t config_backlight_timer(); - bool disable_touchscreen(); - - void set_gui_return_icon(bool v); - void set_load_app_settings(bool v); - void set_save_app_settings(bool v); - void set_show_bigger_qr_code(bool v); - void set_config_splash(bool v); - void set_config_hide_converter(bool v); - void set_config_converter(bool v); - void set_config_updown_converter(const bool v); - void set_config_converter_freq(const int64_t v ); - void set_clock_hidden(bool v); - void set_clock_with_date(bool v); - void set_config_login(bool v); - void set_config_speaker(bool v); - void set_config_backlight_timer(const backlight_config_t& new_value); - void set_disable_touchscreen(bool v); - - //uint8_t ui_config_textentry(); - //void set_config_textentry(uint8_t new_value); - - uint32_t pocsag_last_address(); - void set_pocsag_last_address(uint32_t address); - - uint32_t pocsag_ignore_address(); - void set_pocsag_ignore_address(uint32_t address); - - bool clkout_enabled(); - void set_clkout_enabled(bool v); - uint32_t clkout_freq(); - void set_clkout_freq(uint32_t freq); - - /* Recon app */ - bool recon_autosave_freqs(); - bool recon_autostart_recon(); - bool recon_continuous(); - bool recon_clear_output(); - bool recon_load_freqs(); - bool recon_load_ranges(); - bool recon_update_ranges_when_recon(); - bool recon_load_hamradios(); - bool recon_match_mode(); - void set_recon_autosave_freqs(const bool v); - void set_recon_autostart_recon(const bool v); - void set_recon_continuous(const bool v); - void set_recon_clear_output(const bool v); - void set_recon_load_freqs(const bool v); - void set_recon_load_ranges(const bool v); - void set_recon_update_ranges_when_recon(const bool v); - void set_recon_load_hamradios(const bool v ); - void set_recon_match_mode( const bool v ); - - // sd persisting settings - int save_persistent_settings_to_file( std::string filename ); - int load_persistent_settings_from_file( std::string filename ); - - } /* namespace persistent_memory */ +namespace persistent_memory { + +enum backlight_timeout_t { + Timeout5Sec = 0, + Timeout15Sec = 1, + Timeout30Sec = 2, + Timeout60Sec = 3, + Timeout180Sec = 4, + Timeout300Sec = 5, + Timeout600Sec = 6, + Timeout3600Sec = 7, +}; + +struct backlight_config_t { + public: + backlight_config_t() + : _timeout_enum(backlight_timeout_t::Timeout600Sec), + _timeout_enabled(false) { + } + + backlight_config_t( + backlight_timeout_t timeout_enum, + bool timeout_enabled) + : _timeout_enum(timeout_enum), + _timeout_enabled(timeout_enabled) { + } + + bool timeout_enabled() const { + return _timeout_enabled; + } + + backlight_timeout_t timeout_enum() const { + return _timeout_enum; + } + + uint32_t timeout_seconds() const { + switch (timeout_enum()) { + case Timeout5Sec: + return 5; + case Timeout15Sec: + return 15; + case Timeout30Sec: + return 30; + case Timeout60Sec: + return 60; + case Timeout180Sec: + return 180; + case Timeout300Sec: + return 300; + default: + case Timeout600Sec: + return 600; + case Timeout3600Sec: + return 3600; + } + } + + private: + backlight_timeout_t _timeout_enum; + bool _timeout_enabled; +}; + +namespace cache { + +/* Set values in cache to sensible defaults. */ +void defaults(); + +/* Load cached settings from values in persistent RAM, replacing with defaults + * if persistent RAM contents appear to be invalid. */ +void init(); + +/* Calculate a check value for cached settings, and copy the check value and + * settings into persistent RAM. Intended to be called periodically to update + * persistent settings with current settings. */ +void persist(); + +} /* namespace cache */ + +using ppb_t = int32_t; + +rf::Frequency tuned_frequency(); +void set_tuned_frequency(const rf::Frequency new_value); + +ppb_t correction_ppb(); +void set_correction_ppb(const ppb_t new_value); + +void set_touch_calibration(const touch::Calibration& new_value); +const touch::Calibration& touch_calibration(); + +serial_format_t serial_format(); +void set_serial_format(const serial_format_t new_value); + +int32_t tone_mix(); +void set_tone_mix(const int32_t new_value); + +int32_t afsk_mark_freq(); +void set_afsk_mark(const int32_t new_value); + +int32_t afsk_space_freq(); +void set_afsk_space(const int32_t new_value); + +int32_t modem_baudrate(); +void set_modem_baudrate(const int32_t new_value); + +uint8_t modem_repeat(); +void set_modem_repeat(const uint32_t new_value); + +uint32_t playing_dead(); +void set_playing_dead(const uint32_t new_value); + +uint32_t playdead_sequence(); +void set_playdead_sequence(const uint32_t new_value); + +bool stealth_mode(); +void set_stealth_mode(const bool v); + +uint8_t config_cpld(); +void set_config_cpld(uint8_t i); + +bool config_splash(); +bool config_hide_converter(); +bool config_converter(); +bool config_updown_converter(); +int64_t config_converter_freq(); +bool show_gui_return_icon(); +bool load_app_settings(); +bool save_app_settings(); +bool show_bigger_qr_code(); +bool hide_clock(); +bool clock_with_date(); +bool config_login(); +bool config_speaker(); +backlight_config_t config_backlight_timer(); +bool disable_touchscreen(); + +void set_gui_return_icon(bool v); +void set_load_app_settings(bool v); +void set_save_app_settings(bool v); +void set_show_bigger_qr_code(bool v); +void set_config_splash(bool v); +void set_config_hide_converter(bool v); +void set_config_converter(bool v); +void set_config_updown_converter(const bool v); +void set_config_converter_freq(const int64_t v); +void set_clock_hidden(bool v); +void set_clock_with_date(bool v); +void set_config_login(bool v); +void set_config_speaker(bool v); +void set_config_backlight_timer(const backlight_config_t& new_value); +void set_disable_touchscreen(bool v); + +// uint8_t ui_config_textentry(); +// void set_config_textentry(uint8_t new_value); + +uint32_t pocsag_last_address(); +void set_pocsag_last_address(uint32_t address); + +uint32_t pocsag_ignore_address(); +void set_pocsag_ignore_address(uint32_t address); + +bool clkout_enabled(); +void set_clkout_enabled(bool v); +uint32_t clkout_freq(); +void set_clkout_freq(uint32_t freq); + +/* Recon app */ +bool recon_autosave_freqs(); +bool recon_autostart_recon(); +bool recon_continuous(); +bool recon_clear_output(); +bool recon_load_freqs(); +bool recon_load_ranges(); +bool recon_update_ranges_when_recon(); +bool recon_load_hamradios(); +bool recon_match_mode(); +void set_recon_autosave_freqs(const bool v); +void set_recon_autostart_recon(const bool v); +void set_recon_continuous(const bool v); +void set_recon_clear_output(const bool v); +void set_recon_load_freqs(const bool v); +void set_recon_load_ranges(const bool v); +void set_recon_update_ranges_when_recon(const bool v); +void set_recon_load_hamradios(const bool v); +void set_recon_match_mode(const bool v); + +// sd persisting settings +int save_persistent_settings_to_file(std::string filename); +int load_persistent_settings_from_file(std::string filename); + +} /* namespace persistent_memory */ } /* namespace portapack */ -#endif/*__PORTAPACK_PERSISTENT_MEMORY_H__*/ +#endif /*__PORTAPACK_PERSISTENT_MEMORY_H__*/ diff --git a/firmware/common/portapack_shared_memory.cpp b/firmware/common/portapack_shared_memory.cpp index 4d4e913c1..0a9f32cab 100644 --- a/firmware/common/portapack_shared_memory.cpp +++ b/firmware/common/portapack_shared_memory.cpp @@ -24,10 +24,8 @@ #include "memory_map.hpp" SharedMemory& shared_memory = *reinterpret_cast( - portapack::memory::map::shared_memory.base() -); + portapack::memory::map::shared_memory.base()); static_assert( - sizeof(SharedMemory) <= portapack::memory::map::shared_memory.size(), - "SharedMemory is too large" -); + sizeof(SharedMemory) <= portapack::memory::map::shared_memory.size(), + "SharedMemory is too large"); diff --git a/firmware/common/portapack_shared_memory.hpp b/firmware/common/portapack_shared_memory.hpp index df5be8a19..b335b252c 100644 --- a/firmware/common/portapack_shared_memory.hpp +++ b/firmware/common/portapack_shared_memory.hpp @@ -29,49 +29,49 @@ #include "message_queue.hpp" struct JammerChannel { - bool enabled; - uint64_t center; - uint32_t width; - uint32_t duration; + bool enabled; + uint64_t center; + uint32_t width; + uint32_t duration; }; struct ToneDef { - uint32_t delta; - uint32_t duration; + uint32_t delta; + uint32_t duration; }; - + struct ToneData { - ToneDef tone_defs[32]; - uint32_t silence; - uint8_t message[256]; + ToneDef tone_defs[32]; + uint32_t silence; + uint8_t message[256]; }; /* NOTE: These structures must be located in the same location in both M4 and M0 binaries */ struct SharedMemory { - static constexpr size_t application_queue_k = 11; - static constexpr size_t app_local_queue_k = 11; + static constexpr size_t application_queue_k = 11; + static constexpr size_t app_local_queue_k = 11; + + uint8_t application_queue_data[1 << application_queue_k]{0}; + uint8_t app_local_queue_data[1 << app_local_queue_k]{0}; + const Message* volatile baseband_message{nullptr}; + MessageQueue application_queue{application_queue_data, application_queue_k}; + MessageQueue app_local_queue{app_local_queue_data, app_local_queue_k}; - uint8_t application_queue_data[1 << application_queue_k] { 0 }; - uint8_t app_local_queue_data[1 << app_local_queue_k] { 0 }; - const Message* volatile baseband_message { nullptr }; - MessageQueue application_queue { application_queue_data, application_queue_k }; - MessageQueue app_local_queue { app_local_queue_data, app_local_queue_k }; + char m4_panic_msg[32]{0}; - char m4_panic_msg[32] { 0 }; - - union { - ToneData tones_data; - JammerChannel jammer_channels[24]; - uint8_t data[512]; - } bb_data { { { { 0, 0 } }, 0, { 0 } } }; + union { + ToneData tones_data; + JammerChannel jammer_channels[24]; + uint8_t data[512]; + } bb_data{{{{0, 0}}, 0, {0}}}; - uint8_t volatile request_m4_performance_counter{ 0 }; - uint8_t volatile m4_cpu_usage{ 0 }; - uint16_t volatile m4_stack_usage{ 0 }; - uint32_t volatile m4_heap_usage{ 0 }; - uint16_t volatile m4_buffer_missed{ 0 }; + uint8_t volatile request_m4_performance_counter{0}; + uint8_t volatile m4_cpu_usage{0}; + uint16_t volatile m4_stack_usage{0}; + uint32_t volatile m4_heap_usage{0}; + uint16_t volatile m4_buffer_missed{0}; }; extern SharedMemory& shared_memory; -#endif/*__PORTAPACK_SHARED_MEMORY_H__*/ +#endif /*__PORTAPACK_SHARED_MEMORY_H__*/ diff --git a/firmware/common/random.cpp b/firmware/common/random.cpp index b7de06c41..4a4fbcf68 100644 --- a/firmware/common/random.cpp +++ b/firmware/common/random.cpp @@ -24,16 +24,14 @@ static unsigned long state[N]; /* the array for the state vector */ static int rnd_left = 1; static int rnd_init = 0; -static unsigned long *rnd_next; +static unsigned long* rnd_next; /* initializes state[N] with a seed */ -void init_genrand(unsigned long s) -{ +void init_genrand(unsigned long s) { int j; state[0] = s & 0xffffffffUL; - for (j = 1; j < N; j++) - { + for (j = 1; j < N; j++) { state[j] = (1812433253UL * (state[j - 1] ^ (state[j - 1] >> 30)) + j); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ @@ -46,9 +44,8 @@ void init_genrand(unsigned long s) rnd_init = 1; } -static void next_state(void) -{ - unsigned long *p = state; +static void next_state(void) { + unsigned long* p = state; int j; /* if init_genrand() has not been called, */ @@ -68,8 +65,7 @@ static void next_state(void) *p = p[M - N] ^ TWIST(p[0], state[0]); } -long genrand_int31(void) -{ +long genrand_int31(void) { unsigned long y; if (--rnd_left == 0) diff --git a/firmware/common/simd.hpp b/firmware/common/simd.hpp index c2e9be95c..3093578b4 100644 --- a/firmware/common/simd.hpp +++ b/firmware/common/simd.hpp @@ -29,86 +29,82 @@ #include struct vec4_s8 { - union { - int8_t v[4]; - uint32_t w; - }; + union { + int8_t v[4]; + uint32_t w; + }; }; struct vec2_s16 { - constexpr vec2_s16( - ) : v { 0, 0 } - { - } - - constexpr vec2_s16( - const int16_t v - ) : v { v, v } - { - } - - constexpr vec2_s16( - const int16_t v0, - const int16_t v1 - ) : v { v0, v1 } - { - } - - constexpr vec2_s16( - const vec2_s16& other - ) : w { other.w } - { - } - - vec2_s16& operator=(const vec2_s16& other) { - w = other.w; - return *this; - } - - union { - int16_t v[2]; - uint32_t w; - }; + constexpr vec2_s16() + : v{0, 0} { + } + + constexpr vec2_s16( + const int16_t v) + : v{v, v} { + } + + constexpr vec2_s16( + const int16_t v0, + const int16_t v1) + : v{v0, v1} { + } + + constexpr vec2_s16( + const vec2_s16& other) + : w{other.w} { + } + + vec2_s16& operator=(const vec2_s16& other) { + w = other.w; + return *this; + } + + union { + int16_t v[2]; + uint32_t w; + }; }; static inline vec4_s8 rev16(const vec4_s8 v) { - vec4_s8 result; - result.w = __REV16(v.w); - return result; + vec4_s8 result; + result.w = __REV16(v.w); + return result; } static inline vec4_s8 pkhbt(const vec4_s8 v1, const vec4_s8 v2, const size_t sh = 0) { - vec4_s8 result; - result.w = __PKHBT(v1.w, v2.w, sh); - return result; + vec4_s8 result; + result.w = __PKHBT(v1.w, v2.w, sh); + return result; } static inline vec2_s16 pkhbt(const vec2_s16 v1, const vec2_s16 v2, const size_t sh = 0) { - vec2_s16 result; - result.w = __PKHBT(v1.w, v2.w, sh); - return result; + vec2_s16 result; + result.w = __PKHBT(v1.w, v2.w, sh); + return result; } static inline vec2_s16 pkhtb(const vec2_s16 v1, const vec2_s16 v2, const size_t sh = 0) { - vec2_s16 result; - result.w = __PKHTB(v1.w, v2.w, sh); - return result; + vec2_s16 result; + result.w = __PKHTB(v1.w, v2.w, sh); + return result; } static inline vec2_s16 sxtb16(const vec4_s8 v, const size_t sh = 0) { - vec2_s16 result; - result.w = __SXTB16(v.w, sh); - return result; + vec2_s16 result; + result.w = __SXTB16(v.w, sh); + return result; } static inline int32_t smlsd(const vec2_s16 v1, const vec2_s16 v2, const int32_t accum) { - return __SMLSD(v1.w, v2.w, accum); + return __SMLSD(v1.w, v2.w, accum); } static inline int32_t smlad(const vec2_s16 v1, const vec2_s16 v2, const int32_t accum) { - return __SMLAD(v1.w, v2.w, accum); + return __SMLAD(v1.w, v2.w, accum); } #endif /* defined(LPC43XX_M4) */ -#endif/*__SIMD_H__*/ +#endif /*__SIMD_H__*/ diff --git a/firmware/common/sine_table.hpp b/firmware/common/sine_table.hpp index 8431bb290..b0d72e6a0 100644 --- a/firmware/common/sine_table.hpp +++ b/firmware/common/sine_table.hpp @@ -35,114 +35,113 @@ w = numpy.arange(length, dtype=numpy.float64) * (2 * numpy.pi / length) v = numpy.sin(w) print(v) */ -constexpr uint16_t sine_table_f32_period = 256; +constexpr uint16_t sine_table_f32_period = 256; // periode is 256 . means sine_table_f32[0]= sine_table_f32[0+256], sine_table_f32[1]=sine_table_f32[1+256] (those two added manualy) // Then table has 258 values ,256:[0,..255] + [256] and [257], those two are used when we interpolate[255] with [255+1], and [256] with [256+1] -// [256] index is needed in the function sin_f32() when we are inputing very small radian values , example , sin_f32((-1e-14) in radians) +// [256] index is needed in the function sin_f32() when we are inputing very small radian values , example , sin_f32((-1e-14) in radians) static constexpr std::array sine_table_f32{ - 0.00000000e+00, 2.45412285e-02, 4.90676743e-02, - 7.35645636e-02, 9.80171403e-02, 1.22410675e-01, - 1.46730474e-01, 1.70961889e-01, 1.95090322e-01, - 2.19101240e-01, 2.42980180e-01, 2.66712757e-01, - 2.90284677e-01, 3.13681740e-01, 3.36889853e-01, - 3.59895037e-01, 3.82683432e-01, 4.05241314e-01, - 4.27555093e-01, 4.49611330e-01, 4.71396737e-01, - 4.92898192e-01, 5.14102744e-01, 5.34997620e-01, - 5.55570233e-01, 5.75808191e-01, 5.95699304e-01, - 6.15231591e-01, 6.34393284e-01, 6.53172843e-01, - 6.71558955e-01, 6.89540545e-01, 7.07106781e-01, - 7.24247083e-01, 7.40951125e-01, 7.57208847e-01, - 7.73010453e-01, 7.88346428e-01, 8.03207531e-01, - 8.17584813e-01, 8.31469612e-01, 8.44853565e-01, - 8.57728610e-01, 8.70086991e-01, 8.81921264e-01, - 8.93224301e-01, 9.03989293e-01, 9.14209756e-01, - 9.23879533e-01, 9.32992799e-01, 9.41544065e-01, - 9.49528181e-01, 9.56940336e-01, 9.63776066e-01, - 9.70031253e-01, 9.75702130e-01, 9.80785280e-01, - 9.85277642e-01, 9.89176510e-01, 9.92479535e-01, - 9.95184727e-01, 9.97290457e-01, 9.98795456e-01, - 9.99698819e-01, 1.00000000e+00, 9.99698819e-01, - 9.98795456e-01, 9.97290457e-01, 9.95184727e-01, - 9.92479535e-01, 9.89176510e-01, 9.85277642e-01, - 9.80785280e-01, 9.75702130e-01, 9.70031253e-01, - 9.63776066e-01, 9.56940336e-01, 9.49528181e-01, - 9.41544065e-01, 9.32992799e-01, 9.23879533e-01, - 9.14209756e-01, 9.03989293e-01, 8.93224301e-01, - 8.81921264e-01, 8.70086991e-01, 8.57728610e-01, - 8.44853565e-01, 8.31469612e-01, 8.17584813e-01, - 8.03207531e-01, 7.88346428e-01, 7.73010453e-01, - 7.57208847e-01, 7.40951125e-01, 7.24247083e-01, - 7.07106781e-01, 6.89540545e-01, 6.71558955e-01, - 6.53172843e-01, 6.34393284e-01, 6.15231591e-01, - 5.95699304e-01, 5.75808191e-01, 5.55570233e-01, - 5.34997620e-01, 5.14102744e-01, 4.92898192e-01, - 4.71396737e-01, 4.49611330e-01, 4.27555093e-01, - 4.05241314e-01, 3.82683432e-01, 3.59895037e-01, - 3.36889853e-01, 3.13681740e-01, 2.90284677e-01, - 2.66712757e-01, 2.42980180e-01, 2.19101240e-01, - 1.95090322e-01, 1.70961889e-01, 1.46730474e-01, - 1.22410675e-01, 9.80171403e-02, 7.35645636e-02, - 4.90676743e-02, 2.45412285e-02, 1.22464680e-16, - -2.45412285e-02, -4.90676743e-02, -7.35645636e-02, - -9.80171403e-02, -1.22410675e-01, -1.46730474e-01, - -1.70961889e-01, -1.95090322e-01, -2.19101240e-01, - -2.42980180e-01, -2.66712757e-01, -2.90284677e-01, - -3.13681740e-01, -3.36889853e-01, -3.59895037e-01, - -3.82683432e-01, -4.05241314e-01, -4.27555093e-01, - -4.49611330e-01, -4.71396737e-01, -4.92898192e-01, - -5.14102744e-01, -5.34997620e-01, -5.55570233e-01, - -5.75808191e-01, -5.95699304e-01, -6.15231591e-01, - -6.34393284e-01, -6.53172843e-01, -6.71558955e-01, - -6.89540545e-01, -7.07106781e-01, -7.24247083e-01, - -7.40951125e-01, -7.57208847e-01, -7.73010453e-01, - -7.88346428e-01, -8.03207531e-01, -8.17584813e-01, - -8.31469612e-01, -8.44853565e-01, -8.57728610e-01, - -8.70086991e-01, -8.81921264e-01, -8.93224301e-01, - -9.03989293e-01, -9.14209756e-01, -9.23879533e-01, - -9.32992799e-01, -9.41544065e-01, -9.49528181e-01, - -9.56940336e-01, -9.63776066e-01, -9.70031253e-01, - -9.75702130e-01, -9.80785280e-01, -9.85277642e-01, - -9.89176510e-01, -9.92479535e-01, -9.95184727e-01, - -9.97290457e-01, -9.98795456e-01, -9.99698819e-01, - -1.00000000e+00, -9.99698819e-01, -9.98795456e-01, - -9.97290457e-01, -9.95184727e-01, -9.92479535e-01, - -9.89176510e-01, -9.85277642e-01, -9.80785280e-01, - -9.75702130e-01, -9.70031253e-01, -9.63776066e-01, - -9.56940336e-01, -9.49528181e-01, -9.41544065e-01, - -9.32992799e-01, -9.23879533e-01, -9.14209756e-01, - -9.03989293e-01, -8.93224301e-01, -8.81921264e-01, - -8.70086991e-01, -8.57728610e-01, -8.44853565e-01, - -8.31469612e-01, -8.17584813e-01, -8.03207531e-01, - -7.88346428e-01, -7.73010453e-01, -7.57208847e-01, - -7.40951125e-01, -7.24247083e-01, -7.07106781e-01, - -6.89540545e-01, -6.71558955e-01, -6.53172843e-01, - -6.34393284e-01, -6.15231591e-01, -5.95699304e-01, - -5.75808191e-01, -5.55570233e-01, -5.34997620e-01, - -5.14102744e-01, -4.92898192e-01, -4.71396737e-01, - -4.49611330e-01, -4.27555093e-01, -4.05241314e-01, - -3.82683432e-01, -3.59895037e-01, -3.36889853e-01, - -3.13681740e-01, -2.90284677e-01, -2.66712757e-01, - -2.42980180e-01, -2.19101240e-01, -1.95090322e-01, - -1.70961889e-01, -1.46730474e-01, -1.22410675e-01, - -9.80171403e-02, -7.35645636e-02, -4.90676743e-02, - -2.45412285e-02, 0.00000000e+00, 2.45412285e-02 -}; + 0.00000000e+00, 2.45412285e-02, 4.90676743e-02, + 7.35645636e-02, 9.80171403e-02, 1.22410675e-01, + 1.46730474e-01, 1.70961889e-01, 1.95090322e-01, + 2.19101240e-01, 2.42980180e-01, 2.66712757e-01, + 2.90284677e-01, 3.13681740e-01, 3.36889853e-01, + 3.59895037e-01, 3.82683432e-01, 4.05241314e-01, + 4.27555093e-01, 4.49611330e-01, 4.71396737e-01, + 4.92898192e-01, 5.14102744e-01, 5.34997620e-01, + 5.55570233e-01, 5.75808191e-01, 5.95699304e-01, + 6.15231591e-01, 6.34393284e-01, 6.53172843e-01, + 6.71558955e-01, 6.89540545e-01, 7.07106781e-01, + 7.24247083e-01, 7.40951125e-01, 7.57208847e-01, + 7.73010453e-01, 7.88346428e-01, 8.03207531e-01, + 8.17584813e-01, 8.31469612e-01, 8.44853565e-01, + 8.57728610e-01, 8.70086991e-01, 8.81921264e-01, + 8.93224301e-01, 9.03989293e-01, 9.14209756e-01, + 9.23879533e-01, 9.32992799e-01, 9.41544065e-01, + 9.49528181e-01, 9.56940336e-01, 9.63776066e-01, + 9.70031253e-01, 9.75702130e-01, 9.80785280e-01, + 9.85277642e-01, 9.89176510e-01, 9.92479535e-01, + 9.95184727e-01, 9.97290457e-01, 9.98795456e-01, + 9.99698819e-01, 1.00000000e+00, 9.99698819e-01, + 9.98795456e-01, 9.97290457e-01, 9.95184727e-01, + 9.92479535e-01, 9.89176510e-01, 9.85277642e-01, + 9.80785280e-01, 9.75702130e-01, 9.70031253e-01, + 9.63776066e-01, 9.56940336e-01, 9.49528181e-01, + 9.41544065e-01, 9.32992799e-01, 9.23879533e-01, + 9.14209756e-01, 9.03989293e-01, 8.93224301e-01, + 8.81921264e-01, 8.70086991e-01, 8.57728610e-01, + 8.44853565e-01, 8.31469612e-01, 8.17584813e-01, + 8.03207531e-01, 7.88346428e-01, 7.73010453e-01, + 7.57208847e-01, 7.40951125e-01, 7.24247083e-01, + 7.07106781e-01, 6.89540545e-01, 6.71558955e-01, + 6.53172843e-01, 6.34393284e-01, 6.15231591e-01, + 5.95699304e-01, 5.75808191e-01, 5.55570233e-01, + 5.34997620e-01, 5.14102744e-01, 4.92898192e-01, + 4.71396737e-01, 4.49611330e-01, 4.27555093e-01, + 4.05241314e-01, 3.82683432e-01, 3.59895037e-01, + 3.36889853e-01, 3.13681740e-01, 2.90284677e-01, + 2.66712757e-01, 2.42980180e-01, 2.19101240e-01, + 1.95090322e-01, 1.70961889e-01, 1.46730474e-01, + 1.22410675e-01, 9.80171403e-02, 7.35645636e-02, + 4.90676743e-02, 2.45412285e-02, 1.22464680e-16, + -2.45412285e-02, -4.90676743e-02, -7.35645636e-02, + -9.80171403e-02, -1.22410675e-01, -1.46730474e-01, + -1.70961889e-01, -1.95090322e-01, -2.19101240e-01, + -2.42980180e-01, -2.66712757e-01, -2.90284677e-01, + -3.13681740e-01, -3.36889853e-01, -3.59895037e-01, + -3.82683432e-01, -4.05241314e-01, -4.27555093e-01, + -4.49611330e-01, -4.71396737e-01, -4.92898192e-01, + -5.14102744e-01, -5.34997620e-01, -5.55570233e-01, + -5.75808191e-01, -5.95699304e-01, -6.15231591e-01, + -6.34393284e-01, -6.53172843e-01, -6.71558955e-01, + -6.89540545e-01, -7.07106781e-01, -7.24247083e-01, + -7.40951125e-01, -7.57208847e-01, -7.73010453e-01, + -7.88346428e-01, -8.03207531e-01, -8.17584813e-01, + -8.31469612e-01, -8.44853565e-01, -8.57728610e-01, + -8.70086991e-01, -8.81921264e-01, -8.93224301e-01, + -9.03989293e-01, -9.14209756e-01, -9.23879533e-01, + -9.32992799e-01, -9.41544065e-01, -9.49528181e-01, + -9.56940336e-01, -9.63776066e-01, -9.70031253e-01, + -9.75702130e-01, -9.80785280e-01, -9.85277642e-01, + -9.89176510e-01, -9.92479535e-01, -9.95184727e-01, + -9.97290457e-01, -9.98795456e-01, -9.99698819e-01, + -1.00000000e+00, -9.99698819e-01, -9.98795456e-01, + -9.97290457e-01, -9.95184727e-01, -9.92479535e-01, + -9.89176510e-01, -9.85277642e-01, -9.80785280e-01, + -9.75702130e-01, -9.70031253e-01, -9.63776066e-01, + -9.56940336e-01, -9.49528181e-01, -9.41544065e-01, + -9.32992799e-01, -9.23879533e-01, -9.14209756e-01, + -9.03989293e-01, -8.93224301e-01, -8.81921264e-01, + -8.70086991e-01, -8.57728610e-01, -8.44853565e-01, + -8.31469612e-01, -8.17584813e-01, -8.03207531e-01, + -7.88346428e-01, -7.73010453e-01, -7.57208847e-01, + -7.40951125e-01, -7.24247083e-01, -7.07106781e-01, + -6.89540545e-01, -6.71558955e-01, -6.53172843e-01, + -6.34393284e-01, -6.15231591e-01, -5.95699304e-01, + -5.75808191e-01, -5.55570233e-01, -5.34997620e-01, + -5.14102744e-01, -4.92898192e-01, -4.71396737e-01, + -4.49611330e-01, -4.27555093e-01, -4.05241314e-01, + -3.82683432e-01, -3.59895037e-01, -3.36889853e-01, + -3.13681740e-01, -2.90284677e-01, -2.66712757e-01, + -2.42980180e-01, -2.19101240e-01, -1.95090322e-01, + -1.70961889e-01, -1.46730474e-01, -1.22410675e-01, + -9.80171403e-02, -7.35645636e-02, -4.90676743e-02, + -2.45412285e-02, 0.00000000e+00, 2.45412285e-02}; inline float sin_f32(const float w) { - const float x = w / (2 * pi); // normalization - const float x_frac = x - std::floor(x); // [0, 1] + const float x = w / (2 * pi); // normalization + const float x_frac = x - std::floor(x); // [0, 1] - const float n = x_frac * sine_table_f32_period; - const uint16_t n_int = static_cast(n); - const float n_frac = n - n_int; + const float n = x_frac * sine_table_f32_period; + const uint16_t n_int = static_cast(n); + const float n_frac = n - n_int; - const float p0 = sine_table_f32[n_int]; - const float p1 = sine_table_f32[n_int + 1]; - const float diff = p1 - p0; - const float result = p0 + n_frac * diff; // linear interpolation + const float p0 = sine_table_f32[n_int]; + const float p1 = sine_table_f32[n_int + 1]; + const float diff = p1 - p0; + const float result = p0 + n_frac * diff; // linear interpolation - return result; + return result; } -#endif/*__SINE_TABLE_H__*/ +#endif /*__SINE_TABLE_H__*/ diff --git a/firmware/common/sine_table_int8.hpp b/firmware/common/sine_table_int8.hpp index 0583fa670..450036f90 100644 --- a/firmware/common/sine_table_int8.hpp +++ b/firmware/common/sine_table_int8.hpp @@ -26,23 +26,21 @@ #include static const int8_t sine_table_i8[256] = { - 0, 2, 5, 8, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, - 48, 51, 54, 57, 59, 62, 65, 67, 70, 73, 75, 78, 80, 83, 85, 87, - 90, 92, 94, 96, 98, 100, 102, 104, 105, 107, 109, 110, 112, 113, 115, 116, - 117, 118, 120, 121, 121, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, - 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 121, 121, 120, 118, - 117, 116, 115, 113, 112, 110, 109, 107, 105, 104, 102, 100, 98, 96, 94, 92, - 90, 87, 85, 83, 80, 78, 75, 73, 70, 67, 65, 62, 59, 57, 54, 51, - 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 8, 5, 2, - 0, -3, -6, -9, -13, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, - -49, -52, -55, -58, -60, -63, -66, -68, -71, -74, -76, -79, -81, -84, -86, -88, - -91, -93, -95, -97, -99, -101, -103, -105, -106, -108, -110, -111, -113, -114, -116, -117, - -118, -119, -121, -122, -122, -123, -124, -125, -126, -126, -127, -127, -127, -128, -128, -128, - -128, -128, -128, -128, -127, -127, -127, -126, -126, -125, -124, -123, -122, -122, -121, -119, - -118, -117, -116, -114, -113, -111, -110, -108, -106, -105, -103, -101, -99, -97, -95, -93, - -91, -88, -86, -84, -81, -79, -76, -74, -71, -68, -66, -63, -60, -58, -55, -52, - -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -13, -9, -6, -3 -}; + 0, 2, 5, 8, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, + 48, 51, 54, 57, 59, 62, 65, 67, 70, 73, 75, 78, 80, 83, 85, 87, + 90, 92, 94, 96, 98, 100, 102, 104, 105, 107, 109, 110, 112, 113, 115, 116, + 117, 118, 120, 121, 121, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 121, 121, 120, 118, + 117, 116, 115, 113, 112, 110, 109, 107, 105, 104, 102, 100, 98, 96, 94, 92, + 90, 87, 85, 83, 80, 78, 75, 73, 70, 67, 65, 62, 59, 57, 54, 51, + 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 8, 5, 2, + 0, -3, -6, -9, -13, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, + -49, -52, -55, -58, -60, -63, -66, -68, -71, -74, -76, -79, -81, -84, -86, -88, + -91, -93, -95, -97, -99, -101, -103, -105, -106, -108, -110, -111, -113, -114, -116, -117, + -118, -119, -121, -122, -122, -123, -124, -125, -126, -126, -127, -127, -127, -128, -128, -128, + -128, -128, -128, -128, -127, -127, -127, -126, -126, -125, -124, -123, -122, -122, -121, -119, + -118, -117, -116, -114, -113, -111, -110, -108, -106, -105, -103, -101, -99, -97, -95, -93, + -91, -88, -86, -84, -81, -79, -76, -74, -71, -68, -66, -63, -60, -58, -55, -52, + -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -13, -9, -6, -3}; - -#endif/*__SINE_TABLE_I8_H__*/ +#endif /*__SINE_TABLE_I8_H__*/ diff --git a/firmware/common/sonde_packet.cpp b/firmware/common/sonde_packet.cpp index 7842e994f..2fcc10133 100644 --- a/firmware/common/sonde_packet.cpp +++ b/firmware/common/sonde_packet.cpp @@ -24,438 +24,392 @@ #include "sonde_packet.hpp" #include "string_format.hpp" #include -//#include +// #include namespace sonde { -static uint8_t calibytes[51*16]; //need these vars to survive -static uint8_t calfrchk[51]; //so subframes are preserved while populated +static uint8_t calibytes[51 * 16]; // need these vars to survive +static uint8_t calfrchk[51]; // so subframes are preserved while populated -//Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c +// Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c #define MASK_LEN 64 -//Following values include the 4 bytes less shift, consumed in detecting the header on proc_sonde -#define block_status 0x35 //0x039 // 40 bytes -#define block_gpspos 0x10E //0x112 // 21 bytes -#define block_meas 0x61 //0x65 // 42 bytes -#define pos_FrameNb 0x37 //0x03B // 2 byte -#define pos_SondeID 0x39 //0x03D // 8 byte -#define pos_Voltage 0x041 //0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v -#define pos_CalData 0x04E //0x052 // 1 byte, counter 0x00..0x32 -#define pos_temp 0x063 //0x067 // 3 bytes (uint24_t) -#define pos_GPSecefX 0x110 //0x114 // 4 byte -#define pos_GPSecefY 0x114 //0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop) -#define pos_GPSecefZ 0x118 //0x11C // 4 byte (same as Y) - -#define PI 3.1415926535897932384626433832795 //3.1416 //(3.1415926535897932384626433832795) +// Following values include the 4 bytes less shift, consumed in detecting the header on proc_sonde +#define block_status 0x35 // 0x039 // 40 bytes +#define block_gpspos 0x10E // 0x112 // 21 bytes +#define block_meas 0x61 // 0x65 // 42 bytes +#define pos_FrameNb 0x37 // 0x03B // 2 byte +#define pos_SondeID 0x39 // 0x03D // 8 byte +#define pos_Voltage 0x041 // 0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v +#define pos_CalData 0x04E // 0x052 // 1 byte, counter 0x00..0x32 +#define pos_temp 0x063 // 0x067 // 3 bytes (uint24_t) +#define pos_GPSecefX 0x110 // 0x114 // 4 byte +#define pos_GPSecefY 0x114 // 0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop) +#define pos_GPSecefZ 0x118 // 0x11C // 4 byte (same as Y) + +#define PI 3.1415926535897932384626433832795 // 3.1416 //(3.1415926535897932384626433832795) Packet::Packet( - const baseband::Packet& packet, - const Type type -) : packet_ { packet }, - decoder_ { packet_ }, - reader_bi_m { decoder_ }, - type_ { type } -{ - if (type_ == Type::Meteomodem_unknown) { - // Right now we're just sure that the sync is from a Meteomodem sonde, differentiate between models now - const uint32_t id_byte = reader_bi_m.read(0 * 8, 16); - - if (id_byte == 0x649F) - type_ = Type::Meteomodem_M10; - else if (id_byte == 0x648F) - type_ = Type::Meteomodem_M2K2; - else if (id_byte == 0x4520) //https://raw.githubusercontent.com/projecthorus/radiosonde_auto_rx/master/demod/mod/m20mod.c - type_ = Type::Meteomodem_M20; - } + const baseband::Packet& packet, + const Type type) + : packet_{packet}, + decoder_{packet_}, + reader_bi_m{decoder_}, + type_{type} { + if (type_ == Type::Meteomodem_unknown) { + // Right now we're just sure that the sync is from a Meteomodem sonde, differentiate between models now + const uint32_t id_byte = reader_bi_m.read(0 * 8, 16); + + if (id_byte == 0x649F) + type_ = Type::Meteomodem_M10; + else if (id_byte == 0x648F) + type_ = Type::Meteomodem_M2K2; + else if (id_byte == 0x4520) // https://raw.githubusercontent.com/projecthorus/radiosonde_auto_rx/master/demod/mod/m20mod.c + type_ = Type::Meteomodem_M20; + } } size_t Packet::length() const { - return decoder_.symbols_count(); + return decoder_.symbols_count(); } Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } Packet::Type Packet::type() const { - return type_; + return type_; } -//euquiq here: -//RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame") -//The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp) -//from 0x008 to 0x037 (48 bytes reed-solomon error correction data) - -uint8_t Packet::vaisala_descramble(const uint32_t pos) const -{ //vaisala_descramble(const uint32_t pos) const { - // packet_[i]; its a bit; packet_.size the total (should be 2560 bits) - uint8_t value = 0; - for (uint8_t i = 0; i < 8; i++) - value = (value << 1) | packet_[(pos * 8) + (7 - i)]; //get the byte from the bits collection - - //packetReader reader { packet_ }; //This works just as above. - //value = reader.read(pos * 8,8); - //shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature - uint32_t mask_pos = pos + 4; - value = value ^ vaisala_mask[mask_pos % MASK_LEN]; //descramble with the xor pseudorandom table - return value; +// euquiq here: +// RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame") +// The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp) +// from 0x008 to 0x037 (48 bytes reed-solomon error correction data) + +uint8_t Packet::vaisala_descramble(const uint32_t pos) const { // vaisala_descramble(const uint32_t pos) const { + // packet_[i]; its a bit; packet_.size the total (should be 2560 bits) + uint8_t value = 0; + for (uint8_t i = 0; i < 8; i++) + value = (value << 1) | packet_[(pos * 8) + (7 - i)]; // get the byte from the bits collection + + // packetReader reader { packet_ }; //This works just as above. + // value = reader.read(pos * 8,8); + // shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature + uint32_t mask_pos = pos + 4; + value = value ^ vaisala_mask[mask_pos % MASK_LEN]; // descramble with the xor pseudorandom table + return value; }; -GPS_data Packet::get_GPS_data() const -{ - GPS_data result; - if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) - { - - result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48; - result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0); - result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0); - } - else if (type_ == Type::Meteomodem_M20) - { - result.alt = reader_bi_m.read(8 * 8, 24) / 100.0 ; // <| - result.lat = reader_bi_m.read(28 * 8, 32) / 1000000.0 ; // <| Inspired by https://raw.githubusercontent.com/projecthorus/radiosonde_auto_rx/master/demod/mod/m20mod.c - result.lon = reader_bi_m.read(32 * 8, 32) / 1000000.0 ; // <| - } - else if (type_ == Type::Vaisala_RS41_SG) - { - - uint8_t XYZ_bytes[4]; - int32_t XYZ; // 32bit - double_t X[3]; - for (int32_t k = 0; k < 3; k++) - { //Get X,Y,Z ECEF position from GPS - for (int32_t i = 0; i < 4; i++) //each one is 4 bytes (32 bits) - XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4 * k) + i); - memcpy(&XYZ, XYZ_bytes, 4); - X[k] = XYZ / 100.0; - } - - double_t a = 6378137.0; - double_t b = 6356752.31424518; - double_t e = sqrt((a * a - b * b) / (a * a)); - double_t ee = sqrt((a * a - b * b) / (b * b)); - - double_t lam = atan2(X[1], X[0]); - double_t p = sqrt(X[0] * X[0] + X[1] * X[1]); - double_t t = atan2(X[2] * a, p * b); - double_t phi = atan2(X[2] + ee * ee * b * sin(t) * sin(t) * sin(t), - p - e * e * a * cos(t) * cos(t) * cos(t)); - - double_t R = a / sqrt(1 - e * e * sin(phi) * sin(phi)); - - result.alt = p / cos(phi) - R; - result.lat = phi * 180 / PI; - result.lon = lam * 180 / PI; - } - return result; +GPS_data Packet::get_GPS_data() const { + GPS_data result; + if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) { + result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48; + result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0); + result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0); + } else if (type_ == Type::Meteomodem_M20) { + result.alt = reader_bi_m.read(8 * 8, 24) / 100.0; // <| + result.lat = reader_bi_m.read(28 * 8, 32) / 1000000.0; // <| Inspired by https://raw.githubusercontent.com/projecthorus/radiosonde_auto_rx/master/demod/mod/m20mod.c + result.lon = reader_bi_m.read(32 * 8, 32) / 1000000.0; // <| + } else if (type_ == Type::Vaisala_RS41_SG) { + uint8_t XYZ_bytes[4]; + int32_t XYZ; // 32bit + double_t X[3]; + for (int32_t k = 0; k < 3; k++) { // Get X,Y,Z ECEF position from GPS + for (int32_t i = 0; i < 4; i++) // each one is 4 bytes (32 bits) + XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4 * k) + i); + memcpy(&XYZ, XYZ_bytes, 4); + X[k] = XYZ / 100.0; + } + + double_t a = 6378137.0; + double_t b = 6356752.31424518; + double_t e = sqrt((a * a - b * b) / (a * a)); + double_t ee = sqrt((a * a - b * b) / (b * b)); + + double_t lam = atan2(X[1], X[0]); + double_t p = sqrt(X[0] * X[0] + X[1] * X[1]); + double_t t = atan2(X[2] * a, p * b); + double_t phi = atan2(X[2] + ee * ee * b * sin(t) * sin(t) * sin(t), + p - e * e * a * cos(t) * cos(t) * cos(t)); + + double_t R = a / sqrt(1 - e * e * sin(phi) * sin(phi)); + + result.alt = p / cos(phi) - R; + result.lat = phi * 180 / PI; + result.lon = lam * 180 / PI; + } + return result; } -uint32_t Packet::battery_voltage() const -{ - if (type_ == Type::Meteomodem_M10) - return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150; - else if (type_ == Type::Meteomodem_M20) - { - return 0; //NOT SUPPPORTED YET - } - else if (type_ == Type::Meteomodem_M2K2) - return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8 - else if (type_ == Type::Vaisala_RS41_SG) - { - uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; //byte 69 = voltage * 10 (check if this value needs to be multiplied) - return voltage; - } - else - { - return 0; // Unknown - } +uint32_t Packet::battery_voltage() const { + if (type_ == Type::Meteomodem_M10) + return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150; + else if (type_ == Type::Meteomodem_M20) { + return 0; // NOT SUPPPORTED YET + } else if (type_ == Type::Meteomodem_M2K2) + return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8 + else if (type_ == Type::Vaisala_RS41_SG) { + uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; // byte 69 = voltage * 10 (check if this value needs to be multiplied) + return voltage; + } else { + return 0; // Unknown + } } -uint32_t Packet::frame() const -{ - if (type_ == Type::Vaisala_RS41_SG) - { - uint32_t frame_number = vaisala_descramble(pos_FrameNb) | (vaisala_descramble(pos_FrameNb + 1) << 8); - return frame_number; - } - else - { - return 0; // Unknown - } +uint32_t Packet::frame() const { + if (type_ == Type::Vaisala_RS41_SG) { + uint32_t frame_number = vaisala_descramble(pos_FrameNb) | (vaisala_descramble(pos_FrameNb + 1) << 8); + return frame_number; + } else { + return 0; // Unknown + } } -temp_humid Packet::get_temp_humid() const -{ - temp_humid result; - result.humid = 0; - result.temp = 0; - - if ( type_ == Type::Vaisala_RS41_SG && crc_ok_RS41() ) //Only process if packet is healthy - { - //memset(calfrchk, 0, 51); // is this necessary ? only if the sondeID changes (new sonde) - //original code from https://github.com/rs1729/RS/blob/master/rs41/rs41ptu.c - float Rf1, // ref-resistor f1 (750 Ohm) - Rf2, // ref-resistor f2 (1100 Ohm) - co1[3], // { -243.911 , 0.187654 , 8.2e-06 } - calT1[3], // calibration T1 - co2[3], // { -243.911 , 0.187654 , 8.2e-06 } - calT2[3], // calibration T2-Hum - calH[2]; // calibration Hum - - uint32_t meas[12], i; - - //-------------- get_CalData - - //-------------- populate calibytes (from getFrameConf) - - uint8_t calfr = vaisala_descramble(pos_CalData); //get subframe #slot - - for (i = 0; i < 16; i++) //Load subrfame calibration page (16 bytes) into #slot - calibytes[calfr * 16 + i] = vaisala_descramble(pos_CalData + 1 + i); //pos = pos_CalData + 1 + i ; vaisala_descramble(pos) - - calfrchk[calfr] = 1; //flag this #slot as populated - - memcpy(&Rf1, calibytes + 61, 4); // 0x03*0x10+13 - memcpy(&Rf2, calibytes + 65, 4); // 0x04*0x10+ 1 - - memcpy(co1 + 0, calibytes + 77, 4); // 0x04*0x10+13 - memcpy(co1 + 1, calibytes + 81, 4); // 0x05*0x10+ 1 - memcpy(co1 + 2, calibytes + 85, 4); // 0x05*0x10+ 5 - - memcpy(calT1 + 0, calibytes + 89, 4); // 0x05*0x10+ 9 - memcpy(calT1 + 1, calibytes + 93, 4); // 0x05*0x10+13 - memcpy(calT1 + 2, calibytes + 97, 4); // 0x06*0x10+ 1 - - memcpy(calH + 0, calibytes + 117, 4); // 0x07*0x10+ 5 - memcpy(calH + 1, calibytes + 121, 4); // 0x07*0x10+ 9 - - memcpy(co2 + 0, calibytes + 293, 4); // 0x12*0x10+ 5 - memcpy(co2 + 1, calibytes + 297, 4); // 0x12*0x10+ 9 - memcpy(co2 + 2, calibytes + 301, 4); // 0x12*0x10+13 - - memcpy(calT2 + 0, calibytes + 305, 4); // 0x13*0x10+ 1 - memcpy(calT2 + 1, calibytes + 309, 4); // 0x13*0x10+ 5 - memcpy(calT2 + 2, calibytes + 313, 4); // 0x13*0x10+ 9 - //--------------------------------------- - for (i = 0; i < 12; i++) - meas[i] = vaisala_descramble(pos_temp + (3 * i)) | - (vaisala_descramble(pos_temp + (3 * i) + 1) << 8) | - (vaisala_descramble(pos_temp + (3 * i) + 2) << 16); - - //----Check if necessary calibytes are already present for calculation - - if (calfrchk[0x03] && calfrchk[0x04] && calfrchk[0x04] && calfrchk[0x05] && calfrchk[0x05] && calfrchk[0x06]) //Calibites OK for Temperature - { - //----------get_Tc------------------------ - float *p = co1; - float *c = calT1; - float g = (float)(meas[2] - meas[1]) / (Rf2 - Rf1), // gain - Rb = (meas[1] * Rf2 - meas[2] * Rf1) / (float)(meas[2] - meas[1]), // ofs - Rc = meas[0] / g - Rb, - R = Rc * c[0], - T = (p[0] + p[1] * R + p[2] * R * R + c[1]) * (1.0 + c[2]); - result.temp = T; - } - - if (calfrchk[0x07]) - { - //----------get_RH------------------------ - float a0 = 7.5; // empirical - float a1 = 350.0 / calH[0]; // empirical - float fh = (meas[3] - meas[4]) / (float)(meas[5] - meas[4]); - float rh = 100.0 * (a1 * fh - a0); - float T0 = 0.0, T1 = -25.0; // T/C - rh += T0 - result.temp / 5.5; // empir. temperature compensation - if (result.temp < T1) - rh *= 1.0 + (T1 - result.temp) / 90.0; // empir. temperature compensation - if (rh < 0.0) - rh = 0.0; - if (rh > 100.0) - rh = 100.0; - if (result.temp < -273.0) - rh = -1.0; - result.humid = rh; - } - } - return result; +temp_humid Packet::get_temp_humid() const { + temp_humid result; + result.humid = 0; + result.temp = 0; + + if (type_ == Type::Vaisala_RS41_SG && crc_ok_RS41()) // Only process if packet is healthy + { + // memset(calfrchk, 0, 51); // is this necessary ? only if the sondeID changes (new sonde) + // original code from https://github.com/rs1729/RS/blob/master/rs41/rs41ptu.c + float Rf1, // ref-resistor f1 (750 Ohm) + Rf2, // ref-resistor f2 (1100 Ohm) + co1[3], // { -243.911 , 0.187654 , 8.2e-06 } + calT1[3], // calibration T1 + co2[3], // { -243.911 , 0.187654 , 8.2e-06 } + calT2[3], // calibration T2-Hum + calH[2]; // calibration Hum + + uint32_t meas[12], i; + + //-------------- get_CalData + + //-------------- populate calibytes (from getFrameConf) + + uint8_t calfr = vaisala_descramble(pos_CalData); // get subframe #slot + + for (i = 0; i < 16; i++) // Load subrfame calibration page (16 bytes) into #slot + calibytes[calfr * 16 + i] = vaisala_descramble(pos_CalData + 1 + i); // pos = pos_CalData + 1 + i ; vaisala_descramble(pos) + + calfrchk[calfr] = 1; // flag this #slot as populated + + memcpy(&Rf1, calibytes + 61, 4); // 0x03*0x10+13 + memcpy(&Rf2, calibytes + 65, 4); // 0x04*0x10+ 1 + + memcpy(co1 + 0, calibytes + 77, 4); // 0x04*0x10+13 + memcpy(co1 + 1, calibytes + 81, 4); // 0x05*0x10+ 1 + memcpy(co1 + 2, calibytes + 85, 4); // 0x05*0x10+ 5 + + memcpy(calT1 + 0, calibytes + 89, 4); // 0x05*0x10+ 9 + memcpy(calT1 + 1, calibytes + 93, 4); // 0x05*0x10+13 + memcpy(calT1 + 2, calibytes + 97, 4); // 0x06*0x10+ 1 + + memcpy(calH + 0, calibytes + 117, 4); // 0x07*0x10+ 5 + memcpy(calH + 1, calibytes + 121, 4); // 0x07*0x10+ 9 + + memcpy(co2 + 0, calibytes + 293, 4); // 0x12*0x10+ 5 + memcpy(co2 + 1, calibytes + 297, 4); // 0x12*0x10+ 9 + memcpy(co2 + 2, calibytes + 301, 4); // 0x12*0x10+13 + + memcpy(calT2 + 0, calibytes + 305, 4); // 0x13*0x10+ 1 + memcpy(calT2 + 1, calibytes + 309, 4); // 0x13*0x10+ 5 + memcpy(calT2 + 2, calibytes + 313, 4); // 0x13*0x10+ 9 + //--------------------------------------- + for (i = 0; i < 12; i++) + meas[i] = vaisala_descramble(pos_temp + (3 * i)) | + (vaisala_descramble(pos_temp + (3 * i) + 1) << 8) | + (vaisala_descramble(pos_temp + (3 * i) + 2) << 16); + + //----Check if necessary calibytes are already present for calculation + + if (calfrchk[0x03] && calfrchk[0x04] && calfrchk[0x04] && calfrchk[0x05] && calfrchk[0x05] && calfrchk[0x06]) // Calibites OK for Temperature + { + //----------get_Tc------------------------ + float* p = co1; + float* c = calT1; + float g = (float)(meas[2] - meas[1]) / (Rf2 - Rf1), // gain + Rb = (meas[1] * Rf2 - meas[2] * Rf1) / (float)(meas[2] - meas[1]), // ofs + Rc = meas[0] / g - Rb, + R = Rc * c[0], + T = (p[0] + p[1] * R + p[2] * R * R + c[1]) * (1.0 + c[2]); + result.temp = T; + } + + if (calfrchk[0x07]) { + //----------get_RH------------------------ + float a0 = 7.5; // empirical + float a1 = 350.0 / calH[0]; // empirical + float fh = (meas[3] - meas[4]) / (float)(meas[5] - meas[4]); + float rh = 100.0 * (a1 * fh - a0); + float T0 = 0.0, T1 = -25.0; // T/C + rh += T0 - result.temp / 5.5; // empir. temperature compensation + if (result.temp < T1) + rh *= 1.0 + (T1 - result.temp) / 90.0; // empir. temperature compensation + if (rh < 0.0) + rh = 0.0; + if (rh > 100.0) + rh = 100.0; + if (result.temp < -273.0) + rh = -1.0; + result.humid = rh; + } + } + return result; } -std::string Packet::type_string() const -{ - switch (type_) - { - case Type::Unknown: - return "Unknown"; - case Type::Meteomodem_unknown: - return "Meteomodem ???"; - case Type::Meteomodem_M10: - return "Meteomodem M10"; - case Type::Meteomodem_M20: - return "Meteomodem M20"; - case Type::Meteomodem_M2K2: - return "Meteomodem M2K2"; - case Type::Vaisala_RS41_SG: - return "Vaisala RS41-SG"; - default: - return "? 0x" + symbols_formatted().data.substr(0, 6); - } +std::string Packet::type_string() const { + switch (type_) { + case Type::Unknown: + return "Unknown"; + case Type::Meteomodem_unknown: + return "Meteomodem ???"; + case Type::Meteomodem_M10: + return "Meteomodem M10"; + case Type::Meteomodem_M20: + return "Meteomodem M20"; + case Type::Meteomodem_M2K2: + return "Meteomodem M2K2"; + case Type::Vaisala_RS41_SG: + return "Vaisala RS41-SG"; + default: + return "? 0x" + symbols_formatted().data.substr(0, 6); + } } -std::string Packet::serial_number() const -{ - if (type_ == Type::Meteomodem_M10) - { - // See https://github.com/rs1729/RS/blob/master/m10/m10x.c line 606 - // Starting at byte #93: 00000000 11111111 22222222 33333333 44444444 - // CCCC AAAABBBB - // 44444444 33333333 - // DDDEEEEE EEEEEEEE - - return to_string_hex(reader_bi_m.read(93 * 8 + 16, 4), 1) + - to_string_dec_uint(reader_bi_m.read(93 * 8 + 20, 4), 2, '0') + " " + - to_string_hex(reader_bi_m.read(93 * 8 + 4, 4), 1) + " " + - to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) + - to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0'); - } - else if (type_ == Type::Vaisala_RS41_SG) - { - std::string serial_id = ""; - uint8_t achar; - for (uint8_t i = 0; i < 8; i++) - { //euquiq: Serial ID is 8 bytes long, each byte a char - achar = vaisala_descramble(pos_SondeID + i); - if (achar < 32 || achar > 126) - return "?"; //Maybe there are ids with less than 8 bytes and this is not OK. - serial_id += (char)achar; - } - return serial_id; - } - else - { - return "?"; - } +std::string Packet::serial_number() const { + if (type_ == Type::Meteomodem_M10) { + // See https://github.com/rs1729/RS/blob/master/m10/m10x.c line 606 + // Starting at byte #93: 00000000 11111111 22222222 33333333 44444444 + // CCCC AAAABBBB + // 44444444 33333333 + // DDDEEEEE EEEEEEEE + + return to_string_hex(reader_bi_m.read(93 * 8 + 16, 4), 1) + + to_string_dec_uint(reader_bi_m.read(93 * 8 + 20, 4), 2, '0') + " " + + to_string_hex(reader_bi_m.read(93 * 8 + 4, 4), 1) + " " + + to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) + + to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0'); + } else if (type_ == Type::Vaisala_RS41_SG) { + std::string serial_id = ""; + uint8_t achar; + for (uint8_t i = 0; i < 8; i++) { // euquiq: Serial ID is 8 bytes long, each byte a char + achar = vaisala_descramble(pos_SondeID + i); + if (achar < 32 || achar > 126) + return "?"; // Maybe there are ids with less than 8 bytes and this is not OK. + serial_id += (char)achar; + } + return serial_id; + } else { + return "?"; + } } -FormattedSymbols Packet::symbols_formatted() const -{ - if (type_ == Type::Vaisala_RS41_SG) - { //Euquiq: now we distinguish different types - uint32_t bytes = packet_.size() / 8; //Need the byte amount, which if full, it SHOULD be 320 size() should return 2560 - std::string hex_data; - std::string hex_error; - hex_data.reserve(bytes * 2); //2 hexa chars per byte - hex_error.reserve(1); - for (uint32_t i = 0; i < bytes; i++) //log will show the packet starting on the last 4 bytes from signature 93DF1A60 - hex_data += to_string_hex(vaisala_descramble(i), 2); - return {hex_data, hex_error}; - } - else - { - return format_symbols(decoder_); - } +FormattedSymbols Packet::symbols_formatted() const { + if (type_ == Type::Vaisala_RS41_SG) { // Euquiq: now we distinguish different types + uint32_t bytes = packet_.size() / 8; // Need the byte amount, which if full, it SHOULD be 320 size() should return 2560 + std::string hex_data; + std::string hex_error; + hex_data.reserve(bytes * 2); // 2 hexa chars per byte + hex_error.reserve(1); + for (uint32_t i = 0; i < bytes; i++) // log will show the packet starting on the last 4 bytes from signature 93DF1A60 + hex_data += to_string_hex(vaisala_descramble(i), 2); + return {hex_data, hex_error}; + } else { + return format_symbols(decoder_); + } } -bool Packet::crc_ok() const -{ - switch (type_) - { - case Type::Meteomodem_M10: - return crc_ok_M10(); - case Type::Vaisala_RS41_SG: - return crc_ok_RS41(); - default: - return true; //euquiq: it was false, but if no crc routine, then no way to check - } +bool Packet::crc_ok() const { + switch (type_) { + case Type::Meteomodem_M10: + return crc_ok_M10(); + case Type::Vaisala_RS41_SG: + return crc_ok_RS41(); + default: + return true; // euquiq: it was false, but if no crc routine, then no way to check + } } -//each data block has a 2 byte header, data, and 2 byte tail: -// 1st byte: block ID -// 2nd byte: data length (without header or tail) -// -// 2 bytes CRC16 over the data. -bool Packet::crc_ok_RS41() const //check CRC for the data blocks we need +// each data block has a 2 byte header, data, and 2 byte tail: +// 1st byte: block ID +// 2nd byte: data length (without header or tail) +// +// 2 bytes CRC16 over the data. +bool Packet::crc_ok_RS41() const // check CRC for the data blocks we need { - if (!crc16rs41(block_status)) - return false; + if (!crc16rs41(block_status)) + return false; + + if (!crc16rs41(block_gpspos)) + return false; - if (!crc16rs41(block_gpspos)) - return false; - - if (!crc16rs41(block_meas)) - return false; + if (!crc16rs41(block_meas)) + return false; - return true; + return true; } -//Checks CRC16 on a RS41 field: -bool Packet::crc16rs41(uint32_t field_start) const -{ - int crc16poly = 0x1021; - int rem = 0xFFFF, b, j; - int xbyte; - uint32_t pos = field_start + 1; - uint8_t length = vaisala_descramble(pos); - - if (pos + length + 2 > packet_.size() / 8) - return false; //Out of packet! - - for (b = 0; b < length; b++) - { - pos++; - xbyte = vaisala_descramble(pos); - rem = rem ^ (xbyte << 8); - for (j = 0; j < 8; j++) - { - if (rem & 0x8000) - { - rem = (rem << 1) ^ crc16poly; - } - else - { - rem = (rem << 1); - } - rem &= 0xFFFF; - } - } - //Check calculated CRC against packet's one - pos++; - int crcok = vaisala_descramble(pos) | (vaisala_descramble(pos + 1) << 8); - if (crcok != rem) - return false; - return true; +// Checks CRC16 on a RS41 field: +bool Packet::crc16rs41(uint32_t field_start) const { + int crc16poly = 0x1021; + int rem = 0xFFFF, b, j; + int xbyte; + uint32_t pos = field_start + 1; + uint8_t length = vaisala_descramble(pos); + + if (pos + length + 2 > packet_.size() / 8) + return false; // Out of packet! + + for (b = 0; b < length; b++) { + pos++; + xbyte = vaisala_descramble(pos); + rem = rem ^ (xbyte << 8); + for (j = 0; j < 8; j++) { + if (rem & 0x8000) { + rem = (rem << 1) ^ crc16poly; + } else { + rem = (rem << 1); + } + rem &= 0xFFFF; + } + } + // Check calculated CRC against packet's one + pos++; + int crcok = vaisala_descramble(pos) | (vaisala_descramble(pos + 1) << 8); + if (crcok != rem) + return false; + return true; } -bool Packet::crc_ok_M10() const -{ - uint16_t cs{0}; - uint32_t c0, c1, t, t6, t7, s, b; +bool Packet::crc_ok_M10() const { + uint16_t cs{0}; + uint32_t c0, c1, t, t6, t7, s, b; + + for (size_t i = 0; i < packet_.size(); i++) { + b = packet_[i]; + c1 = cs & 0xFF; - for (size_t i = 0; i < packet_.size(); i++) - { - b = packet_[i]; - c1 = cs & 0xFF; + // B + b = (b >> 1) | ((b & 1) << 7); + b ^= (b >> 2) & 0xFF; - // B - b = (b >> 1) | ((b & 1) << 7); - b ^= (b >> 2) & 0xFF; + // A1 + t6 = (cs & 1) ^ ((cs >> 2) & 1) ^ ((cs >> 4) & 1); + t7 = ((cs >> 1) & 1) ^ ((cs >> 3) & 1) ^ ((cs >> 5) & 1); + t = (cs & 0x3F) | (t6 << 6) | (t7 << 7); - // A1 - t6 = (cs & 1) ^ ((cs >> 2) & 1) ^ ((cs >> 4) & 1); - t7 = ((cs >> 1) & 1) ^ ((cs >> 3) & 1) ^ ((cs >> 5) & 1); - t = (cs & 0x3F) | (t6 << 6) | (t7 << 7); + // A2 + s = (cs >> 7) & 0xFF; + s ^= (s >> 2) & 0xFF; - // A2 - s = (cs >> 7) & 0xFF; - s ^= (s >> 2) & 0xFF; + c0 = b ^ t ^ s; - c0 = b ^ t ^ s; + cs = ((c1 << 8) | c0) & 0xFFFF; + } - cs = ((c1 << 8) | c0) & 0xFFFF; - } - - return ((cs & 0xFFFF) == ((packet_[0x63] << 8) | (packet_[0x63 + 1]))); + return ((cs & 0xFFFF) == ((packet_[0x63] << 8) | (packet_[0x63 + 1]))); } } /* namespace sonde */ diff --git a/firmware/common/sonde_packet.hpp b/firmware/common/sonde_packet.hpp index beb0a235e..f5475df7b 100644 --- a/firmware/common/sonde_packet.hpp +++ b/firmware/common/sonde_packet.hpp @@ -32,75 +32,74 @@ namespace sonde { - struct GPS_data { - uint32_t alt { 0 }; - float lat { 0 }; - float lon { 0 }; - }; +struct GPS_data { + uint32_t alt{0}; + float lat{0}; + float lon{0}; +}; - struct temp_humid { - float temp { 0 }; - float humid { 0 }; - }; +struct temp_humid { + float temp{0}; + float humid{0}; +}; class Packet { -public: - enum class Type : uint32_t { - Unknown = 0, - Meteomodem_unknown = 1, - Meteomodem_M10 = 2, - Meteomodem_M2K2 = 3, - Vaisala_RS41_SG = 4, - Meteomodem_M20 = 5, - }; - - Packet(const baseband::Packet& packet, const Type type); - - size_t length() const; - - Timestamp received_at() const; - - Type type() const; - std::string type_string() const; - - std::string serial_number() const; - uint32_t battery_voltage() const; - GPS_data get_GPS_data() const; - uint32_t frame() const; - temp_humid get_temp_humid() const; - - FormattedSymbols symbols_formatted() const; - - bool crc_ok() const; - -private: - static constexpr uint8_t vaisala_mask[64] = { - 0x96, 0x83, 0x3E, 0x51, 0xB1, 0x49, 0x08, 0x98, - 0x32, 0x05, 0x59, 0x0E, 0xF9, 0x44, 0xC6, 0x26, - 0x21, 0x60, 0xC2, 0xEA, 0x79, 0x5D, 0x6D, 0xA1, - 0x54, 0x69, 0x47, 0x0C, 0xDC, 0xE8, 0x5C, 0xF1, - 0xF7, 0x76, 0x82, 0x7F, 0x07, 0x99, 0xA2, 0x2C, - 0x93, 0x7C, 0x30, 0x63, 0xF5, 0x10, 0x2E, 0x61, - 0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23, - 0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1 - }; - - GPS_data ecef_to_gps() const; - - uint8_t vaisala_descramble(uint32_t pos) const; - - const baseband::Packet packet_; - const BiphaseMDecoder decoder_; - const FieldReader reader_bi_m; - Type type_; - - using packetReader = FieldReader; //baseband::Packet instead of BiphaseMDecoder - - bool crc_ok_M10() const; - bool crc_ok_RS41() const; - bool crc16rs41(uint32_t field_start) const; + public: + enum class Type : uint32_t { + Unknown = 0, + Meteomodem_unknown = 1, + Meteomodem_M10 = 2, + Meteomodem_M2K2 = 3, + Vaisala_RS41_SG = 4, + Meteomodem_M20 = 5, + }; + + Packet(const baseband::Packet& packet, const Type type); + + size_t length() const; + + Timestamp received_at() const; + + Type type() const; + std::string type_string() const; + + std::string serial_number() const; + uint32_t battery_voltage() const; + GPS_data get_GPS_data() const; + uint32_t frame() const; + temp_humid get_temp_humid() const; + + FormattedSymbols symbols_formatted() const; + + bool crc_ok() const; + + private: + static constexpr uint8_t vaisala_mask[64] = { + 0x96, 0x83, 0x3E, 0x51, 0xB1, 0x49, 0x08, 0x98, + 0x32, 0x05, 0x59, 0x0E, 0xF9, 0x44, 0xC6, 0x26, + 0x21, 0x60, 0xC2, 0xEA, 0x79, 0x5D, 0x6D, 0xA1, + 0x54, 0x69, 0x47, 0x0C, 0xDC, 0xE8, 0x5C, 0xF1, + 0xF7, 0x76, 0x82, 0x7F, 0x07, 0x99, 0xA2, 0x2C, + 0x93, 0x7C, 0x30, 0x63, 0xF5, 0x10, 0x2E, 0x61, + 0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23, + 0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1}; + + GPS_data ecef_to_gps() const; + + uint8_t vaisala_descramble(uint32_t pos) const; + + const baseband::Packet packet_; + const BiphaseMDecoder decoder_; + const FieldReader reader_bi_m; + Type type_; + + using packetReader = FieldReader; // baseband::Packet instead of BiphaseMDecoder + + bool crc_ok_M10() const; + bool crc_ok_RS41() const; + bool crc16rs41(uint32_t field_start) const; }; } /* namespace sonde */ -#endif/*__SONDE_PACKET_H__*/ +#endif /*__SONDE_PACKET_H__*/ diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 486197e20..61d5bb78b 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -34,117 +34,116 @@ namespace portapack { namespace spi_flash { struct image_tag_t { - - constexpr image_tag_t( - ) : c { 0, 0, 0, 0 } - { - } - - constexpr image_tag_t( - char c0, char c1, char c2, char c3 - ) : c { c0, c1, c2, c3 } - { - } - - image_tag_t(const image_tag_t& other) - { - c[0] = other.c[0]; - c[1] = other.c[1]; - c[2] = other.c[2]; - c[3] = other.c[3]; - } - - image_tag_t& operator=(const image_tag_t& other) { - c[0] = other.c[0]; - c[1] = other.c[1]; - c[2] = other.c[2]; - c[3] = other.c[3]; - return *this; - } - - bool operator==(const image_tag_t& other) const { - return (c[0] == other.c[0]) && (c[1] == other.c[1]) && (c[2] == other.c[2]) && (c[3] == other.c[3]); - } - - operator bool() const { - return (c[0] != 0) || (c[1] != 0) || (c[2] != 0) || (c[3] != 0); - } - -private: - char c[4]; + constexpr image_tag_t() + : c{0, 0, 0, 0} { + } + + constexpr image_tag_t( + char c0, + char c1, + char c2, + char c3) + : c{c0, c1, c2, c3} { + } + + image_tag_t(const image_tag_t& other) { + c[0] = other.c[0]; + c[1] = other.c[1]; + c[2] = other.c[2]; + c[3] = other.c[3]; + } + + image_tag_t& operator=(const image_tag_t& other) { + c[0] = other.c[0]; + c[1] = other.c[1]; + c[2] = other.c[2]; + c[3] = other.c[3]; + return *this; + } + + bool operator==(const image_tag_t& other) const { + return (c[0] == other.c[0]) && (c[1] == other.c[1]) && (c[2] == other.c[2]) && (c[3] == other.c[3]); + } + + operator bool() const { + return (c[0] != 0) || (c[1] != 0) || (c[2] != 0) || (c[3] != 0); + } + + private: + char c[4]; }; -constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' }; -constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' }; -constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' }; -constexpr image_tag_t image_tag_aprs_rx { 'P', 'A', 'P', 'R' }; -constexpr image_tag_t image_tag_btle_rx { 'P', 'B', 'T', 'R' }; -constexpr image_tag_t image_tag_nrf_rx { 'P', 'N', 'R', 'R' }; -constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; -constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' }; -constexpr image_tag_t image_tag_am_tv { 'P', 'A', 'M', 'T' }; -constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' }; -constexpr image_tag_t image_tag_ert { 'P', 'E', 'R', 'T' }; -constexpr image_tag_t image_tag_nfm_audio { 'P', 'N', 'F', 'M' }; -constexpr image_tag_t image_tag_pocsag { 'P', 'P', 'O', 'C' }; -constexpr image_tag_t image_tag_sonde { 'P', 'S', 'O', 'N' }; -constexpr image_tag_t image_tag_tpms { 'P', 'T', 'P', 'M' }; -constexpr image_tag_t image_tag_wfm_audio { 'P', 'W', 'F', 'M' }; -constexpr image_tag_t image_tag_wideband_spectrum { 'P', 'S', 'P', 'E' }; -constexpr image_tag_t image_tag_test { 'P', 'T', 'S', 'T' }; - -constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'T' }; -constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'T' }; -constexpr image_tag_t image_tag_audio_tx { 'P', 'A', 'T', 'X' }; -constexpr image_tag_t image_tag_fsktx { 'P', 'F', 'S', 'K' }; -constexpr image_tag_t image_tag_jammer { 'P', 'J', 'A', 'M' }; -constexpr image_tag_t image_tag_mic_tx { 'P', 'M', 'T', 'X' }; -constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' }; -constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' }; -constexpr image_tag_t image_tag_replay { 'P', 'R', 'E', 'P' }; -constexpr image_tag_t image_tag_gps { 'P', 'G', 'P', 'S' }; -constexpr image_tag_t image_tag_siggen { 'P', 'S', 'I', 'G' }; -constexpr image_tag_t image_tag_spectrum_painter { 'P', 'S', 'P', 'T' }; -constexpr image_tag_t image_tag_sstv_tx { 'P', 'S', 'T', 'X' }; -constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' }; -constexpr image_tag_t image_tag_flash_utility { 'P', 'F', 'U', 'T' }; -constexpr image_tag_t image_tag_usb_sd { 'P', 'U', 'S', 'B' }; - -constexpr image_tag_t image_tag_noop { 'P', 'N', 'O', 'P' }; - -constexpr image_tag_t image_tag_hackrf { 'H', 'R', 'F', '1' }; +constexpr image_tag_t image_tag_acars{'P', 'A', 'C', 'A'}; +constexpr image_tag_t image_tag_adsb_rx{'P', 'A', 'D', 'R'}; +constexpr image_tag_t image_tag_afsk_rx{'P', 'A', 'F', 'R'}; +constexpr image_tag_t image_tag_aprs_rx{'P', 'A', 'P', 'R'}; +constexpr image_tag_t image_tag_btle_rx{'P', 'B', 'T', 'R'}; +constexpr image_tag_t image_tag_nrf_rx{'P', 'N', 'R', 'R'}; +constexpr image_tag_t image_tag_ais{'P', 'A', 'I', 'S'}; +constexpr image_tag_t image_tag_am_audio{'P', 'A', 'M', 'A'}; +constexpr image_tag_t image_tag_am_tv{'P', 'A', 'M', 'T'}; +constexpr image_tag_t image_tag_capture{'P', 'C', 'A', 'P'}; +constexpr image_tag_t image_tag_ert{'P', 'E', 'R', 'T'}; +constexpr image_tag_t image_tag_nfm_audio{'P', 'N', 'F', 'M'}; +constexpr image_tag_t image_tag_pocsag{'P', 'P', 'O', 'C'}; +constexpr image_tag_t image_tag_sonde{'P', 'S', 'O', 'N'}; +constexpr image_tag_t image_tag_tpms{'P', 'T', 'P', 'M'}; +constexpr image_tag_t image_tag_wfm_audio{'P', 'W', 'F', 'M'}; +constexpr image_tag_t image_tag_wideband_spectrum{'P', 'S', 'P', 'E'}; +constexpr image_tag_t image_tag_test{'P', 'T', 'S', 'T'}; + +constexpr image_tag_t image_tag_adsb_tx{'P', 'A', 'D', 'T'}; +constexpr image_tag_t image_tag_afsk{'P', 'A', 'F', 'T'}; +constexpr image_tag_t image_tag_audio_tx{'P', 'A', 'T', 'X'}; +constexpr image_tag_t image_tag_fsktx{'P', 'F', 'S', 'K'}; +constexpr image_tag_t image_tag_jammer{'P', 'J', 'A', 'M'}; +constexpr image_tag_t image_tag_mic_tx{'P', 'M', 'T', 'X'}; +constexpr image_tag_t image_tag_ook{'P', 'O', 'O', 'K'}; +constexpr image_tag_t image_tag_rds{'P', 'R', 'D', 'S'}; +constexpr image_tag_t image_tag_replay{'P', 'R', 'E', 'P'}; +constexpr image_tag_t image_tag_gps{'P', 'G', 'P', 'S'}; +constexpr image_tag_t image_tag_siggen{'P', 'S', 'I', 'G'}; +constexpr image_tag_t image_tag_spectrum_painter{'P', 'S', 'P', 'T'}; +constexpr image_tag_t image_tag_sstv_tx{'P', 'S', 'T', 'X'}; +constexpr image_tag_t image_tag_tones{'P', 'T', 'O', 'N'}; +constexpr image_tag_t image_tag_flash_utility{'P', 'F', 'U', 'T'}; +constexpr image_tag_t image_tag_usb_sd{'P', 'U', 'S', 'B'}; + +constexpr image_tag_t image_tag_noop{'P', 'N', 'O', 'P'}; + +constexpr image_tag_t image_tag_hackrf{'H', 'R', 'F', '1'}; struct chunk_t { - const image_tag_t tag; - const uint32_t length; - const uint32_t compressed_data_size; - const uint8_t data[]; - - const chunk_t* next() const { - return reinterpret_cast(&data[length]); - } + const image_tag_t tag; + const uint32_t length; + const uint32_t compressed_data_size; + const uint8_t data[]; + + const chunk_t* next() const { + return reinterpret_cast(&data[length]); + } }; struct region_t { - const size_t offset; - const size_t size; + const size_t offset; + const size_t size; - constexpr const void* base() const { - return reinterpret_cast(portapack::memory::map::spifi_cached.base() + offset); - } + constexpr const void* base() const { + return reinterpret_cast(portapack::memory::map::spifi_cached.base() + offset); + } }; -const region_t images { - .offset = reinterpret_cast(&_textend), - .size = portapack::memory::map::spifi_cached.size() - reinterpret_cast(&_textend), +const region_t images{ + .offset = reinterpret_cast(&_textend), + .size = portapack::memory::map::spifi_cached.size() - reinterpret_cast(&_textend), }; -const region_t application { - .offset = 0x00000, - .size = reinterpret_cast(&_textend), +const region_t application{ + .offset = 0x00000, + .size = reinterpret_cast(&_textend), }; } /* namespace spi_flash */ } /* namespace portapack */ -#endif/*__SPI_IMAGE_H__*/ +#endif /*__SPI_IMAGE_H__*/ diff --git a/firmware/common/sstv.hpp b/firmware/common/sstv.hpp index 6ab35532c..ab10bbff5 100644 --- a/firmware/common/sstv.hpp +++ b/firmware/common/sstv.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ namespace sstv { #define SSTV_SAMPLERATE 3072000 #define SSTV_DELTA_COEF ((1ULL << 32) / SSTV_SAMPLERATE) -#define SSTV_F2D(f) (uint32_t)((f) * SSTV_DELTA_COEF) +#define SSTV_F2D(f) (uint32_t)((f)*SSTV_DELTA_COEF) #define SSTV_MS2S(d) (uint32_t)((d) / 1000.0 * (float)SSTV_SAMPLERATE) #define SSTV_VIS_SS SSTV_F2D(1200) @@ -36,59 +36,59 @@ namespace sstv { #define SSTV_VIS_ONE SSTV_F2D(1100) enum sstv_color_seq { - SSTV_COLOR_RGB, - SSTV_COLOR_GBR, - SSTV_COLOR_YUV // Not supported for now + SSTV_COLOR_RGB, + SSTV_COLOR_GBR, + SSTV_COLOR_YUV // Not supported for now }; #define SSTV_MODES_NB 6 // From http://www.graphics.stanford.edu/~seander/bithacks.html, nice ! constexpr inline uint8_t sstv_parity(uint8_t code) { - uint8_t out = code; - out ^= code >> 4; - out &= 0x0F; - return (((0b0110100110010110 >> out) & 1) << 7) | code; + uint8_t out = code; + out ^= code >> 4; + out &= 0x0F; + return (((0b0110100110010110 >> out) & 1) << 7) | code; } struct sstv_tone { - uint32_t frequency; - uint32_t duration; + uint32_t frequency; + uint32_t duration; }; struct sstv_scanline { - sstv_tone start_tone; - sstv_tone gap_tone; - uint8_t luma[320]; + sstv_tone start_tone; + sstv_tone gap_tone; + uint8_t luma[320]; }; struct sstv_mode { - char name[16]; - uint8_t vis_code; - bool color; // Unused for now - sstv_color_seq color_sequence; - uint16_t pixels; - uint16_t lines; - uint32_t samples_per_pixel; - bool sync_on_first; - uint8_t sync_index; - bool gaps; - uint32_t samples_per_sync; - uint32_t samples_per_gap; - //std::pair luma_range; + char name[16]; + uint8_t vis_code; + bool color; // Unused for now + sstv_color_seq color_sequence; + uint16_t pixels; + uint16_t lines; + uint32_t samples_per_pixel; + bool sync_on_first; + uint8_t sync_index; + bool gaps; + uint32_t samples_per_sync; + uint32_t samples_per_gap; + // std::pair luma_range; }; constexpr sstv_mode sstv_modes[SSTV_MODES_NB] = { - { "Scottie 1", sstv_parity(60), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.4320), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5) }, - { "Scottie 2", sstv_parity(56), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.2752), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5) }, - { "Scottie DX", sstv_parity(76), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(1.08), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5) }, - { "Martin 1", sstv_parity(44), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.4576), false, 0, true, SSTV_MS2S(4.862), SSTV_MS2S(0.572) }, - { "Martin 2", sstv_parity(40), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.2288), false, 0, true, SSTV_MS2S(4.862), SSTV_MS2S(0.572) }, - { "SC2-180", sstv_parity(55), true, SSTV_COLOR_RGB, 320, 256, SSTV_MS2S(0.7344), false, 0, false, SSTV_MS2S(5.5225), SSTV_MS2S(0.5) }, - //{ "PASOKON 3", sstv_parity(113), true, SSTV_COLOR_RGB, 640, 496, SSTV_MS2S(0.2083), { 1500, 2300 } }, - //{ "PASOKON 7", sstv_parity(115), true, SSTV_COLOR_RGB, 640, 496, SSTV_MS2S(0.4167), { 1500, 2300 } } + {"Scottie 1", sstv_parity(60), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.4320), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5)}, + {"Scottie 2", sstv_parity(56), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.2752), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5)}, + {"Scottie DX", sstv_parity(76), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(1.08), true, 2, true, SSTV_MS2S(9), SSTV_MS2S(1.5)}, + {"Martin 1", sstv_parity(44), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.4576), false, 0, true, SSTV_MS2S(4.862), SSTV_MS2S(0.572)}, + {"Martin 2", sstv_parity(40), true, SSTV_COLOR_GBR, 320, 256, SSTV_MS2S(0.2288), false, 0, true, SSTV_MS2S(4.862), SSTV_MS2S(0.572)}, + {"SC2-180", sstv_parity(55), true, SSTV_COLOR_RGB, 320, 256, SSTV_MS2S(0.7344), false, 0, false, SSTV_MS2S(5.5225), SSTV_MS2S(0.5)}, + //{ "PASOKON 3", sstv_parity(113), true, SSTV_COLOR_RGB, 640, 496, SSTV_MS2S(0.2083), { 1500, 2300 } }, + //{ "PASOKON 7", sstv_parity(115), true, SSTV_COLOR_RGB, 640, 496, SSTV_MS2S(0.4167), { 1500, 2300 } } }; } /* namespace sstv */ -#endif/*__SSTV_H__*/ +#endif /*__SSTV_H__*/ diff --git a/firmware/common/test_packet.cpp b/firmware/common/test_packet.cpp index ac30cf125..6816ba57d 100644 --- a/firmware/common/test_packet.cpp +++ b/firmware/common/test_packet.cpp @@ -26,27 +26,27 @@ namespace testapp { size_t Packet::length() const { - return decoder_.symbols_count(); + return decoder_.symbols_count(); } bool Packet::is_valid() const { - return true; + return true; } Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } FormattedSymbols Packet::symbols_formatted() const { - return format_symbols(decoder_); + return format_symbols(decoder_); } uint32_t Packet::value() const { - return (reader_.read(10 * 8, 6) << 8) | reader_.read(9 * 8, 8); + return (reader_.read(10 * 8, 6) << 8) | reader_.read(9 * 8, 8); } uint32_t Packet::alt() const { - return reader_.read(1 * 8, 12); + return reader_.read(1 * 8, 12); } } /* namespace testapp */ diff --git a/firmware/common/test_packet.hpp b/firmware/common/test_packet.hpp index d4b23d7d6..8679f6bde 100644 --- a/firmware/common/test_packet.hpp +++ b/firmware/common/test_packet.hpp @@ -33,42 +33,41 @@ namespace testapp { class Packet { -public: - Packet( - const baseband::Packet& packet - ) : packet_ { packet }, - decoder_ { packet_ }, - reader_ { decoder_ } - { - } + public: + Packet( + const baseband::Packet& packet) + : packet_{packet}, + decoder_{packet_}, + reader_{decoder_} { + } - size_t length() const; - - bool is_valid() const; + size_t length() const; - Timestamp received_at() const; + bool is_valid() const; - uint32_t value() const; - uint32_t alt() const; - /*std::string serial_number() const; - uint32_t GPS_altitude() const; - float GPS_latitude() const; - float GPS_longitude() const; - std::string signature() const; - uint32_t battery_voltage() const;*/ + Timestamp received_at() const; - FormattedSymbols symbols_formatted() const; + uint32_t value() const; + uint32_t alt() const; + /*std::string serial_number() const; + uint32_t GPS_altitude() const; + float GPS_latitude() const; + float GPS_longitude() const; + std::string signature() const; + uint32_t battery_voltage() const;*/ - //bool crc_ok() const; + FormattedSymbols symbols_formatted() const; -private: - using Reader = FieldReader; + // bool crc_ok() const; - const baseband::Packet packet_; - const ManchesterDecoder decoder_; - const Reader reader_; + private: + using Reader = FieldReader; + + const baseband::Packet packet_; + const ManchesterDecoder decoder_; + const Reader reader_; }; } /* namespace testapp */ -#endif/*__TEST_PACKET_H__*/ +#endif /*__TEST_PACKET_H__*/ diff --git a/firmware/common/thread_base.hpp b/firmware/common/thread_base.hpp index 921aeeb99..61ddcade3 100644 --- a/firmware/common/thread_base.hpp +++ b/firmware/common/thread_base.hpp @@ -25,19 +25,19 @@ #include class ThreadBase { -public: - virtual ~ThreadBase() = default; - -protected: - static msg_t fn(void* arg) { - auto obj = static_cast(arg); - obj->run(); + public: + virtual ~ThreadBase() = default; - return 0; - } + protected: + static msg_t fn(void* arg) { + auto obj = static_cast(arg); + obj->run(); -private: - virtual void run() = 0; + return 0; + } + + private: + virtual void run() = 0; }; -#endif/*__THREAD_BASE_H__*/ +#endif /*__THREAD_BASE_H__*/ diff --git a/firmware/common/thread_wait.cpp b/firmware/common/thread_wait.cpp index dd3aac271..7b28d5926 100644 --- a/firmware/common/thread_wait.cpp +++ b/firmware/common/thread_wait.cpp @@ -22,21 +22,21 @@ #include "thread_wait.hpp" int ThreadWait::sleep() { - chSysLock(); - thread_to_wake = chThdSelf(); - chSchGoSleepS(THD_STATE_SUSPENDED); - const auto result = chThdSelf()->p_u.rdymsg; - chSysUnlock(); - return result; + chSysLock(); + thread_to_wake = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + const auto result = chThdSelf()->p_u.rdymsg; + chSysUnlock(); + return result; } bool ThreadWait::wake_from_interrupt(const int value) { - if( thread_to_wake ) { - thread_to_wake->p_u.rdymsg = value; - chSchReadyI(thread_to_wake); - thread_to_wake = nullptr; - return true; - } else { - return false; - } + if (thread_to_wake) { + thread_to_wake->p_u.rdymsg = value; + chSchReadyI(thread_to_wake); + thread_to_wake = nullptr; + return true; + } else { + return false; + } } diff --git a/firmware/common/thread_wait.hpp b/firmware/common/thread_wait.hpp index 9380dc43b..455a7299c 100644 --- a/firmware/common/thread_wait.hpp +++ b/firmware/common/thread_wait.hpp @@ -25,12 +25,12 @@ #include class ThreadWait { -public: - int sleep(); - bool wake_from_interrupt(const int value); + public: + int sleep(); + bool wake_from_interrupt(const int value); -private: - Thread* thread_to_wake { nullptr }; + private: + Thread* thread_to_wake{nullptr}; }; -#endif/*__THREAD_WAIT_H__*/ +#endif /*__THREAD_WAIT_H__*/ diff --git a/firmware/common/tonesets.hpp b/firmware/common/tonesets.hpp index 876ef7e6e..e6ed73146 100644 --- a/firmware/common/tonesets.hpp +++ b/firmware/common/tonesets.hpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek - * + * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify @@ -31,99 +31,94 @@ #define BEEP_TONES_NB 6 -#define DTMF_C0 TONES_F2D(1209, TONES_SAMPLERATE) -#define DTMF_C1 TONES_F2D(1336, TONES_SAMPLERATE) -#define DTMF_C2 TONES_F2D(1477, TONES_SAMPLERATE) -#define DTMF_C3 TONES_F2D(1633, TONES_SAMPLERATE) -#define DTMF_R0 TONES_F2D(697, TONES_SAMPLERATE) -#define DTMF_R1 TONES_F2D(770, TONES_SAMPLERATE) -#define DTMF_R2 TONES_F2D(852, TONES_SAMPLERATE) -#define DTMF_R3 TONES_F2D(941, TONES_SAMPLERATE) +#define DTMF_C0 TONES_F2D(1209, TONES_SAMPLERATE) +#define DTMF_C1 TONES_F2D(1336, TONES_SAMPLERATE) +#define DTMF_C2 TONES_F2D(1477, TONES_SAMPLERATE) +#define DTMF_C3 TONES_F2D(1633, TONES_SAMPLERATE) +#define DTMF_R0 TONES_F2D(697, TONES_SAMPLERATE) +#define DTMF_R1 TONES_F2D(770, TONES_SAMPLERATE) +#define DTMF_R2 TONES_F2D(852, TONES_SAMPLERATE) +#define DTMF_R3 TONES_F2D(941, TONES_SAMPLERATE) const std::array ccir_deltas = { - TONES_F2D(1981, TONES_SAMPLERATE), - TONES_F2D(1124, TONES_SAMPLERATE), - TONES_F2D(1197, TONES_SAMPLERATE), - TONES_F2D(1275, TONES_SAMPLERATE), - TONES_F2D(1358, TONES_SAMPLERATE), - TONES_F2D(1446, TONES_SAMPLERATE), - TONES_F2D(1540, TONES_SAMPLERATE), - TONES_F2D(1640, TONES_SAMPLERATE), - TONES_F2D(1747, TONES_SAMPLERATE), - TONES_F2D(1860, TONES_SAMPLERATE), - TONES_F2D(2400, TONES_SAMPLERATE), - TONES_F2D(930, TONES_SAMPLERATE), - TONES_F2D(2247, TONES_SAMPLERATE), - TONES_F2D(991, TONES_SAMPLERATE), - TONES_F2D(2110, TONES_SAMPLERATE), - TONES_F2D(1055, TONES_SAMPLERATE) -}; + TONES_F2D(1981, TONES_SAMPLERATE), + TONES_F2D(1124, TONES_SAMPLERATE), + TONES_F2D(1197, TONES_SAMPLERATE), + TONES_F2D(1275, TONES_SAMPLERATE), + TONES_F2D(1358, TONES_SAMPLERATE), + TONES_F2D(1446, TONES_SAMPLERATE), + TONES_F2D(1540, TONES_SAMPLERATE), + TONES_F2D(1640, TONES_SAMPLERATE), + TONES_F2D(1747, TONES_SAMPLERATE), + TONES_F2D(1860, TONES_SAMPLERATE), + TONES_F2D(2400, TONES_SAMPLERATE), + TONES_F2D(930, TONES_SAMPLERATE), + TONES_F2D(2247, TONES_SAMPLERATE), + TONES_F2D(991, TONES_SAMPLERATE), + TONES_F2D(2110, TONES_SAMPLERATE), + TONES_F2D(1055, TONES_SAMPLERATE)}; // 0123456789ABCD#* const uint32_t dtmf_deltas[16][2] = { - { DTMF_C1, DTMF_R3 }, - { DTMF_C0, DTMF_R0 }, - { DTMF_C1, DTMF_R0 }, - { DTMF_C2, DTMF_R0 }, - { DTMF_C0, DTMF_R1 }, - { DTMF_C1, DTMF_R1 }, - { DTMF_C2, DTMF_R1 }, - { DTMF_C0, DTMF_R2 }, - { DTMF_C1, DTMF_R2 }, - { DTMF_C2, DTMF_R2 }, - { DTMF_C3, DTMF_R0 }, - { DTMF_C3, DTMF_R1 }, - { DTMF_C3, DTMF_R2 }, - { DTMF_C3, DTMF_R3 }, - { DTMF_C2, DTMF_R3 }, - { DTMF_C0, DTMF_R3 } -}; + {DTMF_C1, DTMF_R3}, + {DTMF_C0, DTMF_R0}, + {DTMF_C1, DTMF_R0}, + {DTMF_C2, DTMF_R0}, + {DTMF_C0, DTMF_R1}, + {DTMF_C1, DTMF_R1}, + {DTMF_C2, DTMF_R1}, + {DTMF_C0, DTMF_R2}, + {DTMF_C1, DTMF_R2}, + {DTMF_C2, DTMF_R2}, + {DTMF_C3, DTMF_R0}, + {DTMF_C3, DTMF_R1}, + {DTMF_C3, DTMF_R2}, + {DTMF_C3, DTMF_R3}, + {DTMF_C2, DTMF_R3}, + {DTMF_C0, DTMF_R3}}; const std::array eia_deltas = { - TONES_F2D(600, TONES_SAMPLERATE), - TONES_F2D(741, TONES_SAMPLERATE), - TONES_F2D(882, TONES_SAMPLERATE), - TONES_F2D(1023, TONES_SAMPLERATE), - TONES_F2D(1164, TONES_SAMPLERATE), - TONES_F2D(1305, TONES_SAMPLERATE), - TONES_F2D(1446, TONES_SAMPLERATE), - TONES_F2D(1587, TONES_SAMPLERATE), - TONES_F2D(1728, TONES_SAMPLERATE), - TONES_F2D(1869, TONES_SAMPLERATE), - TONES_F2D(2151, TONES_SAMPLERATE), - TONES_F2D(2433, TONES_SAMPLERATE), - TONES_F2D(2010, TONES_SAMPLERATE), - TONES_F2D(2292, TONES_SAMPLERATE), - TONES_F2D(459, TONES_SAMPLERATE), - TONES_F2D(1091, TONES_SAMPLERATE) -}; + TONES_F2D(600, TONES_SAMPLERATE), + TONES_F2D(741, TONES_SAMPLERATE), + TONES_F2D(882, TONES_SAMPLERATE), + TONES_F2D(1023, TONES_SAMPLERATE), + TONES_F2D(1164, TONES_SAMPLERATE), + TONES_F2D(1305, TONES_SAMPLERATE), + TONES_F2D(1446, TONES_SAMPLERATE), + TONES_F2D(1587, TONES_SAMPLERATE), + TONES_F2D(1728, TONES_SAMPLERATE), + TONES_F2D(1869, TONES_SAMPLERATE), + TONES_F2D(2151, TONES_SAMPLERATE), + TONES_F2D(2433, TONES_SAMPLERATE), + TONES_F2D(2010, TONES_SAMPLERATE), + TONES_F2D(2292, TONES_SAMPLERATE), + TONES_F2D(459, TONES_SAMPLERATE), + TONES_F2D(1091, TONES_SAMPLERATE)}; const std::array zvei_deltas = { - TONES_F2D(2400, TONES_SAMPLERATE), - TONES_F2D(1060, TONES_SAMPLERATE), - TONES_F2D(1160, TONES_SAMPLERATE), - TONES_F2D(1270, TONES_SAMPLERATE), - TONES_F2D(1400, TONES_SAMPLERATE), - TONES_F2D(1530, TONES_SAMPLERATE), - TONES_F2D(1670, TONES_SAMPLERATE), - TONES_F2D(1830, TONES_SAMPLERATE), - TONES_F2D(2000, TONES_SAMPLERATE), - TONES_F2D(2200, TONES_SAMPLERATE), - TONES_F2D(2800, TONES_SAMPLERATE), - TONES_F2D(810, TONES_SAMPLERATE), - TONES_F2D(970, TONES_SAMPLERATE), - TONES_F2D(885, TONES_SAMPLERATE), - TONES_F2D(2600, TONES_SAMPLERATE), - TONES_F2D(680, TONES_SAMPLERATE) -}; + TONES_F2D(2400, TONES_SAMPLERATE), + TONES_F2D(1060, TONES_SAMPLERATE), + TONES_F2D(1160, TONES_SAMPLERATE), + TONES_F2D(1270, TONES_SAMPLERATE), + TONES_F2D(1400, TONES_SAMPLERATE), + TONES_F2D(1530, TONES_SAMPLERATE), + TONES_F2D(1670, TONES_SAMPLERATE), + TONES_F2D(1830, TONES_SAMPLERATE), + TONES_F2D(2000, TONES_SAMPLERATE), + TONES_F2D(2200, TONES_SAMPLERATE), + TONES_F2D(2800, TONES_SAMPLERATE), + TONES_F2D(810, TONES_SAMPLERATE), + TONES_F2D(970, TONES_SAMPLERATE), + TONES_F2D(885, TONES_SAMPLERATE), + TONES_F2D(2600, TONES_SAMPLERATE), + TONES_F2D(680, TONES_SAMPLERATE)}; const uint32_t beep_deltas[BEEP_TONES_NB] = { - TONES_F2D(1475, TONES_SAMPLERATE), - TONES_F2D(740, TONES_SAMPLERATE), - TONES_F2D(587, TONES_SAMPLERATE), - TONES_F2D(1109, TONES_SAMPLERATE), - TONES_F2D(831, TONES_SAMPLERATE), - TONES_F2D(740, TONES_SAMPLERATE) -}; + TONES_F2D(1475, TONES_SAMPLERATE), + TONES_F2D(740, TONES_SAMPLERATE), + TONES_F2D(587, TONES_SAMPLERATE), + TONES_F2D(1109, TONES_SAMPLERATE), + TONES_F2D(831, TONES_SAMPLERATE), + TONES_F2D(740, TONES_SAMPLERATE)}; -#endif/*__TONESETS_H__*/ +#endif /*__TONESETS_H__*/ diff --git a/firmware/common/tpms_packet.cpp b/firmware/common/tpms_packet.cpp index 9f8df9bd2..7935a260f 100644 --- a/firmware/common/tpms_packet.cpp +++ b/firmware/common/tpms_packet.cpp @@ -26,158 +26,157 @@ namespace tpms { Timestamp Packet::received_at() const { - return packet_.timestamp(); + return packet_.timestamp(); } FormattedSymbols Packet::symbols_formatted() const { - return format_symbols(decoder_); + return format_symbols(decoder_); } Optional Packet::reading_fsk_19k2_schrader() const { - const auto length = crc_valid_length(); - - switch(length) { - case 64: - return Reading { - Reading::Type::FLM_64, - (uint32_t)reader_.read(0, 32), - Pressure { static_cast(reader_.read(32, 8)) * 4 / 3 }, - Temperature { static_cast(reader_.read(40, 8) & 0x7f) - 56 } - }; - - case 72: - return Reading { - Reading::Type::FLM_72, - (uint32_t)reader_.read(0, 32), - Pressure { static_cast(reader_.read(40, 8)) * 4 / 3 }, - Temperature { static_cast(reader_.read(48, 8)) - 56 } - }; - - case 80: - return Reading { - Reading::Type::FLM_80, - (uint32_t)reader_.read(8, 32), - Pressure { static_cast(reader_.read(48, 8)) * 4 / 3 }, - Temperature { static_cast(reader_.read(56, 8)) - 56 } - }; - - default: - return { }; - } + const auto length = crc_valid_length(); + + switch (length) { + case 64: + return Reading{ + Reading::Type::FLM_64, + (uint32_t)reader_.read(0, 32), + Pressure{static_cast(reader_.read(32, 8)) * 4 / 3}, + Temperature{static_cast(reader_.read(40, 8) & 0x7f) - 56}}; + + case 72: + return Reading{ + Reading::Type::FLM_72, + (uint32_t)reader_.read(0, 32), + Pressure{static_cast(reader_.read(40, 8)) * 4 / 3}, + Temperature{static_cast(reader_.read(48, 8)) - 56}}; + + case 80: + return Reading{ + Reading::Type::FLM_80, + (uint32_t)reader_.read(8, 32), + Pressure{static_cast(reader_.read(48, 8)) * 4 / 3}, + Temperature{static_cast(reader_.read(56, 8)) - 56}}; + + default: + return {}; + } } Optional Packet::reading_ook_8k192_schrader() const { - /* - * Preamble: 11*2, 01*14, 11, 10 - * Function code: 3 Manchester symbols - * ID: 24 Manchester symbols (one variant seen with 21 symbols?) - * Pressure: 8 Manchester symbols - * Checksum: 2 Manchester symbols (2 LSBs of sum incl this field == 3) - */ - const auto flags = reader_.read(0, 3); - const auto checksum = reader_.read(35, 2); - - uint32_t checksum_calculated = reader_.read(0, 1); - for(size_t i=1; i<37; i+=2) { - checksum_calculated += reader_.read(i, 2); - } - - if( (checksum_calculated & 3) == 3 ) { - return Reading { - Reading::Type::Schrader, - (uint32_t)reader_.read(3, 24), - Pressure { static_cast(reader_.read(27, 8)) * 4 / 3 }, - { }, - Flags { static_cast((flags << 4) | checksum) } - }; - } else { - return { }; - } + /* + * Preamble: 11*2, 01*14, 11, 10 + * Function code: 3 Manchester symbols + * ID: 24 Manchester symbols (one variant seen with 21 symbols?) + * Pressure: 8 Manchester symbols + * Checksum: 2 Manchester symbols (2 LSBs of sum incl this field == 3) + */ + const auto flags = reader_.read(0, 3); + const auto checksum = reader_.read(35, 2); + + uint32_t checksum_calculated = reader_.read(0, 1); + for (size_t i = 1; i < 37; i += 2) { + checksum_calculated += reader_.read(i, 2); + } + + if ((checksum_calculated & 3) == 3) { + return Reading{ + Reading::Type::Schrader, + (uint32_t)reader_.read(3, 24), + Pressure{static_cast(reader_.read(27, 8)) * 4 / 3}, + {}, + Flags{static_cast((flags << 4) | checksum)}}; + } else { + return {}; + } } Optional Packet::reading_ook_8k4_schrader() const { - /* - * Preamble: 01*40 - * System ID: 01100101, ??*20 (not really sure what this data is) - * ID: 32 Manchester symbols - * Value: 8 Manchester symbols (pressure) - * Value: 8 Manchester symbols (temperature) - * Checksum: 8 Manchester symbols (uint8_t sum of bytes starting with system ID) - */ - /* NOTE: First four bits of packet are consumed in preamble detection. - * Those bits assumed to be 0b0100", which may not be entirely true... - */ - constexpr uint8_t first_nibble = 0x4; - // const auto system_id = (first_nibble << 20) | reader_.read(0, 20); - const auto id = reader_.read(20, 32); - const auto value_0 = reader_.read(52, 8); - const auto value_1 = reader_.read(60, 8); - const auto checksum = reader_.read(68, 8); - - uint8_t checksum_calculated = (first_nibble << 4) | reader_.read(0, 4); - for(size_t i=4; i<68; i+=8) { - checksum_calculated += reader_.read(i, 8); - } - - if( checksum_calculated == checksum ) { - return Reading { - Reading::Type::GMC_96, - (uint32_t)id, - Pressure { static_cast(value_0) * 11 / 4 }, - Temperature { static_cast(value_1) - 61 } - }; - } else { - return { }; - } + /* + * Preamble: 01*40 + * System ID: 01100101, ??*20 (not really sure what this data is) + * ID: 32 Manchester symbols + * Value: 8 Manchester symbols (pressure) + * Value: 8 Manchester symbols (temperature) + * Checksum: 8 Manchester symbols (uint8_t sum of bytes starting with system ID) + */ + /* NOTE: First four bits of packet are consumed in preamble detection. + * Those bits assumed to be 0b0100", which may not be entirely true... + */ + constexpr uint8_t first_nibble = 0x4; + // const auto system_id = (first_nibble << 20) | reader_.read(0, 20); + const auto id = reader_.read(20, 32); + const auto value_0 = reader_.read(52, 8); + const auto value_1 = reader_.read(60, 8); + const auto checksum = reader_.read(68, 8); + + uint8_t checksum_calculated = (first_nibble << 4) | reader_.read(0, 4); + for (size_t i = 4; i < 68; i += 8) { + checksum_calculated += reader_.read(i, 8); + } + + if (checksum_calculated == checksum) { + return Reading{ + Reading::Type::GMC_96, + (uint32_t)id, + Pressure{static_cast(value_0) * 11 / 4}, + Temperature{static_cast(value_1) - 61}}; + } else { + return {}; + } } Optional Packet::reading() const { - switch( signal_type() ) { - case SignalType::FSK_19k2_Schrader: return reading_fsk_19k2_schrader(); - case SignalType::OOK_8k192_Schrader: return reading_ook_8k192_schrader(); - case SignalType::OOK_8k4_Schrader: return reading_ook_8k4_schrader(); - default: return { }; - } + switch (signal_type()) { + case SignalType::FSK_19k2_Schrader: + return reading_fsk_19k2_schrader(); + case SignalType::OOK_8k192_Schrader: + return reading_ook_8k192_schrader(); + case SignalType::OOK_8k4_Schrader: + return reading_ook_8k4_schrader(); + default: + return {}; + } } size_t Packet::crc_valid_length() const { - constexpr uint32_t checksum_bytes = 0b1111111; - constexpr uint32_t crc_72_bytes = 0b111111111; - constexpr uint32_t crc_80_bytes = 0b1111111110; - - std::array bytes; - for(size_t i=0; i crc_72 { 0x01, 0x00 }; - CRC<8> crc_80 { 0x01, 0x00 }; - - for(size_t i=0; i bytes; + for (size_t i = 0; i < bytes.size(); i++) { + bytes[i] = reader_.read(i * 8, 8); + } + + uint32_t checksum = 0; + CRC<8> crc_72{0x01, 0x00}; + CRC<8> crc_80{0x01, 0x00}; + + for (size_t i = 0; i < bytes.size(); i++) { + const uint32_t byte_mask = 1 << i; + const auto byte = bytes[i]; + + if (checksum_bytes & byte_mask) { + checksum += byte; + } + if (crc_72_bytes & byte_mask) { + crc_72.process_byte(byte); + } + if (crc_80_bytes & byte_mask) { + crc_80.process_byte(byte); + } + } + + if (crc_80.checksum() == 0) { + return 80; + } else if (crc_72.checksum() == 0) { + return 72; + } else if ((checksum & 0xff) == bytes[7]) { + return 64; + } else { + return 0; + } } } /* namespace tpms */ diff --git a/firmware/common/tpms_packet.hpp b/firmware/common/tpms_packet.hpp index 75ad00775..34b7296cc 100644 --- a/firmware/common/tpms_packet.hpp +++ b/firmware/common/tpms_packet.hpp @@ -28,8 +28,8 @@ #include "optional.hpp" #include "units.hpp" -using units::Temperature; using units::Pressure; +using units::Temperature; #include "baseband_packet.hpp" #include "manchester.hpp" @@ -40,133 +40,127 @@ namespace tpms { using Flags = uint8_t; enum SignalType { - FSK_19k2_Schrader = 1, - OOK_8k192_Schrader = 2, - OOK_8k4_Schrader = 3, + FSK_19k2_Schrader = 1, + OOK_8k192_Schrader = 2, + OOK_8k4_Schrader = 3, }; class TransponderID { -public: - constexpr TransponderID( - ) : id_ { 0 } - { - } - - constexpr TransponderID( - const uint32_t id - ) : id_ { id } - { - } - - constexpr uint32_t value() const { - return id_; - } - -private: - uint32_t id_; + public: + constexpr TransponderID() + : id_{0} { + } + + constexpr TransponderID( + const uint32_t id) + : id_{id} { + } + + constexpr uint32_t value() const { + return id_; + } + + private: + uint32_t id_; }; class Reading { -public: - enum Type { - None = 0, - FLM_64 = 1, - FLM_72 = 2, - FLM_80 = 3, - Schrader = 4, - GMC_96 = 5, - }; - - constexpr Reading( - ) : type_ { Type::None } - { - } - - constexpr Reading( - Type type, - TransponderID id - ) : type_ { type }, - id_ { id } - { - } - - constexpr Reading( - Type type, - TransponderID id, - Optional pressure = { }, - Optional temperature = { }, - Optional flags = { } - ) : type_ { type }, - id_ { id }, - pressure_ { pressure }, - temperature_ { temperature }, - flags_ { flags } - { - } - - Type type() const { - return type_; - } - - TransponderID id() const { - return id_; - } - - Optional pressure() const { - return pressure_; - } - - Optional temperature() const { - return temperature_; - } - - Optional flags() const { - return flags_; - } - -private: - Type type_ { Type::None }; - TransponderID id_ { 0 }; - Optional pressure_ { }; - Optional temperature_ { }; - Optional flags_ { }; + public: + enum Type { + None = 0, + FLM_64 = 1, + FLM_72 = 2, + FLM_80 = 3, + Schrader = 4, + GMC_96 = 5, + }; + + constexpr Reading() + : type_{Type::None} { + } + + constexpr Reading( + Type type, + TransponderID id) + : type_{type}, + id_{id} { + } + + constexpr Reading( + Type type, + TransponderID id, + Optional pressure = {}, + Optional temperature = {}, + Optional flags = {}) + : type_{type}, + id_{id}, + pressure_{pressure}, + temperature_{temperature}, + flags_{flags} { + } + + Type type() const { + return type_; + } + + TransponderID id() const { + return id_; + } + + Optional pressure() const { + return pressure_; + } + + Optional temperature() const { + return temperature_; + } + + Optional flags() const { + return flags_; + } + + private: + Type type_{Type::None}; + TransponderID id_{0}; + Optional pressure_{}; + Optional temperature_{}; + Optional flags_{}; }; class Packet { -public: - constexpr Packet( - const baseband::Packet& packet, - const SignalType signal_type - ) : packet_ { packet }, - signal_type_ { signal_type }, - decoder_ { packet_, 0 }, - reader_ { decoder_ } - { - } + public: + constexpr Packet( + const baseband::Packet& packet, + const SignalType signal_type) + : packet_{packet}, + signal_type_{signal_type}, + decoder_{packet_, 0}, + reader_{decoder_} { + } - SignalType signal_type() const { return signal_type_; } - Timestamp received_at() const; + SignalType signal_type() const { return signal_type_; } + Timestamp received_at() const; - FormattedSymbols symbols_formatted() const; + FormattedSymbols symbols_formatted() const; - Optional reading() const; + Optional reading() const; -private: - using Reader = FieldReader; + private: + using Reader = FieldReader; - const baseband::Packet packet_; - const SignalType signal_type_; - const ManchesterDecoder decoder_; + const baseband::Packet packet_; + const SignalType signal_type_; + const ManchesterDecoder decoder_; - const Reader reader_; + const Reader reader_; - Optional reading_fsk_19k2_schrader() const; - Optional reading_ook_8k192_schrader() const; - Optional reading_ook_8k4_schrader() const; + Optional reading_fsk_19k2_schrader() const; + Optional reading_ook_8k192_schrader() const; + Optional reading_ook_8k4_schrader() const; - size_t crc_valid_length() const; + size_t crc_valid_length() const; }; } /* namespace tpms */ -#endif/*__TPMS_PACKET_H__*/ +#endif /*__TPMS_PACKET_H__*/ diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index c1a44894f..c07cabdb2 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -29,78 +29,77 @@ namespace ui { // CGA palette Color term_colors[16] = { - Color::black(), - Color::dark_blue(), - Color::dark_green(), - Color::dark_cyan(), - Color::dark_red(), - Color::dark_magenta(), - Color::dark_yellow(), - Color::light_grey(), - Color::dark_grey(), - Color::blue(), - Color::green(), - Color::cyan(), - Color::red(), - Color::magenta(), - Color::yellow(), - Color::white() -}; + Color::black(), + Color::dark_blue(), + Color::dark_green(), + Color::dark_cyan(), + Color::dark_red(), + Color::dark_magenta(), + Color::dark_yellow(), + Color::light_grey(), + Color::dark_grey(), + Color::blue(), + Color::green(), + Color::cyan(), + Color::red(), + Color::magenta(), + Color::yellow(), + Color::white()}; bool Rect::contains(const Point p) const { - return (p.x() >= left()) && (p.y() >= top()) && - (p.x() < right()) && (p.y() < bottom()); + return (p.x() >= left()) && (p.y() >= top()) && + (p.x() < right()) && (p.y() < bottom()); } Rect Rect::intersect(const Rect& o) const { - const auto x1 = std::max(left(), o.left()); - const auto x2 = std::min(right(), o.right()); - const auto y1 = std::max(top(), o.top()); - const auto y2 = std::min(bottom(), o.bottom()); - if( (x2 >= x1) && (y2 > y1) ) { - return { x1, y1, x2 - x1, y2 - y1 }; - } else { - return { }; - } + const auto x1 = std::max(left(), o.left()); + const auto x2 = std::min(right(), o.right()); + const auto y1 = std::max(top(), o.top()); + const auto y2 = std::min(bottom(), o.bottom()); + if ((x2 >= x1) && (y2 > y1)) { + return {x1, y1, x2 - x1, y2 - y1}; + } else { + return {}; + } } // TODO: This violates the principle of least surprise! // This does a union, but that might not be obvious from "+=" syntax. Rect& Rect::operator+=(const Rect& p) { - if( is_empty() ) { - *this = p; - } - if( !p.is_empty() ) { - const auto x1 = std::min(left(), p.left()); - const auto y1 = std::min(top(), p.top()); - _pos = { x1, y1 }; - const auto x2 = std::max(right(), p.right()); - const auto y2 = std::max(bottom(), p.bottom()); - _size = { x2 - x1, y2 - y1 }; - } - return *this; + if (is_empty()) { + *this = p; + } + if (!p.is_empty()) { + const auto x1 = std::min(left(), p.left()); + const auto y1 = std::min(top(), p.top()); + _pos = {x1, y1}; + const auto x2 = std::max(right(), p.right()); + const auto y2 = std::max(bottom(), p.bottom()); + _size = {x2 - x1, y2 - y1}; + } + return *this; } Rect& Rect::operator+=(const Point& p) { - _pos += p; - return *this; + _pos += p; + return *this; } Rect& Rect::operator-=(const Point& p) { - _pos -= p; - return *this; + _pos -= p; + return *this; } Point polar_to_point(float angle, uint32_t distance) { - //polar to compass with y negated for screen drawing - return Point(sin_f32(DEG_TO_RAD(-angle) + pi) * distance, - sin_f32(DEG_TO_RAD(-angle) - (pi / 2)) * distance); + // polar to compass with y negated for screen drawing + return Point(sin_f32(DEG_TO_RAD(-angle) + pi) * distance, + sin_f32(DEG_TO_RAD(-angle) - (pi / 2)) * distance); } Point fast_polar_to_point(int32_t angle, uint32_t distance) { - //polar to compass with y negated for screen drawing - return Point((int16_sin_s4(((1<<16)*(-angle + 180))/360) * distance)/(1<<16), - (int16_sin_s4(((1<<16)*(-angle - 90))/360) * distance)/(1<<16)); + // polar to compass with y negated for screen drawing + return Point((int16_sin_s4(((1 << 16) * (-angle + 180)) / 360) * distance) / (1 << 16), + (int16_sin_s4(((1 << 16) * (-angle - 90)) / 360) * distance) / (1 << 16)); } } /* namespace ui */ diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index 853a73676..e4e686aae 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -32,324 +32,313 @@ using Coord = int16_t; using Dim = int16_t; struct Color { - uint16_t v; // rrrrrGGGGGGbbbbb - - constexpr Color( - ) : v { 0 } - { - } - - constexpr Color( - uint16_t v - ) : v { v } - { - } - - constexpr Color( - uint8_t r, - uint8_t g, - uint8_t b - ) : v { - static_cast( - ((r & 0xf8) << 8) - | ((g & 0xfc) << 3) - | ((b & 0xf8) >> 3) - )} - { - } - - uint8_t to_greyscale() { - uint32_t r = ((v >> 11) & 31U) << 3; - uint32_t g = ((v >> 5) & 63U) << 2; - uint32_t b = (v & 31U) << 3; - - uint32_t grey = (r * 299 + g * 587 + b * 114) / 1000; - - return (uint8_t)grey; - } - - Color operator-() const { - return (v ^ 0xffff); - } - - static constexpr Color black() { - return { 0, 0, 0 }; - } - - static constexpr Color red() { - return { 255, 0, 0 }; - } - static constexpr Color dark_red() { - return { 159, 0, 0 }; - } - - static constexpr Color orange() { - return { 255, 175, 0 }; - } - static constexpr Color dark_orange() { - return { 191, 95, 0 }; - } - - static constexpr Color yellow() { - return { 255, 255, 0 }; - } - static constexpr Color dark_yellow() { - return { 191, 191, 0 }; - } - - static constexpr Color green() { - return { 0, 255, 0 }; - } - static constexpr Color dark_green() { - return { 0, 159, 0 }; - } - - static constexpr Color blue() { - return { 0, 0, 255 }; - } - static constexpr Color dark_blue() { - return { 0, 0, 191 }; - } - - static constexpr Color cyan() { - return { 0, 255, 255 }; - } - static constexpr Color dark_cyan() { - return { 0, 191, 191 }; - } - - static constexpr Color magenta() { - return { 255, 0, 255 }; - } - static constexpr Color dark_magenta() { - return { 191, 0, 191 }; - } - - static constexpr Color white() { - return { 255, 255, 255 }; - } - - static constexpr Color light_grey() { - return { 191, 191, 191 }; - } - static constexpr Color grey() { - return { 127, 127, 127 }; - } - static constexpr Color dark_grey() { - return { 63, 63, 63 }; - } - - static constexpr Color purple() { - return { 204, 0, 102 }; - } + uint16_t v; // rrrrrGGGGGGbbbbb + + constexpr Color() + : v{0} { + } + + constexpr Color( + uint16_t v) + : v{v} { + } + + constexpr Color( + uint8_t r, + uint8_t g, + uint8_t b) + : v{ + static_cast( + ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3))} { + } + + uint8_t to_greyscale() { + uint32_t r = ((v >> 11) & 31U) << 3; + uint32_t g = ((v >> 5) & 63U) << 2; + uint32_t b = (v & 31U) << 3; + + uint32_t grey = (r * 299 + g * 587 + b * 114) / 1000; + + return (uint8_t)grey; + } + + Color operator-() const { + return (v ^ 0xffff); + } + + static constexpr Color black() { + return {0, 0, 0}; + } + + static constexpr Color red() { + return {255, 0, 0}; + } + static constexpr Color dark_red() { + return {159, 0, 0}; + } + + static constexpr Color orange() { + return {255, 175, 0}; + } + static constexpr Color dark_orange() { + return {191, 95, 0}; + } + + static constexpr Color yellow() { + return {255, 255, 0}; + } + static constexpr Color dark_yellow() { + return {191, 191, 0}; + } + + static constexpr Color green() { + return {0, 255, 0}; + } + static constexpr Color dark_green() { + return {0, 159, 0}; + } + + static constexpr Color blue() { + return {0, 0, 255}; + } + static constexpr Color dark_blue() { + return {0, 0, 191}; + } + + static constexpr Color cyan() { + return {0, 255, 255}; + } + static constexpr Color dark_cyan() { + return {0, 191, 191}; + } + + static constexpr Color magenta() { + return {255, 0, 255}; + } + static constexpr Color dark_magenta() { + return {191, 0, 191}; + } + + static constexpr Color white() { + return {255, 255, 255}; + } + + static constexpr Color light_grey() { + return {191, 191, 191}; + } + static constexpr Color grey() { + return {127, 127, 127}; + } + static constexpr Color dark_grey() { + return {63, 63, 63}; + } + + static constexpr Color purple() { + return {204, 0, 102}; + } }; extern Color term_colors[16]; struct ColorRGB888 { - uint8_t r; - uint8_t g; - uint8_t b; + uint8_t r; + uint8_t g; + uint8_t b; }; struct Point { -private: - Coord _x; - Coord _y; - -public: - constexpr Point( - ) : _x { 0 }, - _y { 0 } - { - } - - constexpr Point( - int x, - int y - ) : _x { static_cast(x) }, - _y { static_cast(y) } - { - } - - constexpr int x() const { - return _x; - } - - constexpr int y() const { - return _y; - } - - constexpr Point operator-() const { - return { -_x, -_y }; - } - - constexpr Point operator+(const Point& p) const { - return { _x + p._x, _y + p._y }; - } - - constexpr Point operator-(const Point& p) const { - return { _x - p._x, _y - p._y }; - } - - Point& operator+=(const Point& p) { - _x += p._x; - _y += p._y; - return *this; - } - - Point& operator-=(const Point& p) { - _x -= p._x; - _y -= p._y; - return *this; - } + private: + Coord _x; + Coord _y; + + public: + constexpr Point() + : _x{0}, + _y{0} { + } + + constexpr Point( + int x, + int y) + : _x{static_cast(x)}, + _y{static_cast(y)} { + } + + constexpr int x() const { + return _x; + } + + constexpr int y() const { + return _y; + } + + constexpr Point operator-() const { + return {-_x, -_y}; + } + + constexpr Point operator+(const Point& p) const { + return {_x + p._x, _y + p._y}; + } + + constexpr Point operator-(const Point& p) const { + return {_x - p._x, _y - p._y}; + } + + Point& operator+=(const Point& p) { + _x += p._x; + _y += p._y; + return *this; + } + + Point& operator-=(const Point& p) { + _x -= p._x; + _y -= p._y; + return *this; + } }; struct Size { -private: - Dim _w; - Dim _h; - -public: - constexpr Size( - ) : _w { 0 }, - _h { 0 } - { - } - - constexpr Size( - int w, - int h - ) : _w { static_cast(w) }, - _h { static_cast(h) } - { - } - - int width() const { - return _w; - } - - int height() const { - return _h; - } - - bool is_empty() const { - return (_w < 1) || (_h < 1); - } + private: + Dim _w; + Dim _h; + + public: + constexpr Size() + : _w{0}, + _h{0} { + } + + constexpr Size( + int w, + int h) + : _w{static_cast(w)}, + _h{static_cast(h)} { + } + + int width() const { + return _w; + } + + int height() const { + return _h; + } + + bool is_empty() const { + return (_w < 1) || (_h < 1); + } }; struct Rect { -private: - Point _pos; - Size _size; - -public: - constexpr Rect( - ) : _pos { }, - _size { } - { - } - - constexpr Rect( - int x, int y, - int w, int h - ) : _pos { x, y }, - _size { w, h } - { - } - - constexpr Rect( - Point pos, - Size size - ) : _pos(pos), - _size(size) - { - } - - Point location() const { - return _pos; - } - - Size size() const { - return _size; - } - - int top() const { - return _pos.y(); - } - - int bottom() const { - return _pos.y() + _size.height(); - } - - int left() const { - return _pos.x(); - } - - int right() const { - return _pos.x() + _size.width(); - } - - int width() const { - return _size.width(); - } - - int height() const { - return _size.height(); - } - - Point center() const { - return { _pos.x() + _size.width() / 2, _pos.y() + _size.height() / 2 }; - } - - bool is_empty() const { - return _size.is_empty(); - } - - bool contains(const Point p) const; - - Rect intersect(const Rect& o) const; - - Rect operator+(const Point& p) const { - return { _pos + p, _size }; - } - - Rect& operator+=(const Rect& p); - Rect& operator+=(const Point& p); - Rect& operator-=(const Point& p); - - operator bool() const { - return !_size.is_empty(); - } + private: + Point _pos; + Size _size; + + public: + constexpr Rect() + : _pos{}, + _size{} { + } + + constexpr Rect( + int x, + int y, + int w, + int h) + : _pos{x, y}, + _size{w, h} { + } + + constexpr Rect( + Point pos, + Size size) + : _pos(pos), + _size(size) { + } + + Point location() const { + return _pos; + } + + Size size() const { + return _size; + } + + int top() const { + return _pos.y(); + } + + int bottom() const { + return _pos.y() + _size.height(); + } + + int left() const { + return _pos.x(); + } + + int right() const { + return _pos.x() + _size.width(); + } + + int width() const { + return _size.width(); + } + + int height() const { + return _size.height(); + } + + Point center() const { + return {_pos.x() + _size.width() / 2, _pos.y() + _size.height() / 2}; + } + + bool is_empty() const { + return _size.is_empty(); + } + + bool contains(const Point p) const; + + Rect intersect(const Rect& o) const; + + Rect operator+(const Point& p) const { + return {_pos + p, _size}; + } + + Rect& operator+=(const Rect& p); + Rect& operator+=(const Point& p); + Rect& operator-=(const Point& p); + + operator bool() const { + return !_size.is_empty(); + } }; struct Bitmap { - const Size size; - const uint8_t* const data; + const Size size; + const uint8_t* const data; }; enum class KeyEvent { - /* Ordinals map to bit positions reported by CPLD */ - Right = 0, - Left = 1, - Down = 2, - Up = 3, - Select = 4, - Dfu = 5, - Back = 6, /* Left and Up together */ + /* Ordinals map to bit positions reported by CPLD */ + Right = 0, + Left = 1, + Down = 2, + Up = 3, + Select = 4, + Dfu = 5, + Back = 6, /* Left and Up together */ }; using EncoderEvent = int32_t; struct TouchEvent { - enum class Type : uint32_t { - Start = 0, - Move = 1, - End = 2, - }; - - Point point; - Type type; + enum class Type : uint32_t { + Start = 0, + Move = 1, + End = 2, + }; + + Point point; + Type type; }; Point polar_to_point(float angle, uint32_t distance); @@ -358,4 +347,4 @@ Point fast_polar_to_point(int32_t angle, uint32_t distance); } /* namespace ui */ -#endif/*__UI_H__*/ +#endif /*__UI_H__*/ diff --git a/firmware/common/ui_focus.cpp b/firmware/common/ui_focus.cpp index 1b7e23dc4..8dcd55d74 100644 --- a/firmware/common/ui_focus.cpp +++ b/firmware/common/ui_focus.cpp @@ -33,39 +33,39 @@ namespace ui { Widget* FocusManager::focus_widget() const { - return focus_widget_; + return focus_widget_; } void FocusManager::set_focus_widget(Widget* const new_focus_widget) { - // Widget already has focus. - if( new_focus_widget == focus_widget() ) { - return; - } - - if( new_focus_widget ) { - // if( !new_focus_widget->visible() ) { - // if( new_focus_widget->hidden() ) { - // // New widget is not visible. Do nothing. - // // TODO: Should this be a debug assertion? - // return; - // } - if( !new_focus_widget->focusable() ) { - return; - } - } - - // Blur old widget. - if( focus_widget() ) { - focus_widget()->on_blur(); - focus_widget()->set_dirty(); - } - - focus_widget_ = new_focus_widget; - - if( focus_widget() ) { - focus_widget()->on_focus(); - focus_widget()->set_dirty(); - } + // Widget already has focus. + if (new_focus_widget == focus_widget()) { + return; + } + + if (new_focus_widget) { + // if( !new_focus_widget->visible() ) { + // if( new_focus_widget->hidden() ) { + // // New widget is not visible. Do nothing. + // // TODO: Should this be a debug assertion? + // return; + // } + if (!new_focus_widget->focusable()) { + return; + } + } + + // Blur old widget. + if (focus_widget()) { + focus_widget()->on_blur(); + focus_widget()->set_dirty(); + } + + focus_widget_ = new_focus_widget; + + if (focus_widget()) { + focus_widget()->on_focus(); + focus_widget()->set_dirty(); + } } using test_result_t = std::pair; @@ -73,149 +73,143 @@ using test_fn = std::function; using test_collection_t = std::vector; /* Walk all visible widgets in hierarchy, collecting those that pass test */ -template +template static void widget_collect_visible(Widget* const w, TestFn test, test_collection_t& collection) { - for(auto child : w->children()) { - if( !child->hidden() ) { - const auto result = test(child); - if( result.first ) { - collection.push_back(result); - } - widget_collect_visible(child, test, collection); - } - } + for (auto child : w->children()) { + if (!child->hidden()) { + const auto result = test(child); + if (result.first) { + collection.push_back(result); + } + widget_collect_visible(child, test, collection); + } + } } static int32_t rect_distances( - const KeyEvent direction, - const Rect& rect_start, - const Rect& rect_end -) { - Coord on_axis_max, on_axis_min; - - switch(direction) { - case KeyEvent::Right: - on_axis_max = rect_end.left(); - on_axis_min = rect_start.right(); - break; - - case KeyEvent::Left: - on_axis_max = rect_start.left(); - on_axis_min = rect_end.right(); - break; - - case KeyEvent::Down: - on_axis_max = rect_end.top(); - on_axis_min = rect_start.bottom(); - break; - - case KeyEvent::Up: - on_axis_max = rect_start.top(); - on_axis_min = rect_end.bottom(); - break; - - default: - return -1; - } - - Coord on_axis_distance = on_axis_max - on_axis_min; - if( on_axis_distance < 0 ) { - return -1; - } - - Coord perpendicular_axis_start, perpendicular_axis_end; - - switch(direction) { - case KeyEvent::Right: - case KeyEvent::Left: - perpendicular_axis_start = rect_start.center().y(); - perpendicular_axis_end = rect_end.center().y(); - break; - - case KeyEvent::Up: - case KeyEvent::Down: - perpendicular_axis_start = rect_start.center().x(); - perpendicular_axis_end = rect_end.center().x(); - break; - - default: - return -1; - } - - - switch(direction) { - case KeyEvent::Right: - case KeyEvent::Left: - return ((std::abs(perpendicular_axis_end - perpendicular_axis_start) + 1) ^ 3) * sqrt((on_axis_distance + 1)); - break; - - case KeyEvent::Up: - case KeyEvent::Down: - return (sqrt(std::abs(perpendicular_axis_end - perpendicular_axis_start) + 1)) * ((on_axis_distance + 1) ^ 3); - break; - - default: - return 0; - } - - + const KeyEvent direction, + const Rect& rect_start, + const Rect& rect_end) { + Coord on_axis_max, on_axis_min; + + switch (direction) { + case KeyEvent::Right: + on_axis_max = rect_end.left(); + on_axis_min = rect_start.right(); + break; + + case KeyEvent::Left: + on_axis_max = rect_start.left(); + on_axis_min = rect_end.right(); + break; + + case KeyEvent::Down: + on_axis_max = rect_end.top(); + on_axis_min = rect_start.bottom(); + break; + + case KeyEvent::Up: + on_axis_max = rect_start.top(); + on_axis_min = rect_end.bottom(); + break; + + default: + return -1; + } + + Coord on_axis_distance = on_axis_max - on_axis_min; + if (on_axis_distance < 0) { + return -1; + } + + Coord perpendicular_axis_start, perpendicular_axis_end; + + switch (direction) { + case KeyEvent::Right: + case KeyEvent::Left: + perpendicular_axis_start = rect_start.center().y(); + perpendicular_axis_end = rect_end.center().y(); + break; + + case KeyEvent::Up: + case KeyEvent::Down: + perpendicular_axis_start = rect_start.center().x(); + perpendicular_axis_end = rect_end.center().x(); + break; + + default: + return -1; + } + + switch (direction) { + case KeyEvent::Right: + case KeyEvent::Left: + return ((std::abs(perpendicular_axis_end - perpendicular_axis_start) + 1) ^ 3) * sqrt((on_axis_distance + 1)); + break; + + case KeyEvent::Up: + case KeyEvent::Down: + return (sqrt(std::abs(perpendicular_axis_end - perpendicular_axis_start) + 1)) * ((on_axis_distance + 1) ^ 3); + break; + + default: + return 0; + } } void FocusManager::update( - Widget* const top_widget, - const KeyEvent event -) { - if( focus_widget() ) { - const auto focus_screen_rect = focus_widget()->screen_rect(); - - const auto test_fn = [&focus_screen_rect, event](ui::Widget* const w) -> test_result_t { - // if( w->visible() && w->focusable() ) { - if( w->focusable() ) { - const auto distance = rect_distances(event, focus_screen_rect, w->screen_rect()); - if( distance >= 0 ) { - return { w, distance }; - } - } - - return { nullptr, 0 }; - }; - - const auto find_back_fn = [](ui::Widget* const w) -> test_result_t { - if( w->focusable() && (w->id == -1) ) - return { w, 0 }; - else - return { nullptr, 0 }; - }; - - test_collection_t collection; - widget_collect_visible(top_widget, test_fn, collection); - - const auto compare_fn = [](const test_result_t& a, const test_result_t& b) { - return a.second < b.second; - }; - - const auto nearest = std::min_element(collection.cbegin(), collection.cend(), compare_fn); - // Up and left to indicate back - if (event == KeyEvent::Back) { - collection.clear(); - widget_collect_visible(top_widget, find_back_fn, collection); - if (!collection.empty()) - set_focus_widget(collection[0].first); - } - else if( nearest != collection.cend() ) { - //focus->blur(); - const auto new_focus = (*nearest).first; - set_focus_widget(new_focus); - } else { - if ((focus_widget()->id >= 0) && (event == KeyEvent::Left)) { - // Stuck left, move to back button - collection.clear(); - widget_collect_visible(top_widget, find_back_fn, collection); - if (!collection.empty()) - set_focus_widget(collection[0].first); - } - } - } + Widget* const top_widget, + const KeyEvent event) { + if (focus_widget()) { + const auto focus_screen_rect = focus_widget()->screen_rect(); + + const auto test_fn = [&focus_screen_rect, event](ui::Widget* const w) -> test_result_t { + // if( w->visible() && w->focusable() ) { + if (w->focusable()) { + const auto distance = rect_distances(event, focus_screen_rect, w->screen_rect()); + if (distance >= 0) { + return {w, distance}; + } + } + + return {nullptr, 0}; + }; + + const auto find_back_fn = [](ui::Widget* const w) -> test_result_t { + if (w->focusable() && (w->id == -1)) + return {w, 0}; + else + return {nullptr, 0}; + }; + + test_collection_t collection; + widget_collect_visible(top_widget, test_fn, collection); + + const auto compare_fn = [](const test_result_t& a, const test_result_t& b) { + return a.second < b.second; + }; + + const auto nearest = std::min_element(collection.cbegin(), collection.cend(), compare_fn); + // Up and left to indicate back + if (event == KeyEvent::Back) { + collection.clear(); + widget_collect_visible(top_widget, find_back_fn, collection); + if (!collection.empty()) + set_focus_widget(collection[0].first); + } else if (nearest != collection.cend()) { + // focus->blur(); + const auto new_focus = (*nearest).first; + set_focus_widget(new_focus); + } else { + if ((focus_widget()->id >= 0) && (event == KeyEvent::Left)) { + // Stuck left, move to back button + collection.clear(); + widget_collect_visible(top_widget, find_back_fn, collection); + if (!collection.empty()) + set_focus_widget(collection[0].first); + } + } + } } } /* namespace ui */ diff --git a/firmware/common/ui_focus.hpp b/firmware/common/ui_focus.hpp index af742095e..2f9c45650 100644 --- a/firmware/common/ui_focus.hpp +++ b/firmware/common/ui_focus.hpp @@ -29,17 +29,17 @@ namespace ui { class Widget; class FocusManager { -public: - Widget* focus_widget() const; - void set_focus_widget(Widget* const new_focus_widget); + public: + Widget* focus_widget() const; + void set_focus_widget(Widget* const new_focus_widget); - void update(Widget* const top_widget, const KeyEvent event); - //void update(Widget* const top_widget, const TouchEvent event); + void update(Widget* const top_widget, const KeyEvent event); + // void update(Widget* const top_widget, const TouchEvent event); -private: - Widget* focus_widget_ { nullptr }; + private: + Widget* focus_widget_{nullptr}; }; } /* namespace ui */ -#endif/*__UI_FOCUS_H__*/ +#endif /*__UI_FOCUS_H__*/ diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index b390668bc..fa3d7da69 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -29,109 +29,106 @@ using namespace portapack; namespace ui { Style Style::invert() const { - return { - .font = font, - .background = foreground, - .foreground = background - }; + return { + .font = font, + .background = foreground, + .foreground = background}; } int Painter::draw_char(const Point p, const Style& style, const char c) { - const auto glyph = style.font.glyph(c); - display.draw_glyph(p, glyph, style.foreground, style.background); - return glyph.advance().x(); + const auto glyph = style.font.glyph(c); + display.draw_glyph(p, glyph, style.foreground, style.background); + return glyph.advance().x(); } -int Painter::draw_string(Point p, const Font& font, const Color foreground, - const Color background, const std::string& text) { - - bool escape = false; - size_t width = 0; - Color pen = foreground; - - for(const auto c : text) { - if (escape) { - if (c <= 15) - pen = term_colors[c & 15]; - else - pen = foreground; - escape = false; - } else { - if (c == '\x1B') { - escape = true; - } else { - const auto glyph = font.glyph(c); - display.draw_glyph(p, glyph, pen, background); - const auto advance = glyph.advance(); - p += advance; - width += advance.x(); - } - } - } - return width; +int Painter::draw_string(Point p, const Font& font, const Color foreground, const Color background, const std::string& text) { + bool escape = false; + size_t width = 0; + Color pen = foreground; + + for (const auto c : text) { + if (escape) { + if (c <= 15) + pen = term_colors[c & 15]; + else + pen = foreground; + escape = false; + } else { + if (c == '\x1B') { + escape = true; + } else { + const auto glyph = font.glyph(c); + display.draw_glyph(p, glyph, pen, background); + const auto advance = glyph.advance(); + p += advance; + width += advance.x(); + } + } + } + return width; } int Painter::draw_string(Point p, const Style& style, const std::string& text) { - return draw_string(p, style.font, style.foreground, style.background, text); + return draw_string(p, style.font, style.foreground, style.background, text); } void Painter::draw_bitmap(const Point p, const Bitmap& bitmap, const Color foreground, const Color background) { - display.draw_bitmap(p, bitmap.size, bitmap.data, foreground, background); + display.draw_bitmap(p, bitmap.size, bitmap.data, foreground, background); } void Painter::draw_hline(Point p, int width, const Color c) { - display.fill_rectangle({ p, { width, 1 } }, c); + display.fill_rectangle({p, {width, 1}}, c); } void Painter::draw_vline(Point p, int height, const Color c) { - display.fill_rectangle({ p, { 1, height } }, c); + display.fill_rectangle({p, {1, height}}, c); } void Painter::draw_rectangle(const Rect r, const Color c) { - draw_hline(r.location(), r.width(), c); - draw_vline({ r.left(), r.top() + 1 }, r.height() - 2, c); - draw_vline({ r.left() + r.width() - 1, r.top() + 1 }, r.height() - 2, c); - draw_hline({ r.left(), r.top() + r.height() - 1 }, r.width(), c); + draw_hline(r.location(), r.width(), c); + draw_vline({r.left(), r.top() + 1}, r.height() - 2, c); + draw_vline({r.left() + r.width() - 1, r.top() + 1}, r.height() - 2, c); + draw_hline({r.left(), r.top() + r.height() - 1}, r.width(), c); } void Painter::fill_rectangle(const Rect r, const Color c) { - display.fill_rectangle(r, c); + display.fill_rectangle(r, c); } void Painter::fill_rectangle_unrolled8(const Rect r, const Color c) { - display.fill_rectangle_unrolled8(r, c); + display.fill_rectangle_unrolled8(r, c); } void Painter::paint_widget_tree(Widget* const w) { - if( ui::is_dirty() ) { - paint_widget(w); - ui::dirty_clear(); - } + if (ui::is_dirty()) { + paint_widget(w); + ui::dirty_clear(); + } } void Painter::paint_widget(Widget* const w) { - if( w->hidden() ) { - // Mark widget (and all children) as invisible. - w->visible(false); - } else { - // Mark this widget as visible and recurse. - w->visible(true); - - if( w->dirty() ) { - w->paint(*this); - // Force-paint all children. - for(const auto child : w->children()) { - child->set_dirty(); - paint_widget(child); - } - w->set_clean(); - } else { - // Selectively paint all children. - for(const auto child : w->children()) { - paint_widget(child); - } - } - } + if (w->hidden()) { + // Mark widget (and all children) as invisible. + w->visible(false); + } else { + // Mark this widget as visible and recurse. + w->visible(true); + + if (w->dirty()) { + w->paint(*this); + // Force-paint all children. + for (const auto child : w->children()) { + child->set_dirty(); + paint_widget(child); + } + w->set_clean(); + } else { + // Selectively paint all children. + for (const auto child : w->children()) { + paint_widget(child); + } + } + } } } /* namespace ui */ diff --git a/firmware/common/ui_painter.hpp b/firmware/common/ui_painter.hpp index 0fb867c7d..24f7835e8 100644 --- a/firmware/common/ui_painter.hpp +++ b/firmware/common/ui_painter.hpp @@ -30,43 +30,42 @@ namespace ui { struct Style { - const Font& font; - const Color background; - const Color foreground; + const Font& font; + const Color background; + const Color foreground; - Style invert() const; + Style invert() const; }; class Widget; class Painter { -public: - Painter() { }; + public: + Painter(){}; - Painter(const Painter&) = delete; - Painter(Painter&&) = delete; + Painter(const Painter&) = delete; + Painter(Painter&&) = delete; - int draw_char(const Point p, const Style& style, const char c); + int draw_char(const Point p, const Style& style, const char c); - int draw_string(Point p, const Font& font, const Color foreground, - const Color background, const std::string& text); - int draw_string(Point p, const Style& style, const std::string& text); + int draw_string(Point p, const Font& font, const Color foreground, const Color background, const std::string& text); + int draw_string(Point p, const Style& style, const std::string& text); - void draw_bitmap(const Point p, const Bitmap& bitmap, const Color background, const Color foreground); + void draw_bitmap(const Point p, const Bitmap& bitmap, const Color background, const Color foreground); - void draw_rectangle(const Rect r, const Color c); - void fill_rectangle(const Rect r, const Color c); - void fill_rectangle_unrolled8(const Rect r, const Color c); + void draw_rectangle(const Rect r, const Color c); + void fill_rectangle(const Rect r, const Color c); + void fill_rectangle_unrolled8(const Rect r, const Color c); - void paint_widget_tree(Widget* const w); - - void draw_hline(Point p, int width, const Color c); - void draw_vline(Point p, int height, const Color c); - -private: - void paint_widget(Widget* const w); + void paint_widget_tree(Widget* const w); + + void draw_hline(Point p, int width, const Color c); + void draw_vline(Point p, int height, const Color c); + + private: + void paint_widget(Widget* const w); }; } /* namespace ui */ -#endif/*__UI_PAINTER_H__*/ +#endif /*__UI_PAINTER_H__*/ diff --git a/firmware/common/ui_text.cpp b/firmware/common/ui_text.cpp index e926ba0d4..7f7b1b4e6 100644 --- a/firmware/common/ui_text.cpp +++ b/firmware/common/ui_text.cpp @@ -24,33 +24,32 @@ namespace ui { Glyph Font::glyph(const char c) const { - if( c < c_start ) { - return { w, h, data }; - } - const size_t index = c - c_start; - if( index >= c_count ) { - return { w, h, data }; - } else { - return { w, h, &data[index * data_stride] }; - } + if (c < c_start) { + return {w, h, data}; + } + const size_t index = c - c_start; + if (index >= c_count) { + return {w, h, data}; + } else { + return {w, h, &data[index * data_stride]}; + } } Dim Font::line_height() const { - return h; + return h; } Size Font::size_of(const std::string s) const { - Size size; + Size size; - for(const auto c : s) { - const auto glyph_data = glyph(c); - size = { - size.width() + glyph_data.w(), - std::max(size.height(), glyph_data.h()) - }; - } + for (const auto c : s) { + const auto glyph_data = glyph(c); + size = { + size.width() + glyph_data.w(), + std::max(size.height(), glyph_data.h())}; + } - return size; + return size; } } /* namespace ui */ diff --git a/firmware/common/ui_text.hpp b/firmware/common/ui_text.hpp index d60c746ff..45a9f6ccf 100644 --- a/firmware/common/ui_text.hpp +++ b/firmware/common/ui_text.hpp @@ -31,74 +31,72 @@ namespace ui { class Glyph { -public: - constexpr Glyph( - Dim w, - Dim h, - const uint8_t* const pixels - ) : w_ { static_cast(w) }, - h_ { static_cast(h) }, - pixels_ { pixels } - { - } - - int w() const { - return w_; - } - - int h() const { - return h_; - } - - Size size() const { - return { w_, h_ }; - } - - Point advance() const { - return { w_, 0 }; - } - - const uint8_t* pixels() const { - return pixels_; - } - -private: - const uint8_t w_; - const uint8_t h_; - const uint8_t* const pixels_; + public: + constexpr Glyph( + Dim w, + Dim h, + const uint8_t* const pixels) + : w_{static_cast(w)}, + h_{static_cast(h)}, + pixels_{pixels} { + } + + int w() const { + return w_; + } + + int h() const { + return h_; + } + + Size size() const { + return {w_, h_}; + } + + Point advance() const { + return {w_, 0}; + } + + const uint8_t* pixels() const { + return pixels_; + } + + private: + const uint8_t w_; + const uint8_t h_; + const uint8_t* const pixels_; }; class Font { -public: - constexpr Font( - Dim w, - Dim h, - const uint8_t* data, - char c_start, - size_t c_count - ) : w { w }, - h { h }, - data { data }, - c_start { c_start }, - c_count { c_count }, - data_stride { (w * h + 7U) >> 3 } - { - } - - Glyph glyph(const char c) const; - - Dim line_height() const; - Size size_of(const std::string s) const; - -private: - const Dim w; - const Dim h; - const uint8_t* const data; - const char c_start; - const size_t c_count; - const size_t data_stride; + public: + constexpr Font( + Dim w, + Dim h, + const uint8_t* data, + char c_start, + size_t c_count) + : w{w}, + h{h}, + data{data}, + c_start{c_start}, + c_count{c_count}, + data_stride{(w * h + 7U) >> 3} { + } + + Glyph glyph(const char c) const; + + Dim line_height() const; + Size size_of(const std::string s) const; + + private: + const Dim w; + const Dim h; + const uint8_t* const data; + const char c_start; + const size_t c_count; + const size_t data_stride; }; } /* namespace ui */ -#endif/*__UI_TEXT_H__*/ +#endif /*__UI_TEXT_H__*/ diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 413ee37bc..b707daa92 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -37,920 +37,889 @@ namespace ui { static bool ui_dirty = true; void dirty_set() { - ui_dirty = true; + ui_dirty = true; } void dirty_clear() { - ui_dirty = false; + ui_dirty = false; } bool is_dirty() { - return ui_dirty; + return ui_dirty; } /* Widget ****************************************************************/ -const std::vector Widget::no_children { }; +const std::vector Widget::no_children{}; Point Widget::screen_pos() { - return screen_rect().location(); + return screen_rect().location(); } Size Widget::size() const { - return _parent_rect.size(); + return _parent_rect.size(); } Rect Widget::screen_rect() const { - return parent() ? (parent_rect() + parent()->screen_pos()) : parent_rect(); + return parent() ? (parent_rect() + parent()->screen_pos()) : parent_rect(); } Rect Widget::parent_rect() const { - return _parent_rect; + return _parent_rect; } void Widget::set_parent_rect(const Rect new_parent_rect) { - _parent_rect = new_parent_rect; - set_dirty(); + _parent_rect = new_parent_rect; + set_dirty(); } Widget* Widget::parent() const { - return parent_; + return parent_; } void Widget::set_parent(Widget* const widget) { - if( widget == parent_ ) { - return; - } + if (widget == parent_) { + return; + } - if( parent_ && !widget ) { - // We have a parent, but are losing it. Update visible status. - dirty_overlapping_children_in_rect(screen_rect()); - visible(false); - } + if (parent_ && !widget) { + // We have a parent, but are losing it. Update visible status. + dirty_overlapping_children_in_rect(screen_rect()); + visible(false); + } - parent_ = widget; + parent_ = widget; - set_dirty(); + set_dirty(); } void Widget::set_dirty() { - flags.dirty = true; - dirty_set(); + flags.dirty = true; + dirty_set(); } bool Widget::dirty() const { - return flags.dirty; + return flags.dirty; } void Widget::set_clean() { - flags.dirty = false; + flags.dirty = false; } void Widget::hidden(bool hide) { - if( hide != flags.hidden ) { - flags.hidden = hide; - - // If parent is hidden, either of these is a no-op. - if( hide ) { - // TODO: Instead of dirtying parent entirely, dirty only children - // that overlap with this widget. - - //parent()->dirty_overlapping_children_in_rect(parent_rect()); - - /* TODO: Notify self and all non-hidden children that they're - * now effectively hidden? - */ - } else { - set_dirty(); - /* TODO: Notify self and all non-hidden children that they're - * now effectively shown? - */ - } - } + if (hide != flags.hidden) { + flags.hidden = hide; + + // If parent is hidden, either of these is a no-op. + if (hide) { + // TODO: Instead of dirtying parent entirely, dirty only children + // that overlap with this widget. + + // parent()->dirty_overlapping_children_in_rect(parent_rect()); + + /* TODO: Notify self and all non-hidden children that they're + * now effectively hidden? + */ + } else { + set_dirty(); + /* TODO: Notify self and all non-hidden children that they're + * now effectively shown? + */ + } + } } void Widget::focus() { - context().focus_manager().set_focus_widget(this); + context().focus_manager().set_focus_widget(this); } void Widget::on_focus() { - //set_dirty(); + // set_dirty(); } void Widget::blur() { - context().focus_manager().set_focus_widget(nullptr); + context().focus_manager().set_focus_widget(nullptr); } void Widget::on_blur() { - //set_dirty(); + // set_dirty(); } bool Widget::focusable() const { - return flags.focusable; + return flags.focusable; } void Widget::set_focusable(const bool value) { - flags.focusable = value; + flags.focusable = value; } bool Widget::has_focus() { - return (context().focus_manager().focus_widget() == this); + return (context().focus_manager().focus_widget() == this); } bool Widget::on_key(const KeyEvent event) { - (void)event; - return false; + (void)event; + return false; } bool Widget::on_encoder(const EncoderEvent event) { - (void)event; - return false; + (void)event; + return false; } bool Widget::on_touch(const TouchEvent event) { - (void)event; - return false; + (void)event; + return false; } const std::vector& Widget::children() const { - return no_children; + return no_children; } Context& Widget::context() const { - return parent()->context(); + return parent()->context(); } void Widget::set_style(const Style* new_style) { - if( new_style != style_ ) { - style_ = new_style; - set_dirty(); - } + if (new_style != style_) { + style_ = new_style; + set_dirty(); + } } const Style& Widget::style() const { - return style_ ? *style_ : parent()->style(); + return style_ ? *style_ : parent()->style(); } void Widget::visible(bool v) { - if( v != flags.visible ) { - flags.visible = v; - - /* TODO: This on_show/on_hide implementation seems inelegant. - * But I need *some* way to take/configure resources when - * a widget becomes visible, and reverse the process when the - * widget becomes invisible, whether the widget (or parent) is - * hidden, or the widget (or parent) is removed from the tree. - */ - if( v ) { - on_show(); - } else { - on_hide(); - - // Set all children invisible too. - for(const auto child : children()) { - child->visible(false); - } - } - } + if (v != flags.visible) { + flags.visible = v; + + /* TODO: This on_show/on_hide implementation seems inelegant. + * But I need *some* way to take/configure resources when + * a widget becomes visible, and reverse the process when the + * widget becomes invisible, whether the widget (or parent) is + * hidden, or the widget (or parent) is removed from the tree. + */ + if (v) { + on_show(); + } else { + on_hide(); + + // Set all children invisible too. + for (const auto child : children()) { + child->visible(false); + } + } + } } bool Widget::highlighted() const { - return flags.highlighted; + return flags.highlighted; } void Widget::set_highlighted(const bool value) { - flags.highlighted = value; + flags.highlighted = value; } void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) { - for(auto child : children()) { - if( !child_rect.intersect(child->parent_rect()).is_empty() ) { - child->set_dirty(); - } - } + for (auto child : children()) { + if (!child_rect.intersect(child->parent_rect()).is_empty()) { + child->set_dirty(); + } + } } /* View ******************************************************************/ void View::paint(Painter& painter) { - painter.fill_rectangle( - screen_rect(), - style().background - ); + painter.fill_rectangle( + screen_rect(), + style().background); } void View::add_child(Widget* const widget) { - if( widget ) { - if( widget->parent() == nullptr ) { - widget->set_parent(this); - children_.push_back(widget); - } - } + if (widget) { + if (widget->parent() == nullptr) { + widget->set_parent(this); + children_.push_back(widget); + } + } } void View::add_children(const std::initializer_list children) { - children_.insert(std::end(children_), children); - for(auto child : children) { - child->set_parent(this); - } + children_.insert(std::end(children_), children); + for (auto child : children) { + child->set_parent(this); + } } void View::remove_child(Widget* const widget) { - if( widget ) { - children_.erase(std::remove(children_.begin(), children_.end(), widget), children_.end()); - widget->set_parent(nullptr); - } + if (widget) { + children_.erase(std::remove(children_.begin(), children_.end(), widget), children_.end()); + widget->set_parent(nullptr); + } } void View::remove_children(const std::vector& children) { - for(auto child : children) { - remove_child(child); - } + for (auto child : children) { + remove_child(child); + } } const std::vector& View::children() const { - return children_; + return children_; } std::string View::title() const { - return ""; + return ""; }; /* OptionTabView *********************************************************/ OptionTabView::OptionTabView(Rect parent_rect) { - set_parent_rect(parent_rect); - - add_child(&check_enable); - hidden(true); - - check_enable.on_select = [this](Checkbox&, bool value) { - enabled = value; - }; + set_parent_rect(parent_rect); + + add_child(&check_enable); + hidden(true); + + check_enable.on_select = [this](Checkbox&, bool value) { + enabled = value; + }; } void OptionTabView::set_enabled(bool value) { - check_enable.set_value(value); + check_enable.set_value(value); } bool OptionTabView::is_enabled() { - return check_enable.value(); + return check_enable.value(); } void OptionTabView::set_type(std::string type) { - check_enable.set_text("Transmit " + type); + check_enable.set_text("Transmit " + type); } void OptionTabView::focus() { - check_enable.focus(); + check_enable.focus(); } /* Rectangle *************************************************************/ Rectangle::Rectangle( - Color c -) : Widget { }, - color { c } -{ + Color c) + : Widget{}, + color{c} { } Rectangle::Rectangle( - Rect parent_rect, - Color c -) : Widget { parent_rect }, - color { c } -{ + Rect parent_rect, + Color c) + : Widget{parent_rect}, + color{c} { } void Rectangle::set_color(const Color c) { - color = c; - set_dirty(); + color = c; + set_dirty(); } void Rectangle::set_outline(const bool outline) { - _outline = outline; - set_dirty(); + _outline = outline; + set_dirty(); } void Rectangle::paint(Painter& painter) { - if (!_outline) { - painter.fill_rectangle( - screen_rect(), - color - ); - } else { - painter.draw_rectangle( - screen_rect(), - color - ); - } + if (!_outline) { + painter.fill_rectangle( + screen_rect(), + color); + } else { + painter.draw_rectangle( + screen_rect(), + color); + } } /* Text ******************************************************************/ Text::Text( - Rect parent_rect, - std::string text -) : Widget { parent_rect }, - text { text } -{ + Rect parent_rect, + std::string text) + : Widget{parent_rect}, + text{text} { } Text::Text( - Rect parent_rect -) : Text { parent_rect, { } } -{ + Rect parent_rect) + : Text{parent_rect, {}} { } void Text::set(const std::string value) { - text = value; - set_dirty(); + text = value; + set_dirty(); } void Text::paint(Painter& painter) { - const auto rect = screen_rect(); - const auto s = style(); + const auto rect = screen_rect(); + const auto s = style(); - painter.fill_rectangle(rect, s.background); + painter.fill_rectangle(rect, s.background); - painter.draw_string( - rect.location(), - s, - text - ); + painter.draw_string( + rect.location(), + s, + text); } /* Labels ****************************************************************/ Labels::Labels( - std::initializer_list