mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-21 06:59:02 +00:00
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.
This commit is contained in:
+101
-8
@@ -4,7 +4,6 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "memory_map.hpp"
|
||||
#include "usb_serial_asyncmsg.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -12,26 +11,41 @@ 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,
|
||||
&console});
|
||||
&menu_view});
|
||||
|
||||
field_frequency.set_value(receiver_model.target_frequency());
|
||||
// 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() {
|
||||
@@ -43,32 +57,111 @@ 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 = "";
|
||||
|
||||
text += "FLEX ";
|
||||
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;
|
||||
|
||||
console.writeln(text);
|
||||
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);
|
||||
console.writeln(text);
|
||||
log_message(text);
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::flex_rx
|
||||
+50
-22
@@ -6,14 +6,12 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "freqman_db.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace ui::external_app::flex_rx {
|
||||
|
||||
@@ -27,37 +25,70 @@ class FlexAppView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
929612500 /* frequency - common pager freq in some regions, or 931M */,
|
||||
max283x::filter::bandwidth_minimum /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
|
||||
// UI Elements - Row 0
|
||||
// Saved settings
|
||||
rf::Frequency frequency_value{931740000}; // Default FLEX frequency
|
||||
uint32_t current_color_index{0}; // Current text color selection
|
||||
|
||||
// Available text colors for message display
|
||||
static constexpr std::array<Color, 7> text_colors = {{Color::green(),
|
||||
Color::white(),
|
||||
Color::cyan(),
|
||||
Color::magenta(),
|
||||
Color::yellow(),
|
||||
Color::blue(),
|
||||
Color::red()}};
|
||||
|
||||
RxRadioState radio_state_{};
|
||||
|
||||
// Message log settings
|
||||
static constexpr size_t MAX_LOG_LINES = 32; // Limit to prevent memory issues
|
||||
std::vector<std::string> log_messages{}; // Stored log lines
|
||||
|
||||
// Helper methods
|
||||
void log_message(const std::string& message); // Add message with word wrap
|
||||
void rebuild_menu(); // Rebuild menu after color change or overflow
|
||||
void update_freq(rf::Frequency f); // Update tuned frequency
|
||||
void cycle_color(); // Cycle through text colors
|
||||
|
||||
// UI Elements - Row 0, dynamically positioned
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
{UI_POS_X(0), UI_POS_Y(0)},
|
||||
nav_};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{13 * 8, 0 * 16}};
|
||||
{UI_POS_X(13), UI_POS_Y(0)}};
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, 0 * 16}};
|
||||
{UI_POS_X(15), UI_POS_Y(0)}};
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
{UI_POS_X(18), UI_POS_Y(0)}};
|
||||
|
||||
// Color cycle button
|
||||
Button button_color{
|
||||
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)},
|
||||
"COLOR"};
|
||||
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
{UI_POS_X(26), 0, UI_POS_WIDTH(4), 4}};
|
||||
|
||||
// Console - starts at row 1, extends to bottom of screen
|
||||
Console console{
|
||||
{0, 1 * 16, screen_width, screen_height - 1 * 16}};
|
||||
// Message display area - scrollable menu view
|
||||
MenuView menu_view{
|
||||
{0, 1 * 16, screen_width, screen_height - 1 * 16},
|
||||
true};
|
||||
|
||||
// Logic
|
||||
// Persistent settings manager
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_flex",
|
||||
app_settings::Mode::RX,
|
||||
{{"frequency", &frequency_value},
|
||||
{"color_index", ¤t_color_index}}};
|
||||
|
||||
// Message handlers
|
||||
void on_packet(const FlexPacketMessage* message);
|
||||
void on_stats(const FlexStatsMessage* message);
|
||||
void on_debug(const FlexDebugMessage* message);
|
||||
|
||||
// Message Handlers
|
||||
// Message handler registrations
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::FlexPacket,
|
||||
[this](const Message* const p) {
|
||||
@@ -78,9 +109,6 @@ class FlexAppView : public View {
|
||||
const auto message = *static_cast<const FlexDebugMessage*>(p);
|
||||
this->on_debug(&message);
|
||||
}};
|
||||
|
||||
// Config
|
||||
void update_freq(rf::Frequency f);
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::flex_rx
|
||||
|
||||
Reference in New Issue
Block a user