Add some features to support RX-only mode (#2904)

* Remove duplicate LED control code
* Add TX limit settings and UI
* Fix App Manager to handle non-application menu items
This commit is contained in:
Harin Lee
2026-01-04 06:10:34 +09:00
committed by GitHub
parent 70ac1a03a1
commit ee97b51eb3
8 changed files with 144 additions and 43 deletions
+31
View File
@@ -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<SetFrequencyCorrectionView>(); }},
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<SetPersistentMemoryView>(); }},
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetRadioView>(); }},
{"TX Limit", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetTXLimitView>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
+40
View File
@@ -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 {
+38 -25
View File
@@ -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<std::string> 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<std::string> 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([&current_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++;
});
+10 -11
View File
@@ -42,6 +42,8 @@ using namespace hackrf::one;
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include <algorithm>
/* 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<int8_t>(db), portapack::persistent_memory::config_tx_gain_max_db()));
}
void set_baseband_filter_bandwidth_rx(const uint32_t bandwidth_minimum) {
-3
View File
@@ -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() {
@@ -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() {
@@ -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;
}
@@ -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();