diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 7d77a0716..f697db0d0 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -309,6 +309,36 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() { }; } +/* SetTXLimitView ************************************/ + +SetTXLimitView::SetTXLimitView(NavigationView& nav) { + add_children({ + &labels, + &tx_gain_max_db, + &tx_amp_disable_switch, + &button_save, + &button_cancel, + }); + + tx_gain_max_db.set_value(pmem::config_tx_gain_max_db()); + tx_amp_disable_switch.set_value(pmem::config_tx_amp_disabled()); + + button_save.on_select = [&nav, this](Button&) { + pmem::set_config_tx_gain_max_db(tx_gain_max_db.value()); + pmem::set_config_tx_amp_disabled(tx_amp_disable_switch.value()); + send_system_refresh(); + nav.pop(); + }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetTXLimitView::focus() { + button_save.focus(); +} + /* SetUIView *********************************************/ SetUIView::SetUIView(NavigationView& nav) { @@ -1107,6 +1137,7 @@ void SettingsMenuView::on_populate() { {"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push(); }}, {"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push(); }}, {"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push(); }}, + {"TX Limit", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push(); }}, {"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push(); }}, {"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push(); }}, {"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push(); }}, diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index a2f5f8e05..a2f12307d 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -264,6 +264,46 @@ class SetRadioView : public View { SetFrequencyCorrectionModel form_collect(); }; +class SetTXLimitView : public View { + public: + SetTXLimitView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "TX Limit"; }; + + private: + Labels labels{ + {{1 * 8, 1 * 16}, "Limits RF TX Gain", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, 2 * 16}, "(This may affect", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, 3 * 16}, "all applications.)", Theme::getInstance()->fg_light->foreground}, + {{2 * 8, 8 * 16}, "TX Gain (Max):", Theme::getInstance()->fg_light->foreground}, + + }; + + NumberField tx_gain_max_db{ + {20 * 8, 8 * 16}, + 6, + {0, 47}, + 1, + ' ', + }; + + Checkbox tx_amp_disable_switch{ + {1 * 8, 12 * 16}, + 23, + "Disable TX Amp"}; + + Button button_save{ + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Save"}; + + Button button_cancel{ + {UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Cancel", + }; +}; + using portapack::persistent_memory::backlight_timeout_t; class SetUIView : public View { diff --git a/firmware/application/external/app_manager/ui_app_manager.cpp b/firmware/application/external/app_manager/ui_app_manager.cpp index f8ccf70fd..98107e5a9 100644 --- a/firmware/application/external/app_manager/ui_app_manager.cpp +++ b/firmware/application/external/app_manager/ui_app_manager.cpp @@ -62,19 +62,24 @@ AppManagerView::AppManagerView(NavigationView& nav) auto app_name = get_app_info(menu_view.highlighted_index(), true); auto app_id = get_app_info(menu_view.highlighted_index(), false); - info += "Hidden:"; + if (!app_name.empty()) { + info += "Hidden:"; - if (is_blacklisted(app_name)) { - info += "Yes "; - } else { - info += "No "; + if (is_blacklisted(app_name)) { + info += "Yes "; + } else { + info += "No "; + } } - info += "Autostart:"; - if (is_autostart_app(app_id)) { - info += "Yes"; - } else { - info += "No"; + if (!app_id.empty()) { + info += "Autostart:"; + + if (is_autostart_app(app_id)) { + info += "Yes"; + } else { + info += "No"; + } } if (info.empty()) { @@ -82,6 +87,8 @@ AppManagerView::AppManagerView(NavigationView& nav) } text_app_info.set(info); + button_hide_unhide.set_focusable(!app_name.empty()); + button_set_cancel_autostart.set_focusable(!app_id.empty()); }; button_hide_unhide.on_select = [this](Button&) { @@ -120,27 +127,23 @@ void AppManagerView::refresh_list() { }; for (auto& app : NavigationView::appList) { - if (app.id == nullptr) continue; - app_list_index++; - menu_view.add_item({app.displayName + std::string(is_autostart_app(app.id) ? padding(app.displayName) : ""), + menu_view.add_item({app.displayName + std::string((app.id != nullptr && is_autostart_app(app.id)) ? padding(app.displayName) : ""), app.iconColor, app.icon, - [this, app_id = std::string(app.id)](KeyEvent) { + [this, app_id = std::string(app.id != nullptr ? app.id : "")](KeyEvent) { button_hide_unhide.focus(); }}); } ExternalItemsMenuLoader::load_all_external_items_callback([this, &index, &padding](ui::AppInfoConsole& app) { - if (app.appCallName == nullptr) return; - app_list_index++; - menu_view.add_item({app.appFriendlyName + std::string(is_autostart_app(app.appCallName) ? padding(app.appFriendlyName) : ""), + menu_view.add_item({app.appFriendlyName + std::string((app.appCallName != nullptr && is_autostart_app(app.appCallName)) ? padding(app.appFriendlyName) : ""), ui::Color::light_grey(), &bitmap_icon_sdcard, - [this, app_id = std::string(app.appCallName)](KeyEvent) { + [this, app_id = std::string(app.appCallName != nullptr ? app.appCallName : "")](KeyEvent) { button_hide_unhide.focus(); }}); }); @@ -156,19 +159,29 @@ void AppManagerView::hide_app() { std::vector blacklist; get_blacklist(blacklist); - blacklist.push_back(get_app_info(menu_view.highlighted_index(), true)); + auto app_name = get_app_info(menu_view.highlighted_index(), true); + if (app_name.empty()) return; + + blacklist.push_back(app_name); write_blacklist(blacklist); } void AppManagerView::unhide_app() { std::vector blacklist; get_blacklist(blacklist); - blacklist.erase(std::remove(blacklist.begin(), blacklist.end(), get_app_info(menu_view.highlighted_index(), true)), blacklist.end()); + + auto app_name = get_app_info(menu_view.highlighted_index(), true); + if (app_name.empty()) return; + + blacklist.erase(std::remove(blacklist.begin(), blacklist.end(), app_name), blacklist.end()); write_blacklist(blacklist); } void AppManagerView::hide_unhide_app() { - if (is_blacklisted(get_app_info(menu_view.highlighted_index(), true))) + auto app_name = get_app_info(menu_view.highlighted_index(), true); + if (app_name.empty()) return; + + if (is_blacklisted(app_name)) unhide_app(); else hide_app(); @@ -218,6 +231,7 @@ void AppManagerView::set_auto_start() { if (app_index >= app_list_index) return; auto id_aka_app_call_name = get_app_info(app_index, false); + if (id_aka_app_call_name.empty()) return; autostart_app = id_aka_app_call_name; @@ -234,6 +248,7 @@ void AppManagerView::set_unset_autostart_app() { if (app_index >= app_list_index) return; auto id_aka_friendly_name = get_app_info(app_index, false); + if (id_aka_friendly_name.empty()) return; if (is_autostart_app(id_aka_friendly_name)) unset_auto_start(); @@ -254,17 +269,15 @@ std::string AppManagerView::get_app_info(uint16_t index, bool is_display_name) { std::string result; for (auto& app : NavigationView::appList) { - if (app.id == nullptr) continue; if (current_index == index) { - return is_display_name ? std::string(app.displayName) : std::string(app.id); + return is_display_name ? std::string(app.displayName != nullptr ? app.displayName : "") : std::string(app.id != nullptr ? app.id : ""); } current_index++; } ExternalItemsMenuLoader::load_all_external_items_callback([¤t_index, &index, &result, &is_display_name](ui::AppInfoConsole& app) { - if (app.appCallName == nullptr) return; if (current_index == index) { - result = is_display_name ? app.appFriendlyName : app.appCallName; + result = is_display_name ? (app.appFriendlyName != nullptr ? app.appFriendlyName : "") : (app.appCallName != nullptr ? app.appCallName : ""); } current_index++; }); diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 1e00c5cdb..782e23b20 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -42,6 +42,8 @@ using namespace hackrf::one; #include "portapack.hpp" #include "portapack_persistent_memory.hpp" +#include + /* Direct access to the radio. Setting values incorrectly can damage * the device. Applications should use ReceiverModel or TransmitterModel * instead of calling these functions directly. */ @@ -150,10 +152,14 @@ void set_direction(const rf::Direction new_direction) { baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive); - if (direction == rf::Direction::Receive) + if (direction == rf::Direction::Receive) { led_rx.on(); - else + } else { + if (portapack::persistent_memory::config_tx_amp_disabled()) + rf_path.set_rf_amp(false); + led_tx.on(); + } } bool set_tuning_frequency(const rf::Frequency frequency) { @@ -205,14 +211,7 @@ bool set_tuning_frequency(const rf::Frequency frequency) { } 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 && (direction != rf::Direction::Transmit || !portapack::persistent_memory::config_tx_amp_disabled())); } void set_lna_gain(const int_fast8_t db) { @@ -224,7 +223,7 @@ void set_vga_gain(const int_fast8_t db) { } void set_tx_gain(const int_fast8_t db) { - second_if->set_tx_vga_gain(db); + second_if->set_tx_vga_gain(std::min(static_cast(db), portapack::persistent_memory::config_tx_gain_max_db())); } void set_baseband_filter_bandwidth_rx(const uint32_t bandwidth_minimum) { diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index d08249271..8bc3b0695 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -252,8 +252,6 @@ void ReceiverModel::enable() { // TODO: maybe not the perfect place for this, but it's reasonable. update_headphone_volume(); - - led_rx.on(); } void ReceiverModel::disable() { @@ -262,7 +260,6 @@ void ReceiverModel::disable() { // TODO: Responsibility for enabling/disabling the radio is muddy. // Some happens in ReceiverModel, some inside radio namespace. radio::disable(); - led_rx.off(); } void ReceiverModel::initialize() { diff --git a/firmware/application/transmitter_model.cpp b/firmware/application/transmitter_model.cpp index e2cedb3cb..9706a9eda 100644 --- a/firmware/application/transmitter_model.cpp +++ b/firmware/application/transmitter_model.cpp @@ -101,7 +101,6 @@ void TransmitterModel::enable() { update_sampling_rate(); update_tx_gain(); - led_tx.on(); signal_token_tick_second = rtc_time::signal_tick_second += [this]() { this->on_tick_second(); }; @@ -118,7 +117,6 @@ void TransmitterModel::disable() { radio::disable(); rtc_time::signal_tick_second -= signal_token_tick_second; - led_tx.off(); } void TransmitterModel::initialize() { diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index 26365d09b..eef2706df 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -152,12 +152,12 @@ struct misc_config_t { bool config_sdcard_high_speed_io : 1; bool config_disable_config_mode : 1; bool beep_on_packets : 1; - bool UNUSED_6 : 1; + bool tx_amp_disabled : 1; bool UNUSED_7 : 1; + int8_t tx_gain_max_db; uint8_t PLACEHOLDER_1; uint8_t PLACEHOLDER_2; - uint8_t PLACEHOLDER_3; }; static_assert(sizeof(misc_config_t) == sizeof(uint32_t)); @@ -441,6 +441,9 @@ void defaults() { set_recon_repeat_delay(1); set_config_sdcard_high_speed_io(false, true); + + set_config_tx_amp_disabled(false); + set_config_tx_gain_max_db(47); } void init() { @@ -649,6 +652,14 @@ bool config_sdcard_high_speed_io() { return data->misc_config.config_sdcard_high_speed_io; } +bool config_tx_amp_disabled() { + return data->misc_config.tx_amp_disabled; +} + +int8_t config_tx_gain_max_db() { + return data->misc_config.tx_gain_max_db; +} + bool stealth_mode() { return data->ui_config.stealth_mode; } @@ -743,6 +754,14 @@ void set_config_sdcard_high_speed_io(bool v, bool save) { data->misc_config.config_sdcard_high_speed_io = v; } +void set_config_tx_amp_disabled(bool v) { + data->misc_config.tx_amp_disabled = v; +} + +void set_config_tx_gain_max_db(int8_t v) { + data->misc_config.tx_gain_max_db = v; +} + void set_stealth_mode(bool v) { data->ui_config.stealth_mode = v; } diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index 35810c384..b500552f5 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -203,6 +203,8 @@ bool config_disable_external_tcxo(); bool config_sdcard_high_speed_io(); bool config_disable_config_mode(); bool beep_on_packets(); +bool config_tx_amp_disabled(); +int8_t config_tx_gain_max_db(); bool config_splash(); bool config_converter(); @@ -226,6 +228,8 @@ void set_config_disable_external_tcxo(bool v); void set_config_sdcard_high_speed_io(bool v, bool save); void set_config_disable_config_mode(bool v); void set_beep_on_packets(bool v); +void set_config_tx_amp_disabled(bool v); +void set_config_tx_gain_max_db(int8_t v); void set_config_splash(bool v); bool config_converter();