diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index 20817141..47aa72be 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2020 euquiq + * Copyright (C) 2023 gullradriel, Nilorea Studio Inc. * * This file is part of PortaPack. * @@ -49,40 +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 + 16 } , { 240 , 320 - (108 + 16) } } , { 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 ) - { - 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 - //display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); - for( uint16_t xpos = 0 ; xpos < 240 ; xpos ++ ) - { - int16_t point = y_max_range.clip( ( ( spectrum_data[ xpos ] - raw_min ) * ( 320 - 108 ) ) / raw_delta ); - uint8_t color_gradient = (point * 255) / 212 ; - 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 } } @@ -155,6 +187,7 @@ namespace ui void GlassView::on_range_changed() { + reset_live_view( false ); f_min = field_frequency_min.value(); f_max = field_frequency_max.value(); search_span = f_max - f_min; @@ -194,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( @@ -220,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) @@ -228,6 +269,7 @@ namespace ui field_frequency_min.set_step( steps ); field_frequency_min.on_change = [this](int32_t v) { + reset_live_view( true ); int32_t steps_ = steps ; if( steps_ < 24 ) steps_ = 24 ; @@ -261,6 +303,7 @@ namespace ui field_frequency_max.set_step( steps ); field_frequency_max.on_change = [this](int32_t v) { + reset_live_view( true ); int32_t steps_ = steps ; if( steps_ < 24 ) steps_ = 24 ; @@ -293,12 +336,14 @@ namespace ui field_lna.set_value(receiver_model.lna()); field_lna.on_change = [this](int32_t v) { + 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) { + reset_live_view( true ); this->on_vga_changed(v_db); }; @@ -313,34 +358,54 @@ namespace ui view_config.on_change = [this](size_t n, OptionsField::value_t v) { - (void)n; - live_frequency_view = v ; - if( v ) - { - display.scroll_disable(); - level_integration.hidden( false ); - } - else - { - level_integration.hidden( true ); - set_dirty(); - display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates - } - // clear between changes - display.fill_rectangle( { { 0 , 108 } , { 240 , 320 - 108 } } , { 0 , 0 , 0 } ); + (void)n; + // 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 ); + 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 ); + } + set_dirty(); }; view_config.set_selected_index(0); //default spectrum level_integration.on_change = [this](size_t n, OptionsField::value_t v) { - (void)n; - live_frequency_integrate = v ; + (void)n; + reset_live_view( true ); + live_frequency_integrate = v ; }; level_integration.set_selected_index(2); //default integration of ( 3 * old value + new_value ) / 4 - filter_config.set_selected_index(0); + filter_config.set_selected_index(0); filter_config.on_change = [this](size_t n, OptionsField::value_t v) { (void)n; + reset_live_view( true ); min_color_power = v; }; @@ -373,6 +438,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 0e97b3be..998dbc37 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2020 euquiq + * Copyright (C) 2023 gullradriel, Nilorea Studio Inc. * * This file is part of PortaPack. * @@ -36,7 +37,9 @@ namespace ui { -#define LOOKING_GLASS_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz +#define LOOKING_GLASS_SLICE_WIDTH 19999920 // Each slice bandwidth 20 MHz and a multiple of 240 + // since we are using LOOKING_GLASS_SLICE_WIDTH/240 as the each_bin_size + // it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq #define MHZ_DIV 1000000 #define X2_MHZ_DIV 2000000 @@ -66,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(); @@ -93,8 +97,10 @@ namespace ui ChannelSpectrumFIFO* fifo { nullptr }; uint8_t max_power = 0; int32_t steps = 250 ; // default of 250 Mhz steps - bool live_frequency_view = false ; + uint8_t live_frequency_view = 0 ; int16_t live_frequency_integrate = 3 ; + uint64_t max_freq_hold = 0 ; + int16_t max_freq_power = -1000 ; Labels labels{ {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, @@ -132,41 +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", false }, - {"LEVEL-V", true }, - }}; - - 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, @@ -204,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,