mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-16 21:03:24 +00:00
472571b1ed
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.
167 lines
4.7 KiB
C++
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
|