From 01ba7a57ea8adc8d4a3a484c6a6423d0e6be5b14 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Wed, 21 Oct 2020 13:21:34 -0300 Subject: [PATCH 1/4] New "looking Glass" app Capable of showing a cascade with full bandwidth scan. You can select Min and Max Mhz for the cascade. You can move a marker so to (aproximately) know a particular frequency on the cascade. If you press the select button, the app will jump into the RX -> AUDIO app, already tuned into the just "marked" frequency. This first version SURELY has space for lots of optimizations and improvement in general. --- firmware/application/CMakeLists.txt | 1 + .../application/apps/ui_looking_glass_app.cpp | 226 ++++++++++++++++++ .../application/apps/ui_looking_glass_app.hpp | 162 +++++++++++++ firmware/application/ui_navigation.cpp | 3 +- 4 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 firmware/application/apps/ui_looking_glass_app.cpp create mode 100644 firmware/application/apps/ui_looking_glass_app.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index f551f676c..3ee7d074b 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -231,6 +231,7 @@ set(CPPSRC apps/ui_keyfob.cpp apps/ui_lcr.cpp apps/lge_app.cpp + apps/ui_looking_glass_app.cpp apps/ui_mictx.cpp apps/ui_modemsetup.cpp apps/ui_morse.cpp diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp new file mode 100644 index 000000000..9f80b8d18 --- /dev/null +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 euquiq + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_looking_glass_app.hpp" + + +using namespace portapack; + +namespace ui +{ + +void GlassView::focus() { + field_marker.focus(); +} + +GlassView::~GlassView() { + receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps + receiver_model.disable(); + baseband::shutdown(); +} + +void GlassView::on_lna_changed(int32_t v_db) { + receiver_model.set_lna(v_db); +} + +void GlassView::on_vga_changed(int32_t v_db) { + receiver_model.set_vga(v_db); +} + +void GlassView::add_spectrum_pixel(Color color) +{ + spectrum_row[pixel_index++] = color; + if (pixel_index == 240) //got an entire waterfall line + { + const auto draw_y = display.scroll(1); //Scroll 1 pixel down + display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top + pixel_index = 0; //Start New cascade line + } +} + +//Apparently, the spectrum object returns an array of 256 bins +//Each having the radio signal power for it's corresponding frequency slot +void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) +{ + uint8_t max_power = 0; + baseband::spectrum_streaming_stop(); + + // Convert bins of this spectrum slice into a representative max_power and when enough, into pixels + for (uint16_t bin = 0; bin < 256; bin++) //Spectrum.db has 256 bins + { // Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored + if (bin > 1 && bin < 122) //> 1 + { + if (spectrum.db[128 + bin] > max_power) + max_power = spectrum.db[128 + bin]; + } + else if (bin > 133 && bin < 254) // < 254 + { + if (spectrum.db[bin - 128] > max_power) + max_power = spectrum.db[bin - 128]; + } + + bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz" + + if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled + { + if (min_color_power < max_power) + add_spectrum_pixel(spectrum_rgb3_lut[max_power]); //Pixel will represent max_power + else + add_spectrum_pixel(0); //Filtered out, show black + + max_power = 0; + bins_Hz_size = 0; + if (!pixel_index) //a waterfall line has been completed + break; + } + } + + if (pixel_index) + f_center += SEARCH_SLICE_WIDTH; //Move into the next bandwidth slice + else + f_center = f_center_ini; //Start a new sweep + + receiver_model.set_tuning_frequency(f_center); //tune rx for this slice + baseband::spectrum_streaming_start(); //Do the RX +} + +void GlassView::on_hide() +{ + baseband::spectrum_streaming_stop(); + display.scroll_disable(); +} + +void GlassView::on_show() +{ + baseband::spectrum_streaming_start(); +} + +void GlassView::on_range_changed() +{ + f_min = field_frequency_min.value(); + f_max = field_frequency_max.value(); + search_span = f_max - f_min; + + field_marker.set_range(f_min, f_max); //Move the marker between range + field_marker.set_value(f_min + (search_span / 2)); //Put MARKER AT MIDDLE RANGE + text_range.set(to_string_dec_uint(search_span)); + + f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm + f_max = (f_max)*MHZ_DIV; + search_span = search_span * MHZ_DIV; + + marker_pixel_step = search_span / 240; //Each pixel value in Hz + text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision + field_marker.set_step(marker_pixel_step / MHZ_DIV); //step needs to be a pixel wide. + f_center_ini = f_min + (SEARCH_SLICE_WIDTH / 2); //Initial center frequency for sweep + f_center_ini += SEARCH_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy) + + PlotMarker(field_marker.value()); //Refresh marker on screen + + f_center = f_center_ini; //Reset sweep into first slice + pixel_index = 0; //reset pixel counter + bins_Hz_size = 0; //reset amount of Hz filled up by pixels + receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice +} + +void GlassView::PlotMarker(double pos) +{ + pos = pos * MHZ_DIV; + pos -= f_min; + pos = pos / marker_pixel_step; //Real pixel + + portapack::display.fill_rectangle({0, 84, 240, 4}, Color::black()); //Clear old marker and whole marker rectangle btw + portapack::display.fill_rectangle({pos - 1, 84, 3, 4}, Color::red()); //Red marker middle +} + +GlassView::GlassView( + NavigationView &nav) : nav_(nav) +{ + baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); + + add_children({&labels, + &field_frequency_min, + &field_frequency_max, + &field_lna, + &field_vga, + &text_range, + &filter_config, + &field_rf_amp, + &field_marker, + &text_marker_pm + }); + + field_frequency_min.set_value(2400); + field_frequency_min.on_change = [this](int32_t v) { + if (v >= field_frequency_max.value()) + field_frequency_max.set_value(v + 240); + this->on_range_changed(); + }; + + field_frequency_max.set_value(2640); + field_frequency_max.on_change = [this](int32_t v) { + if (v <= field_frequency_min.value()) + field_frequency_min.set_value(v - 240); + this->on_range_changed(); + }; + + field_lna.set_value(receiver_model.lna()); + field_lna.on_change = [this](int32_t v) { + this->on_lna_changed(v); + }; + + field_vga.set_value(receiver_model.vga()); + field_vga.on_change = [this](int32_t v_db) { + this->on_vga_changed(v_db); + }; + + filter_config.set_selected_index(0); + filter_config.on_change = [this](size_t n, OptionsField::value_t v) { + min_color_power = v; + }; + + field_marker.on_change = [this](int32_t v) { + PlotMarker(v); //Refresh marker on screen + }; + + field_marker.on_select = [this](NumberField&) { + f_center = field_marker.value(); + f_center = f_center * MHZ_DIV; + receiver_model.set_tuning_frequency(f_center); //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 + }; + + display.scroll_set_area( 88, 319); + baseband::set_spectrum(SEARCH_SLICE_WIDTH, 16); // Trigger was 31. Need to understand this parameter. + + on_range_changed(); + + receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); + receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH); //20mhz + receiver_model.set_baseband_bandwidth(SEARCH_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz + receiver_model.set_squelch_level(0); + receiver_model.enable(); +} + +} diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp new file mode 100644 index 000000000..c2390490b --- /dev/null +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 euquiq + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "portapack.hpp" +#include "baseband_api.hpp" +#include "receiver_model.hpp" +#include "ui_widget.hpp" +#include "ui_navigation.hpp" +#include "ui_receiver.hpp" +#include "string_format.hpp" +#include "analog_audio_app.hpp" +#include "spectrum_color_lut.hpp" + +namespace ui +{ + #define SEARCH_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz + #define MHZ_DIV 1000000 + #define X2_MHZ_DIV 2000000 + + class GlassView : public View + { + public: + GlassView(NavigationView &nav); + ~GlassView(); + std::string title() const override { return "Looking Glass"; }; + + void on_show() override; + void on_hide() override; + void focus() override; + + private: + NavigationView& nav_; + + 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 add_spectrum_pixel(Color color); + void PlotMarker(double pos); + + rf::Frequency f_min { 0 }, f_max { 0 }; + rf::Frequency search_span { 0 }; + rf::Frequency f_center { 0 }; + rf::Frequency f_center_ini { 0 }; + double marker_pixel_step { 0 }; + rf::Frequency each_bin_size { SEARCH_SLICE_WIDTH / 256 }; + rf::Frequency bins_Hz_size { 0 }; + uint8_t timing_div { 0 }; + uint8_t min_color_power { 0 }; + uint32_t pixel_index { 0 }; + std::array spectrum_row = { 0 }; + ChannelSpectrumFIFO* fifo { nullptr }; + + Labels labels { + { { 0, 0 }, "MIN: MAX: LNA VGA ", Color::light_grey() }, + { { 0, 1 * 16 }, "RANGE: FILTER: AMP:", Color::light_grey() }, + { { 0, 2 * 16 }, "MARKER: MHz +/- MHz", Color::light_grey() } + }; + + NumberField field_frequency_min { + { 4 * 8, 0 * 16 }, + 4, + { 0, 6960 }, + 240, + ' ' + }; + + NumberField field_frequency_max { + { 13 * 8, 0 * 16 }, + 4, + { 240, 7200 }, + 240, + ' ' + }; + + LNAGainField field_lna { + { 21 * 8, 0 * 16 } + }; + + VGAGainField field_vga { + { 27 * 8, 0 * 16 } + }; + + Text text_range { + { 6 * 8, 1 * 16, 4 * 8, 16 }, + "" + }; + + OptionsField filter_config { + { 18 * 8, 1 * 16 }, + 4, + { + { "OFF ", 0 }, + { "MID ", 118 }, //85 +25 (110) + a bit more to kill all blue + { "HIGH", 202 }, //168 + 25 (193) + } + }; + + RFAmpField field_rf_amp { + { 29 * 8, 1 * 16 } + }; + + Text text_marker { + { 8 * 8, 2 * 16, 4 * 8, 16 }, + "" + }; + + NumberField field_marker { + { 7 * 8, 2 * 16 }, + 4, + { 0, 6000 }, + 25, + ' ' + }; + + Text text_marker_pm { + { 20 * 8, 2 * 16, 2 * 8, 16 }, + "" + }; + + MessageHandlerRegistration message_handler_spectrum_config { + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->fifo = message.fifo; + } + }; + MessageHandlerRegistration message_handler_frame_sync { + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if( this->fifo ) { + ChannelSpectrum channel_spectrum; + while( fifo->out(channel_spectrum) ) { + this->on_channel_spectrum(channel_spectrum); + } + } + } + }; + + }; +} diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index ca6a63347..abec3d23f 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -81,7 +81,7 @@ #include "tpms_app.hpp" #include "core_control.hpp" - +#include "ui_looking_glass_app.hpp" #include "file.hpp" #include "png_writer.hpp" @@ -527,6 +527,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { { "Tools", ui::Color::cyan(), &bitmap_icon_utilities, [&nav](){ nav.push(); } }, { "Options", ui::Color::cyan(), &bitmap_icon_setup, [&nav](){ nav.push(); } }, { "Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav](){ nav.push(); } }, + { "Lookin'Glass", ui::Color::red(), &bitmap_icon_search, [&nav](){ nav.push(); } }, { "HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } }, //{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } } }); From 3e1d726064b8df9eb93da37d622d7340cddd9a7e Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 23 Oct 2020 12:19:57 -0300 Subject: [PATCH 2/4] Enhancement and bug Added a nicer MARKER (thanks to XSX(H1) contributor for the suggestion) Fixed a bug that made the screen scroll from top, when using a popup "window" and returning (like, when pressing the DC VOLTAGE enable / disable" button on top bar) THanks to GregoryFenton for the testing and bug spotting! --- firmware/application/apps/ui_looking_glass_app.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index 9f80b8d18..52e6f2ea3 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -111,6 +111,7 @@ void GlassView::on_hide() void GlassView::on_show() { + display.scroll_set_area( 88, 319); //Restart scrolling on the correct coordinates baseband::spectrum_streaming_start(); } @@ -148,8 +149,10 @@ void GlassView::PlotMarker(double pos) pos -= f_min; pos = pos / marker_pixel_step; //Real pixel - portapack::display.fill_rectangle({0, 84, 240, 4}, Color::black()); //Clear old marker and whole marker rectangle btw - portapack::display.fill_rectangle({pos - 1, 84, 3, 4}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({0, 82, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw + portapack::display.fill_rectangle({pos - 2, 82, 5, 3}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({pos - 1, 84, 3, 3}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({pos, 86, 1, 2}, Color::red()); //Red marker middle } GlassView::GlassView( From 210ec9dd1bbd3df63999c196921f2cc44850ed7e Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:53:10 -0300 Subject: [PATCH 3/4] Looking Glass with PRESETS Added a PRESETS.TXT file (inside /LOOKINGGLASS folder). Also optimized the way the spectrum signal is integrated into the cascade. Added provision for ranges lower than 240MHz but I am afraid that at this time it will not be advisable to lower ranges any more than 240MHz, since some artifacts and frequency running - moving out of place- occurs. I can only hope that someone with a better understanding of hackrf's inner code can fix this issue and perhaps enhance the scanning speed. I found some "original commenting" inside the code: // TODO: Move more low-level radio control stuff to M4. It'll enable tighter // synchronization for things like wideband (sweeping) spectrum analysis, and // protocols that need quick RX/TX turn-around. Which makes me think that there are things "missing" from the portapack side of the code, for allowing serious speed sweeping. So I am concluding that with current "portapack framework" this might be "the best possible thing". It is to be noted that the "new" internal sweep mode code is signed by: * Copyright 2016 Mike Walters, Dominic Spill * * This file is part of HackRF. Maybe Mike or Dominic can be contacted and hopefully lend a hand on enhancing this code. --- .../application/apps/ui_looking_glass_app.cpp | 144 +++++++-- .../application/apps/ui_looking_glass_app.hpp | 289 +++++++++--------- sdcard/LOOKINGGLASS/PRESETS.TXT | 11 + 3 files changed, 282 insertions(+), 162 deletions(-) create mode 100644 sdcard/LOOKINGGLASS/PRESETS.TXT diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index 52e6f2ea3..c690983b8 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -61,21 +61,21 @@ void GlassView::add_spectrum_pixel(Color color) //Each having the radio signal power for it's corresponding frequency slot void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) { - uint8_t max_power = 0; baseband::spectrum_streaming_stop(); // Convert bins of this spectrum slice into a representative max_power and when enough, into pixels - for (uint16_t bin = 0; bin < 256; bin++) //Spectrum.db has 256 bins - { // Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored - if (bin > 1 && bin < 122) //> 1 + // Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored + // All things said and done, we actually need 240 of those bins: + for(uint8_t bin = 0; bin < 240; bin++) + { + if (bin < 120) { + if (spectrum.db[134 + bin] > max_power) + max_power = spectrum.db[134 + bin]; + } + else { - if (spectrum.db[128 + bin] > max_power) - max_power = spectrum.db[128 + bin]; - } - else if (bin > 133 && bin < 254) // < 254 - { - if (spectrum.db[bin - 128] > max_power) - max_power = spectrum.db[bin - 128]; + if (spectrum.db[bin - 118] > max_power) + max_power = spectrum.db[bin - 118]; } bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz" @@ -88,9 +88,14 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) add_spectrum_pixel(0); //Filtered out, show black max_power = 0; - bins_Hz_size = 0; - if (!pixel_index) //a waterfall line has been completed + + if (!pixel_index) //Received indication that a waterfall line has been completed + { + bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin" break; + } else { + bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel + } } } @@ -111,7 +116,7 @@ void GlassView::on_hide() void GlassView::on_show() { - display.scroll_set_area( 88, 319); //Restart scrolling on the correct coordinates + display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates baseband::spectrum_streaming_start(); } @@ -131,7 +136,13 @@ void GlassView::on_range_changed() marker_pixel_step = search_span / 240; //Each pixel value in Hz text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision - field_marker.set_step(marker_pixel_step / MHZ_DIV); //step needs to be a pixel wide. + + int32_t marker_step = marker_pixel_step / MHZ_DIV; + if (!marker_step) + field_marker.set_step(1); //in case selected range is less than 240 (pixels) + else + field_marker.set_step(marker_step); //step needs to be a pixel wide. + f_center_ini = f_min + (SEARCH_SLICE_WIDTH / 2); //Initial center frequency for sweep f_center_ini += SEARCH_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy) @@ -139,20 +150,23 @@ void GlassView::on_range_changed() f_center = f_center_ini; //Reset sweep into first slice pixel_index = 0; //reset pixel counter + max_power = 0; bins_Hz_size = 0; //reset amount of Hz filled up by pixels + + baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Trigger was 31. Need to understand this parameter. receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice } -void GlassView::PlotMarker(double pos) +void GlassView::PlotMarker(rf::Frequency pos) { pos = pos * MHZ_DIV; pos -= f_min; pos = pos / marker_pixel_step; //Real pixel - portapack::display.fill_rectangle({0, 82, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw - portapack::display.fill_rectangle({pos - 2, 82, 5, 3}, Color::red()); //Red marker middle - portapack::display.fill_rectangle({pos - 1, 84, 3, 3}, Color::red()); //Red marker middle - portapack::display.fill_rectangle({pos, 86, 1, 2}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw + portapack::display.fill_rectangle({pos - 2, 100, 5, 3}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({pos - 1, 103, 3, 3}, Color::red()); //Red marker middle + portapack::display.fill_rectangle({pos, 106, 1, 2}, Color::red()); //Red marker middle } GlassView::GlassView( @@ -168,18 +182,21 @@ GlassView::GlassView( &text_range, &filter_config, &field_rf_amp, + &range_presets, &field_marker, &text_marker_pm }); + + load_Presets(); //Load available presets from TXT files (or default) - field_frequency_min.set_value(2400); + field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset field_frequency_min.on_change = [this](int32_t v) { if (v >= field_frequency_max.value()) field_frequency_max.set_value(v + 240); this->on_range_changed(); }; - field_frequency_max.set_value(2640); + field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset field_frequency_max.on_change = [this](int32_t v) { if (v <= field_frequency_min.value()) field_frequency_min.set_value(v - 240); @@ -201,6 +218,12 @@ GlassView::GlassView( min_color_power = v; }; + range_presets.on_change = [this](size_t n, OptionsField::value_t v) { + field_frequency_min.set_value(presets_db[v].min,false); + field_frequency_max.set_value(presets_db[v].max,false); + this->on_range_changed(); + }; + field_marker.on_change = [this](int32_t v) { PlotMarker(v); //Refresh marker on screen }; @@ -214,8 +237,8 @@ GlassView::GlassView( nav_.push(); //Jump into audio view }; - display.scroll_set_area( 88, 319); - baseband::set_spectrum(SEARCH_SLICE_WIDTH, 16); // Trigger was 31. Need to understand this parameter. + display.scroll_set_area( 109, 319); + baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Trigger was 31. Need to understand this parameter. on_range_changed(); @@ -226,4 +249,77 @@ GlassView::GlassView( receiver_model.enable(); } +void GlassView::load_Presets() { + File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD + auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT"); + presets_db.clear(); //Start with fresh db + if (result.is_valid()) { + presets_Default(); //There is no txt, store a default range + } else { + + std::string line; //There is a txt file + char one_char[1]; //Read it char by char + for (size_t pointer=0; pointer < presets_file.size();pointer++) { + presets_file.seek(pointer); + presets_file.read(one_char, 1); + if ((int)one_char[0] > 31) { //ascii space upwards + line += one_char[0]; //Add it to the textline + } + else if (one_char[0] == '\n') { //New Line + txtline_process(line); //make sense of this textline + line.clear(); //Ready for next textline + } + } + if (line.length() > 0) txtline_process(line); //Last line had no newline at end ? + if (!presets_db.size()) presets_Default(); //no antenna on txt, use default + } + + populate_Presets(); +} + +void GlassView::txtline_process(std::string& line) +{ + if (line.find("#") != std::string::npos) return; //Line is just a comment + + size_t comma = line.find(","); //Get first comma position + if (comma == std::string::npos) return; //No comma at all + + size_t previous = 0; + preset_entry new_preset; + + new_preset.min = std::stoi(line.substr(0,comma)); + if (!new_preset.min) return; //No frequency! + + previous = comma + 1; + comma = line.find(",",previous); //Search for next delimiter + if (comma == std::string::npos) return; //No comma at all + + new_preset.max = std::stoi(line.substr(previous,comma - previous)); + if (!new_preset.max) return; //No frequency! + + new_preset.label = line.substr(comma + 1); + if (new_preset.label.size() == 0) return; //No label ? + + presets_db.push_back(new_preset); //Add this preset. +} + +void GlassView::populate_Presets() +{ + using option_t = std::pair; + using options_t = std::vector; + options_t entries; + + for (preset_entry preset : presets_db) + { //go thru all available presets + entries.emplace_back(preset.label,entries.size()); + } + range_presets.set_options(entries); +} + +void GlassView::presets_Default() +{ + presets_db.clear(); + presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"}); +} + } diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index c2390490b..bb6d61346 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -1,162 +1,175 @@ /* - * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. - * Copyright (C) 2020 euquiq - * - * This file is part of PortaPack. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 euquiq + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ -#include "ui.hpp" -#include "portapack.hpp" -#include "baseband_api.hpp" -#include "receiver_model.hpp" -#include "ui_widget.hpp" -#include "ui_navigation.hpp" -#include "ui_receiver.hpp" -#include "string_format.hpp" -#include "analog_audio_app.hpp" -#include "spectrum_color_lut.hpp" + #include "ui.hpp" + #include "portapack.hpp" + #include "baseband_api.hpp" + #include "receiver_model.hpp" + #include "ui_widget.hpp" + #include "ui_navigation.hpp" + #include "ui_receiver.hpp" + #include "string_format.hpp" + #include "analog_audio_app.hpp" + #include "spectrum_color_lut.hpp" -namespace ui -{ - #define SEARCH_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz - #define MHZ_DIV 1000000 - #define X2_MHZ_DIV 2000000 + namespace ui + { + #define SEARCH_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz + #define MHZ_DIV 1000000 + #define X2_MHZ_DIV 2000000 - class GlassView : public View - { - public: - GlassView(NavigationView &nav); - ~GlassView(); - std::string title() const override { return "Looking Glass"; }; + class GlassView : public View + { + public: + GlassView(NavigationView &nav); + ~GlassView(); + std::string title() const override { return "Looking Glass"; }; - void on_show() override; - void on_hide() override; - void focus() override; + void on_show() override; + void on_hide() override; + void focus() override; - private: - NavigationView& nav_; + private: + NavigationView& nav_; - 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 add_spectrum_pixel(Color color); - void PlotMarker(double pos); - - rf::Frequency f_min { 0 }, f_max { 0 }; - rf::Frequency search_span { 0 }; - rf::Frequency f_center { 0 }; - rf::Frequency f_center_ini { 0 }; - double marker_pixel_step { 0 }; - rf::Frequency each_bin_size { SEARCH_SLICE_WIDTH / 256 }; - rf::Frequency bins_Hz_size { 0 }; - uint8_t timing_div { 0 }; - uint8_t min_color_power { 0 }; - uint32_t pixel_index { 0 }; - std::array spectrum_row = { 0 }; - ChannelSpectrumFIFO* fifo { nullptr }; - - Labels labels { - { { 0, 0 }, "MIN: MAX: LNA VGA ", Color::light_grey() }, - { { 0, 1 * 16 }, "RANGE: FILTER: AMP:", Color::light_grey() }, - { { 0, 2 * 16 }, "MARKER: MHz +/- MHz", Color::light_grey() } + struct preset_entry + { + rf::Frequency min{}; + rf::Frequency max{}; + std::string label{}; }; - NumberField field_frequency_min { - { 4 * 8, 0 * 16 }, - 4, - { 0, 6960 }, - 240, - ' ' + 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 add_spectrum_pixel(Color color); + void PlotMarker(rf::Frequency pos); + void load_Presets(); + void txtline_process(std::string& line); + void populate_Presets(); + void presets_Default(); + + rf::Frequency f_min { 0 }, f_max { 0 }; + rf::Frequency search_span { 0 }; + rf::Frequency f_center { 0 }; + rf::Frequency f_center_ini { 0 }; + rf::Frequency marker_pixel_step { 0 }; + rf::Frequency each_bin_size { SEARCH_SLICE_WIDTH / 240 }; + rf::Frequency bins_Hz_size { 0 }; + uint8_t min_color_power { 0 }; + uint32_t pixel_index { 0 }; + std::array spectrum_row = { 0 }; + ChannelSpectrumFIFO* fifo { nullptr }; + uint8_t max_power = 0; + + Labels labels{ + {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, + {{0, 1 * 16}, " RANGE: FILTER: AMP:", Color::light_grey()}, + {{0, 2 * 16}, "PRESET:", Color::light_grey()}, + {{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()} + }; - NumberField field_frequency_max { - { 13 * 8, 0 * 16 }, - 4, - { 240, 7200 }, - 240, - ' ' - }; + NumberField field_frequency_min { + { 4 * 8, 0 * 16 }, + 4, + { 0, 6960 }, + 240, + ' ' + }; - LNAGainField field_lna { - { 21 * 8, 0 * 16 } - }; - - VGAGainField field_vga { - { 27 * 8, 0 * 16 } - }; + NumberField field_frequency_max { + { 13 * 8, 0 * 16 }, + 4, + { 240, 7200 }, + 240, + ' ' + }; - Text text_range { - { 6 * 8, 1 * 16, 4 * 8, 16 }, - "" - }; + LNAGainField field_lna { + { 21 * 8, 0 * 16 } + }; - OptionsField filter_config { - { 18 * 8, 1 * 16 }, + VGAGainField field_vga { + { 27 * 8, 0 * 16 } + }; + + Text text_range{ + {7 * 8, 1 * 16, 4 * 8, 16}, + ""}; + + OptionsField filter_config{ + {19 * 8, 1 * 16}, 4, { - { "OFF ", 0 }, - { "MID ", 118 }, //85 +25 (110) + a bit more to kill all blue - { "HIGH", 202 }, //168 + 25 (193) - } - }; + {"OFF ", 0}, + {"MID ", 118}, //85 +25 (110) + a bit more to kill all blue + {"HIGH", 202}, //168 + 25 (193) + }}; - RFAmpField field_rf_amp { - { 29 * 8, 1 * 16 } - }; + RFAmpField field_rf_amp{ + {29 * 8, 1 * 16}}; - Text text_marker { - { 8 * 8, 2 * 16, 4 * 8, 16 }, - "" - }; + OptionsField range_presets{ + {7 * 8, 2 * 16}, + 20, + { + {" NONE (WIFI 2.4GHz)", 0}, + }}; - NumberField field_marker { - { 7 * 8, 2 * 16 }, + NumberField field_marker{ + {7 * 8, 3 * 16}, 4, - { 0, 6000 }, + {0, 7200}, 25, - ' ' - }; + ' '}; - Text text_marker_pm { - { 20 * 8, 2 * 16, 2 * 8, 16 }, - "" - }; + Text text_marker_pm{ + {20 * 8, 3 * 16, 2 * 8, 16}, + ""}; - MessageHandlerRegistration message_handler_spectrum_config { - Message::ID::ChannelSpectrumConfig, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->fifo = message.fifo; - } - }; - MessageHandlerRegistration message_handler_frame_sync { - Message::ID::DisplayFrameSync, - [this](const Message* const) { - if( this->fifo ) { - ChannelSpectrum channel_spectrum; - while( fifo->out(channel_spectrum) ) { - this->on_channel_spectrum(channel_spectrum); - } - } - } - }; + MessageHandlerRegistration message_handler_spectrum_config { + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->fifo = message.fifo; + } + }; + MessageHandlerRegistration message_handler_frame_sync { + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if( this->fifo ) { + ChannelSpectrum channel_spectrum; + while( fifo->out(channel_spectrum) ) { + this->on_channel_spectrum(channel_spectrum); + } + } + } + }; - }; -} + }; + } + \ No newline at end of file diff --git a/sdcard/LOOKINGGLASS/PRESETS.TXT b/sdcard/LOOKINGGLASS/PRESETS.TXT new file mode 100644 index 000000000..cd179d542 --- /dev/null +++ b/sdcard/LOOKINGGLASS/PRESETS.TXT @@ -0,0 +1,11 @@ +# INI,END,DESCRIPTION +# (Freq range in MHz, min separation 240MHz) +# (Description up to 20 char) +# (fields comma delimiter) +260,500,315/433 MHz KEYFOBS +2320,2560,BL / WIFI 2.4GHz +5160,5900,WIFI 5GHz +10,7250,FULL RANGE +140,380,VHF MICS AND MARINE +390,420,RADIOSONDES +420,660,UHF MICS \ No newline at end of file From 13ce56f7bf44b1020840bfe82fc7200356ebbb1a Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 26 Oct 2020 23:43:35 -0300 Subject: [PATCH 4/4] Added "RESOLUTION" parameter Discord User jteich did some investigation (Thanks!) and helped me understanding this rather obscure parameter: Internally, is called "TRIGGER", and is passed into the baseband when configuring the desired spectrum sample rate. Please forgive me in advance if this explanation is not 100% correct. It's only my interpretation, based on my own observation and jteich's comments over Discord chat. This trigger parameter apparently determines the amount of data over time used for calculating the signal's power inside each specttrum's bin, before considering it "done". In short, if you lower this resolution value then the cascade will tend to be rendered a bit faster, while kind of blind to tiny signals. On the other hand, a bigger value will help rendering and distinguishing different signals on the cascade. Too big a value can easily clutter up the cascade. But then it may be a "blessing" when inspecting higher freuqencies -where hackrf is more deaf" The default value of 32 is quite decent. But then, now you can experiment with it. Cheers --- .../application/apps/ui_looking_glass_app.cpp | 17 +++++++++++++---- .../application/apps/ui_looking_glass_app.hpp | 12 ++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index c690983b8..e18bba627 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -100,7 +100,7 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) } if (pixel_index) - f_center += SEARCH_SLICE_WIDTH; //Move into the next bandwidth slice + f_center += SEARCH_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = SEARCH_SLICE_WIDTH else f_center = f_center_ini; //Start a new sweep @@ -153,7 +153,7 @@ void GlassView::on_range_changed() max_power = 0; bins_Hz_size = 0; //reset amount of Hz filled up by pixels - baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Trigger was 31. Need to understand this parameter. + baseband::set_spectrum(SEARCH_SLICE_WIDTH, field_trigger.value()); receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice } @@ -184,7 +184,8 @@ GlassView::GlassView( &field_rf_amp, &range_presets, &field_marker, - &text_marker_pm + &text_marker_pm, + &field_trigger }); load_Presets(); //Load available presets from TXT files (or default) @@ -237,8 +238,16 @@ GlassView::GlassView( nav_.push(); //Jump into audio view }; + field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution + field_trigger.on_change = [this](int32_t v) { + baseband::set_spectrum(SEARCH_SLICE_WIDTH, v); + }; + display.scroll_set_area( 109, 319); - baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Trigger was 31. Need to understand this parameter. + baseband::set_spectrum(SEARCH_SLICE_WIDTH, field_trigger.value()); //trigger: + // Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute , + // it keeps adding the output of the fft to the buffer until "trigger" number of calls are made, + //at which time it pushes the buffer up with channel_spectrum.feed on_range_changed(); diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index bb6d61346..3313a55f4 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -89,8 +89,8 @@ {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, {{0, 1 * 16}, " RANGE: FILTER: AMP:", Color::light_grey()}, {{0, 2 * 16}, "PRESET:", Color::light_grey()}, - {{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()} - + {{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()}, + {{0, 4 * 16}, "RESOLUTION: (fft trigger)", Color::light_grey()} }; NumberField field_frequency_min { @@ -151,6 +151,14 @@ {20 * 8, 3 * 16, 2 * 8, 16}, ""}; + NumberField field_trigger{ + {11 * 8, 4 * 16}, + 3, + {2, 128}, + 2, + ' '}; + + MessageHandlerRegistration message_handler_spectrum_config { Message::ID::ChannelSpectrumConfig, [this](const Message* const p) {