diff --git a/firmware/application/external/rf3d/ui_rf3d.cpp b/firmware/application/external/rf3d/ui_rf3d.cpp index a771ba82f..86056bc54 100644 --- a/firmware/application/external/rf3d/ui_rf3d.cpp +++ b/firmware/application/external/rf3d/ui_rf3d.cpp @@ -11,8 +11,8 @@ using namespace portapack; namespace ui::external_app::rf3d { RF3DView::RF3DView(NavigationView& nav) - : nav_{nav}, spectrum_data(SCREEN_WIDTH, std::vector(MAX_RENDER_DEPTH, 0)) { - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + : nav_{nav}, bar_heights(NUM_BARS, 0), prev_bar_heights(NUM_BARS, 0) { + baseband::run_image(spi_flash::image_tag_wfm_audio); add_children({&rssi, &channel, &audio, &field_frequency, &field_lna, &field_vga, &options_modulation, &field_volume, &text_ctcss, &record_view, &dummy}); @@ -24,10 +24,11 @@ RF3DView::RF3DView(NavigationView& nav) receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(40000); receiver_model.set_target_frequency(93100000); + receiver_model.set_rf_amp(true); receiver_model.enable(); - field_lna.set_value(32); - field_vga.set_value(40); + field_lna.set_value(40); + field_vga.set_value(62); options_modulation.set_by_value(toUType(ReceiverModel::Mode::WidebandFMAudio)); options_modulation.on_change = [this](size_t, OptionsField::value_t v) { @@ -58,72 +59,54 @@ void RF3DView::focus() { void RF3DView::start() { if (!running) { - baseband::spectrum_streaming_start(); running = true; } } void RF3DView::stop() { if (running) { - baseband::spectrum_streaming_stop(); running = false; } } -void RF3DView::update_spectrum(const ChannelSpectrum& spectrum) { - for (int x = 0; x < SCREEN_WIDTH; x++) { - int index = x * 256 / SCREEN_WIDTH; - uint8_t new_value = spectrum.db[index]; - if (new_value > 50) { - spectrum_data[x][0] = new_value; - } else { - spectrum_data[x][0] = 0; +void RF3DView::update_spectrum(const AudioSpectrum& spectrum) { + const int bins_per_bar = 128 / NUM_BARS; // Simple linear grouping + for (int bar = 0; bar < NUM_BARS; bar++) { + int start_bin = bar * bins_per_bar; + uint8_t max_db = 0; + for (int bin = start_bin; bin < start_bin + bins_per_bar; bin++) { + if (spectrum.db[bin] > max_db) { + max_db = spectrum.db[bin]; + } } + int height = (max_db * RENDER_HEIGHT) / 255; + bar_heights[bar] = height; } - sampling_rate = spectrum.sampling_rate; } -void RF3DView::render_3d_waterfall(Painter& painter) { - static bool background_drawn = false; - static std::vector prev_segments(SCREEN_WIDTH / (12 + 2), 0); - - if (!background_drawn) { - painter.fill_rectangle({0, header_height, SCREEN_WIDTH, RENDER_HEIGHT}, Color(20, 0, 40)); - for (int x = 0; x < SCREEN_WIDTH; x += 20) { - painter.fill_rectangle({x, header_height, 1, RENDER_HEIGHT}, Color(80, 0, 160)); - } - for (int y = header_height; y < SCREEN_HEIGHT; y += 20) { - painter.fill_rectangle({0, y, SCREEN_WIDTH, 1}, Color(80, 0, 160)); - } - background_drawn = true; - } - - const int bar_width = 12; - const int bar_spacing = 2; - const int num_bars = SCREEN_WIDTH / (bar_width + bar_spacing); - const int segment_height = 10; - const int num_segments = RENDER_HEIGHT / segment_height; +void RF3DView::render_equalizer(Painter& painter) { + const int num_bars = SCREEN_WIDTH / (BAR_WIDTH + BAR_SPACING); + const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT; for (int bar = 0; bar < num_bars; bar++) { - int x = bar * (bar_width + bar_spacing); - uint8_t intensity = spectrum_data[bar * SCREEN_WIDTH / num_bars][0]; - int active_segments = (intensity * num_segments) / 255; + int x = bar * (BAR_WIDTH + BAR_SPACING); + int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT; - if (prev_segments[bar] > active_segments) { - int clear_height = (prev_segments[bar] - active_segments) * segment_height; - int clear_y = SCREEN_HEIGHT - prev_segments[bar] * segment_height; - painter.fill_rectangle({x, clear_y, bar_width, clear_height}, Color(20, 0, 40)); + if (prev_bar_heights[bar] > active_segments) { + int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT; + int clear_y = SCREEN_HEIGHT - prev_bar_heights[bar] * SEGMENT_HEIGHT; + painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Color(0, 0, 0)); } for (int seg = 0; seg < active_segments; seg++) { - int y = SCREEN_HEIGHT - (seg + 1) * segment_height; + int y = SCREEN_HEIGHT - (seg + 1) * SEGMENT_HEIGHT; if (y < header_height) break; Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? Color(255, 255, 255) : Color(255, 0, 255); - painter.fill_rectangle({x, y, bar_width, segment_height - 1}, segment_color); + painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color); } - prev_segments[bar] = active_segments; + prev_bar_heights[bar] = active_segments; } } @@ -131,25 +114,16 @@ void RF3DView::paint(Painter& painter) { if (!initialized) { initialized = true; start(); + painter.fill_rectangle({0, header_height, SCREEN_WIDTH, RENDER_HEIGHT}, Color(0, 0, 0)); } - render_3d_waterfall(painter); -} - -void RF3DView::frame_sync() { - if (running && channel_fifo) { - ChannelSpectrum channel_spectrum; - while (channel_fifo->out(channel_spectrum)) { - update_spectrum(channel_spectrum); - } - set_dirty(); - } + render_equalizer(painter); } void RF3DView::on_modulation_changed(ReceiverModel::Mode modulation) { - baseband::spectrum_streaming_stop(); + stop(); update_modulation(modulation); on_show_options_modulation(); - baseband::spectrum_streaming_start(); + start(); } void RF3DView::on_show_options_frequency() { @@ -229,32 +203,33 @@ void RF3DView::update_modulation(ReceiverModel::Mode modulation) { record_view.stop(); baseband::shutdown(); - portapack::spi_flash::image_tag_t image_tag; + spi_flash::image_tag_t image_tag; switch (modulation) { case ReceiverModel::Mode::AMAudio: - image_tag = portapack::spi_flash::image_tag_am_audio; + image_tag = spi_flash::image_tag_am_audio; break; case ReceiverModel::Mode::NarrowbandFMAudio: - image_tag = portapack::spi_flash::image_tag_nfm_audio; + image_tag = spi_flash::image_tag_nfm_audio; break; case ReceiverModel::Mode::WidebandFMAudio: - image_tag = portapack::spi_flash::image_tag_wfm_audio; + image_tag = spi_flash::image_tag_wfm_audio; break; case ReceiverModel::Mode::SpectrumAnalysis: - image_tag = portapack::spi_flash::image_tag_wideband_spectrum; + image_tag = spi_flash::image_tag_wideband_spectrum; break; default: - image_tag = portapack::spi_flash::image_tag_wfm_audio; + image_tag = spi_flash::image_tag_wfm_audio; break; } baseband::run_image(image_tag); if (modulation == ReceiverModel::Mode::SpectrumAnalysis) { - baseband::set_spectrum(sampling_rate, 63); + baseband::set_spectrum(receiver_model.sampling_rate(), 63); } receiver_model.set_modulation(modulation); - receiver_model.set_sampling_rate(modulation == ReceiverModel::Mode::SpectrumAnalysis ? sampling_rate : 3072000); + receiver_model.set_sampling_rate(3072000); + receiver_model.set_rf_amp(true); receiver_model.enable(); size_t record_sampling_rate = 0; diff --git a/firmware/application/external/rf3d/ui_rf3d.hpp b/firmware/application/external/rf3d/ui_rf3d.hpp index 79ab1290c..7ddc697fa 100644 --- a/firmware/application/external/rf3d/ui_rf3d.hpp +++ b/firmware/application/external/rf3d/ui_rf3d.hpp @@ -8,6 +8,7 @@ #include "baseband_api.hpp" #include "portapack.hpp" #include "ui_record_view.hpp" +#include "ui_spectrum.hpp" namespace ui::external_app::rf3d { @@ -23,41 +24,40 @@ public: std::string title() const override { return "RF3D"; } void paint(Painter& painter) override; - void frame_sync(); private: static constexpr ui::Dim header_height = 3 * 16; static constexpr int SCREEN_WIDTH = 240; static constexpr int SCREEN_HEIGHT = 320; static constexpr int RENDER_HEIGHT = 280; - static constexpr int HALF_WIDTH = SCREEN_WIDTH / 2; - static constexpr int HALF_HEIGHT = RENDER_HEIGHT / 2; - static constexpr int MAX_RENDER_DEPTH = 12; + static constexpr int NUM_BARS = 16; + static constexpr int BAR_WIDTH = SCREEN_WIDTH / NUM_BARS; + static constexpr int BAR_SPACING = 2; + static constexpr int SEGMENT_HEIGHT = 10; NavigationView& nav_; bool initialized{false}; - std::vector> spectrum_data; - uint32_t sampling_rate{3072000}; - double angle{0.0}; + std::vector bar_heights; + std::vector prev_bar_heights; bool running{false}; RSSI rssi{{21 * 8, 0, 6 * 8, 4}}; Channel channel{{21 * 8, 5, 6 * 8, 4}}; Audio audio{{21 * 8, 10, 6 * 8, 4}}; - FrequencyField field_frequency{{5 * 8, 0 * 16}}; - LNAGainField field_lna{{15 * 8, 0 * 16}}; - VGAGainField field_vga{{18 * 8, 0 * 16}}; + FrequencyField field_frequency{Point{5 * 8, 0 * 16}}; + LNAGainField field_lna{Point{15 * 8, 0 * 16}}; + VGAGainField field_vga{Point{18 * 8, 0 * 16}}; OptionsField options_modulation{ {0 * 8, 0 * 16}, 4, { - {"AM", toUType(ReceiverModel::Mode::AMAudio)}, - {"NFM", toUType(ReceiverModel::Mode::NarrowbandFMAudio)}, - {"WFM", toUType(ReceiverModel::Mode::WidebandFMAudio)}, + {"AM ", toUType(ReceiverModel::Mode::AMAudio)}, + {"NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio)}, + {"WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, {"SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis)} } }; - AudioVolumeField field_volume{{28 * 8, 0 * 16}}; + AudioVolumeField field_volume{Point{28 * 8, 0 * 16}}; Text text_ctcss{{16 * 8, 1 * 16, 14 * 8, 1 * 16}, ""}; RecordView record_view{ {0 * 8, 2 * 16, 30 * 8, 1 * 16}, @@ -73,8 +73,8 @@ private: void start(); void stop(); - void update_spectrum(const ChannelSpectrum& spectrum); - void render_3d_waterfall(Painter& painter); + void update_spectrum(const AudioSpectrum& spectrum); + void render_equalizer(Painter& painter); void on_modulation_changed(ReceiverModel::Mode modulation); void on_show_options_frequency(); void on_show_options_rf_gain(); @@ -88,13 +88,14 @@ private: MessageHandlerRegistration message_handler_frame_sync{ Message::ID::DisplayFrameSync, - [this](const Message* const) { this->frame_sync(); } + [this](const Message* const) { } }; - MessageHandlerRegistration message_handler_channel_spectrum{ - Message::ID::ChannelSpectrumConfig, + MessageHandlerRegistration message_handler_audio_spectrum{ + Message::ID::AudioSpectrum, [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->channel_fifo = message.fifo; + const auto message = *reinterpret_cast(p); + this->update_spectrum(*message.data); + this->set_dirty(); } }; MessageHandlerRegistration message_handler_coded_squelch{ @@ -104,7 +105,6 @@ private: this->handle_coded_squelch(message.value); } }; - ChannelSpectrumFIFO* channel_fifo{nullptr}; }; } // namespace ui::external_app::rf3d