Files
mayhem-firmware/firmware/application/external/flex_rx/ui_flex_rx.cpp
T
RocketGod 472571b1ed Refactor FLEX RX UI to add color cycling and message log (#2885)
Replaces the console with a scrollable menu view for displaying FLEX messages, adds support for cycling text colors, and implements message logging with line wrapping and persistence. Also refactors frequency handling for persistence and updates UI element layout and initialization.
2025-12-12 19:40:15 +01:00

167 lines
4.7 KiB
C++

#include "ui_flex_rx.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "string_format.hpp"
#include "memory_map.hpp"
using namespace portapack;
namespace ui::external_app::flex_rx {
FlexAppView::FlexAppView(NavigationView& nav)
: nav_{nav} {
// Load baseband image for FLEX decoding
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&field_frequency,
&field_rf_amp,
&field_lna,
&field_vga,
&button_color,
&rssi,
&menu_view});
// Restore saved frequency
field_frequency.set_value(frequency_value);
receiver_model.set_target_frequency(frequency_value);
// Frequency change callback
field_frequency.updated = [this](rf::Frequency f) {
update_freq(f);
};
// Color button cycles through available colors
button_color.on_select = [this](Button&) {
cycle_color();
};
// Configure receiver
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.enable();
receiver_model.set_squelch_level(0);
// Initialize FLEX baseband
baseband::set_flex_config();
log_message("FLEX RX Ready");
}
FlexAppView::~FlexAppView() {
receiver_model.disable();
baseband::shutdown();
}
void FlexAppView::focus() {
field_frequency.focus();
}
// Cycle to next text color and refresh display
void FlexAppView::cycle_color() {
current_color_index = (current_color_index + 1) % text_colors.size();
rebuild_menu();
}
// Rebuild entire menu with current color
void FlexAppView::rebuild_menu() {
menu_view.clear();
Color current_color = text_colors[current_color_index];
for (const auto& msg : log_messages) {
menu_view.add_item({msg,
current_color,
nullptr,
[](KeyEvent) {}});
}
if (menu_view.item_count() > 0) {
menu_view.set_highlighted(menu_view.item_count() - 1);
}
}
// Add message to log with automatic line wrapping
void FlexAppView::log_message(const std::string& message) {
// Calculate characters per line based on screen width (8 pixels per char)
const size_t chars_per_line = screen_width / 8;
Color current_color = text_colors[current_color_index];
std::string remaining = message;
bool first_line = true;
bool needs_rebuild = false;
size_t lines_added = 0;
// Split message into screen-width chunks
while (!remaining.empty()) {
std::string line;
if (remaining.length() <= chars_per_line) {
line = remaining;
remaining.clear();
} else {
line = remaining.substr(0, chars_per_line);
remaining = remaining.substr(chars_per_line);
}
// Indent continuation lines
if (!first_line) {
line = " " + line;
}
first_line = false;
// Remove oldest line if at limit
if (log_messages.size() >= MAX_LOG_LINES) {
log_messages.erase(log_messages.begin());
needs_rebuild = true;
}
log_messages.push_back(line);
lines_added++;
}
// Either rebuild all or just add new lines
if (needs_rebuild) {
rebuild_menu();
} else {
size_t start_idx = log_messages.size() - lines_added;
for (size_t i = start_idx; i < log_messages.size(); i++) {
menu_view.add_item({log_messages[i],
current_color,
nullptr,
[](KeyEvent) {}});
}
if (menu_view.item_count() > 0) {
menu_view.set_highlighted(menu_view.item_count() - 1);
}
}
}
// Update frequency and save for persistence
void FlexAppView::update_freq(rf::Frequency f) {
frequency_value = f;
receiver_model.set_target_frequency(f);
}
// Handle decoded FLEX packet from baseband
void FlexAppView::on_packet(const FlexPacketMessage* message) {
std::string text = "FLEX ";
text += to_string_dec_uint(message->packet.bitrate);
text += " ";
text += to_string_dec_uint(message->packet.capcode);
text += ": ";
text += message->packet.message;
log_message(text);
}
// Handle stats message (currently unused)
void FlexAppView::on_stats(const FlexStatsMessage* /* message */) {
}
// Handle debug message from baseband
void FlexAppView::on_debug(const FlexDebugMessage* message) {
std::string text = "DBG: ";
text += message->text;
text += " " + to_string_hex(message->val1, 8);
text += " " + to_string_hex(message->val2, 8);
log_message(text);
}
} // namespace ui::external_app::flex_rx