diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index a22e6829..114ee783 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -50,49 +50,71 @@ namespace ui receiver_model.set_vga(v_db); } + void GlassView::reset_live_view( bool clear_screen ) + { + max_freq_hold = 0 ; + max_freq_power = -1000 ; + if( clear_screen ) + { + // only clear screen in peak mode + if( live_frequency_view == 2 ) + { + display.fill_rectangle( { { 0 , 108 + 32 } , { 240 , 320 - (108 + 32) } } , { 0 , 0 , 0 } ); + } + } + } + void GlassView::add_spectrum_pixel(int16_t color) { + static uint64_t last_max_freq = 0 ; + spectrum_row[pixel_index] = spectrum_rgb3_lut[color] ; spectrum_data[pixel_index] = ( live_frequency_integrate * spectrum_data[pixel_index] + color ) / (live_frequency_integrate + 1); // smoothing pixel_index ++ ; if (pixel_index == 240) // got an entire waterfall line { - if( live_frequency_view > 0 ) - { - constexpr int rssi_sample_range = 256; - //constexpr float rssi_voltage_min = 0.4; - constexpr float rssi_voltage_max = 2.2; - constexpr float adc_voltage_max = 3.3; - //constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; - constexpr int raw_min = 0 ; - constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; - constexpr int raw_delta = raw_max - raw_min; - const range_t y_max_range { 0 , 320 - 108 }; + if( live_frequency_view > 0 ) + { + constexpr int rssi_sample_range = 256; + constexpr float rssi_voltage_min = 0.4; + constexpr float rssi_voltage_max = 2.2; + constexpr float adc_voltage_max = 3.3; + constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; + constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; + constexpr int raw_delta = raw_max - raw_min; + const range_t y_max_range { 0 , 320 - ( 108 + 16 ) }; - //drawing and keeping track of max freq - for( uint16_t xpos = 0 ; xpos < 240 ; xpos ++ ) - { - // save max powerwull freq - if( spectrum_data[ xpos ] > max_freq_power ) - { - max_freq_power = spectrum_data[ xpos ]; - max_freq_hold = ( f_center - LOOKING_GLASS_SLICE_WIDTH/2 ) + xpos * bins_Hz_size ; - } - int16_t point = y_max_range.clip( ( ( spectrum_data[ xpos ] - raw_min ) * ( 320 - 108 ) ) / raw_delta ); - uint8_t color_gradient = (point * 255) / 212 ; - // clear if not in peak view - if( live_frequency_view != 2 ) - { - display.fill_rectangle( { { xpos , 108 } , { 1 , 320 - point } } , { 0 , 0 , 0 } ); - } - display.fill_rectangle( { { xpos , 320 - point } , { 1 , point } } , { color_gradient , 0 , uint8_t( 255 - color_gradient ) } ); - } - } - else - { - display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier - } + //drawing and keeping track of max freq + for( uint16_t xpos = 0 ; xpos < 240 ; xpos ++ ) + { + // save max powerwull freq + if( spectrum_data[ xpos ] > max_freq_power ) + { + max_freq_power = spectrum_data[ xpos ]; + max_freq_hold = f_min + ( (f_max - f_min) * xpos) / 240 ; + } + + int16_t point = y_max_range.clip( ( ( spectrum_data[ xpos ] - raw_min ) * ( 320 - ( 108 + 16 ) ) ) / raw_delta ); + uint8_t color_gradient = (point * 255) / 212 ; + // clear if not in peak view + if( live_frequency_view != 2 ) + { + display.fill_rectangle( { { xpos , 108 + 16 } , { 1 , 320 - point } } , { 0 , 0 , 0 } ); + } + display.fill_rectangle( { { xpos , 320 - point } , { 1 , point } } , { color_gradient , 0 , uint8_t( 255 - color_gradient ) } ); + } + if( last_max_freq != max_freq_hold ) + { + last_max_freq = max_freq_hold ; + freq_stats.set( "MAX:"+to_string_short_freq( max_freq_hold ) ); + } + PlotMarker(field_marker.value()); + } + else + { + display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier + } pixel_index = 0; // Start New cascade line } } @@ -165,11 +187,7 @@ namespace ui void GlassView::on_range_changed() { - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( false ); f_min = field_frequency_min.value(); f_max = field_frequency_max.value(); search_span = f_max - f_min; @@ -209,11 +227,16 @@ namespace ui pos = pos * MHZ_DIV; pos -= f_min; pos = pos / marker_pixel_step; // Real pixel - - portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw - portapack::display.fill_rectangle({(int)pos - 2, 100, 5, 3}, Color::red()); // Red marker top - portapack::display.fill_rectangle({(int)pos - 1, 103, 3, 3}, Color::red()); // Red marker middle - portapack::display.fill_rectangle({(int)pos, 106, 1, 2}, Color::red()); // Red marker bottom + + uint8_t shift_y = 0 ; + if( live_frequency_view > 0 ) + { + shift_y = 16 ; + } + portapack::display.fill_rectangle({0, 100 + shift_y, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw + portapack::display.fill_rectangle({(int)pos - 2, 100 + shift_y, 5, 3}, Color::red()); // Red marker top + portapack::display.fill_rectangle({(int)pos - 1, 103 + shift_y, 3, 3}, Color::red()); // Red marker middle + portapack::display.fill_rectangle({(int)pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom } GlassView::GlassView( @@ -235,7 +258,10 @@ namespace ui &range_presets, &field_marker, &text_marker_pm, - &field_trigger}); + &field_trigger, + &button_jump, + &button_rst, + &freq_stats}); load_Presets(); // Load available presets from TXT files (or default) @@ -243,11 +269,7 @@ namespace ui field_frequency_min.set_step( steps ); field_frequency_min.on_change = [this](int32_t v) { - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); int32_t steps_ = steps ; if( steps_ < 24 ) steps_ = 24 ; @@ -281,11 +303,7 @@ namespace ui field_frequency_max.set_step( steps ); field_frequency_max.on_change = [this](int32_t v) { - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); int32_t steps_ = steps ; if( steps_ < 24 ) steps_ = 24 ; @@ -318,22 +336,14 @@ namespace ui field_lna.set_value(receiver_model.lna()); field_lna.on_change = [this](int32_t v) { - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); this->on_lna_changed(v); }; field_vga.set_value(receiver_model.vga()); field_vga.on_change = [this](int32_t v_db) { - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); this->on_vga_changed(v_db); }; @@ -349,39 +359,46 @@ namespace ui view_config.on_change = [this](size_t n, OptionsField::value_t v) { (void)n; - max_freq_hold = 0 ; + // clear between changes + reset_live_view( true ); if( v == 0 ) { live_frequency_view = 0 ; level_integration.hidden( true ); + freq_stats.hidden( true ); + button_jump.hidden( true ); + button_rst.hidden( true ); set_dirty(); display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates } else if( v == 1 ) { + display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } ); live_frequency_view = 1 ; display.scroll_disable(); level_integration.hidden( false ); + freq_stats.hidden( false ); + button_jump.hidden( false ); + button_rst.hidden( false ); } else if( v == 2 ) { + display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } ); live_frequency_view = 2 ; display.scroll_disable(); level_integration.hidden( false ); + freq_stats.hidden( false ); + button_jump.hidden( false ); + button_rst.hidden( false ); } - // clear between changes - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); + set_dirty(); }; view_config.set_selected_index(0); //default spectrum level_integration.on_change = [this](size_t n, OptionsField::value_t v) { (void)n; - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); live_frequency_integrate = v ; }; level_integration.set_selected_index(2); //default integration of ( 3 * old value + new_value ) / 4 @@ -389,11 +406,7 @@ namespace ui filter_config.set_selected_index(0); filter_config.on_change = [this](size_t n, OptionsField::value_t v) { (void)n; - max_freq_hold = 0 ; - if( live_frequency_view == 2 ) - { - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - } + reset_live_view( true ); min_color_power = v; }; @@ -426,6 +439,17 @@ namespace ui baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v); }; + button_jump.on_select = [this](Button&) { + receiver_model.set_tuning_frequency(max_freq_hold); // Center tune rx in marker freq. + receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO + nav_.pop(); + nav_.push(); // Jump into audio view + }; + + button_rst.on_select = [this](Button&) { + reset_live_view( true ); + }; + display.scroll_set_area(109, 319); baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger: // Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute , diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index 9335996d..998dbc37 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -69,12 +69,13 @@ namespace ui }; std::vector presets_db{}; - + void on_channel_spectrum(const ChannelSpectrum& spectrum); void do_timers(); void on_range_changed(); void on_lna_changed(int32_t v_db); void on_vga_changed(int32_t v_db); + void reset_live_view( bool clear_screen ); void add_spectrum_pixel(int16_t color); void PlotMarker(rf::Frequency pos); void load_Presets(); @@ -137,42 +138,6 @@ namespace ui {7 * 8, 1 * 16, 4 * 8, 16}, ""}; - OptionsField steps_config{ - { 14 * 8, 4 * 16}, - 4, - { - {"1", 1}, - {"50", 50}, - {"100", 100}, - {"250", 250}, - {"500", 500}, - }}; - - OptionsField view_config{ - { 19 * 8, 4 * 16}, - 7, - { - {"SPCTR-V", 0 }, - {"LEVEL-V", 1 }, - {"PEAK-V" , 2 }, - }}; - - OptionsField level_integration{ - { 27 * 8, 4 * 16}, - 2, - { - {"x1", 1 }, - {"x2", 2 }, - {"x3", 3 }, - {"x4", 4 }, - {"x5", 5 }, - {"x6", 6 }, - {"x7", 7 }, - {"x8", 8 }, - {"x9", 9 }, - }}; - - OptionsField filter_config{ {19 * 8, 1 * 16}, 4, @@ -210,6 +175,55 @@ namespace ui 2, ' '}; + OptionsField steps_config{ + { 14 * 8, 4 * 16}, + 4, + { + {"1", 1}, + {"50", 50}, + {"100", 100}, + {"250", 250}, + {"500", 500}, + }}; + + OptionsField view_config{ + { 19 * 8, 4 * 16}, + 7, + { + {"SPCTR-V", 0 }, + {"LEVEL-V", 1 }, + {"PEAK-V" , 2 }, + }}; + + OptionsField level_integration{ + { 27 * 8, 4 * 16}, + 2, + { + {"x1", 1 }, + {"x2", 2 }, + {"x3", 3 }, + {"x4", 4 }, + {"x5", 5 }, + {"x6", 6 }, + {"x7", 7 }, + {"x8", 8 }, + {"x9", 9 }, + }}; + + Button button_jump { + { 240 - 4 * 8 , 5 * 16 , 4 * 8, 16 }, + "JMP" + }; + + Button button_rst { + { 240 - 9 * 8 , 5 * 16 , 4 * 8, 16 }, + "RST" + }; + + Text freq_stats{ + {0 * 8, 5 * 16 , 240 - 10 * 8 , 8 }, + "" + }; MessageHandlerRegistration message_handler_spectrum_config { Message::ID::ChannelSpectrumConfig,