From 27f566be8f8423ae92140950116057e9db8a12e8 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 20 Jul 2020 16:43:24 -0300 Subject: [PATCH 1/6] scanner-enhanced-version New ui_scanner, inspired on AlainD's (alain00091) PR: https://github.com/eried/portapack-mayhem/pull/80 It includes the following: 1) A big frequency numbers display. 2) A Manual scan section (you can input a frequency range (START / END), choose a STEP value from an available of standard frequency intervals, and press SCAN button. 3) An AM / WFM / NFM scan mode selector, changing "on the fly". 4) A PAUSE / RESUME button, which will make the scanner to stop upon you listening something of interest 5) AUDIO APP button, a quick shortcut into the analog audio visualizing / recording app, with the mode, frequency, amp, LNA, VGA settings already in tune with the scanner. 6) Two enums are added to freqman.hpp, reserved for compatibility with AlainD's proposed freqman's app and / or further enhancement. More on this topic: ORIGINAL scanner just used one frequency step, when creating scanning frequency ranges, which was unacceptable. AlainD enhanced freqman in order to pass different steppings along with ranges. This seems an excellent idea, and I preserved that aspect on my current implementation of thisscanner, while adding those enums into the freqman just to keep the door open for AlainD's freqman in the future. 7) I did eliminate the extra blank spaces added by function to_string_short_freq() which created unnecessary spacing in every app where there is need for a SHORT string, from a frequency number. (SHORT!, no extra spaces!!) 8) I also maintained AlainD idea of capping the number of frequencies which are dynamically created for each range and stored inside a memory based db. While AlainD capped the number into 400 frequencies, I was able to up that value a bit more, into 500. Cheers! --- firmware/application/apps/ui_scanner.cpp | 373 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 172 ++++++++--- firmware/application/freqman.hpp | 17 ++ firmware/application/string_format.cpp | 3 +- 4 files changed, 421 insertions(+), 144 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 26c007286..1e68d081d 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -26,6 +26,7 @@ #include "string_format.hpp" #include "audio.hpp" + using namespace portapack; namespace ui { @@ -38,6 +39,10 @@ ScannerThread::ScannerThread( } ScannerThread::~ScannerThread() { + stop(); +} + +void ScannerThread::stop() { if( thread ) { chThdTerminate(thread); chThdWait(thread); @@ -49,6 +54,18 @@ void ScannerThread::set_scanning(const bool v) { _scanning = v; } +bool ScannerThread::is_scanning() { + return _scanning; +} + +void ScannerThread::set_userpause(const bool v) { + _userpause = v; +} + +bool ScannerThread::is_userpause() { + return _userpause; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -56,36 +73,33 @@ msg_t ScannerThread::static_fn(void* arg) { } void ScannerThread::run() { - RetuneMessage message { }; - uint32_t frequency_index = 0; - - while( !chThdShouldTerminate() ) { - if (_scanning) { - // Retune - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... + RetuneMessage message { }; + uint32_t frequency_index = 0; + while( !chThdShouldTerminate() ) { + if (_scanning) { + // Retune + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + message.range = frequency_index; + EventDispatcher::send_message(message); - message.range = frequency_index; - EventDispatcher::send_message(message); - - - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + } + chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? } - - chThdSleepMilliseconds(50); } } void ScannerView::handle_retune(uint32_t i) { - text_cycle.set( to_string_dec_uint(i) + "/" + - to_string_dec_uint(frequency_list.size()) + " : " + - to_string_dec_uint(frequency_list[i]) ); - desc_cycle.set( description_list[i] ); + big_display.set(frequency_list[i]); //Show the big Freq + text_cycle.set( to_string_dec_uint(i + 1,3) ); + if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show } void ScannerView::focus() { - field_lna.focus(); + field_mode.focus(); } ScannerView::~ScannerView() { @@ -94,9 +108,16 @@ ScannerView::~ScannerView() { baseband::shutdown(); } +void ScannerView::show_max() { //show total number of freqs to scan + if (frequency_list.size() == MAX_DB_ENTRY) + text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); + else + text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); +} + ScannerView::ScannerView( - NavigationView& -) + NavigationView& nav + ) : nav_ { nav } { add_children({ &labels, @@ -105,104 +126,190 @@ ScannerView::ScannerView( &field_rf_amp, &field_volume, &field_bw, - &field_trigger, &field_squelch, &field_wait, - //&record_view, + &rssi, &text_cycle, + &text_max, &desc_cycle, - //&waterfall, + &big_display, + &button_manual_start, + &button_manual_end, + &field_mode, + &step_mode, + &button_manual_scan, + &button_pause, + &button_audio_app }); - std::string scanner_file = "SCANNER"; - if (load_freqman_file(scanner_file, database)) { - for(auto& entry : database) { - // FIXME - if (entry.type == RANGE) { - for (uint32_t i=entry.frequency_a; i < entry.frequency_b; i+= 1000000) { - frequency_list.push_back(i); - description_list.push_back("RNG " + to_string_dec_uint(entry.frequency_a) + ">" + to_string_dec_uint(entry.frequency_b)); - } - } else { - frequency_list.push_back(entry.frequency_a); - description_list.push_back(entry.description); - } + def_step = change_mode(AM); //Start on AM + field_mode.set_by_value(AM); //Reflect the mode into the manual selector + + big_display.set_style(&style_green); //Start with green color + + button_manual_start.on_select = [this, &nav](Button& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + button_manual_end.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + button_pause.on_select = [this](Button&) { + if (scan_thread->is_userpause()) { + timer = wait * 10; //Unlock timer pause on_statistics_update + button_pause.set_text("PAUSE"); //resume scanning (show button for pause) + scan_thread->set_userpause(false); + //scan_resume(); + } else { + scan_pause(); + scan_thread->set_userpause(true); + button_pause.set_text("RESUME"); //PAUSED, show resume } - } else { - // DEBUG - // TODO: Clean this - frequency_list.push_back(466025000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466050000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466075000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466175000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466206250); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466231250); - description_list.push_back("POCSAG-France"); - } - - field_bw.set_selected_index(2); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_nbfm_configuration(n); }; - field_wait.on_change = [this](int32_t v) { - wait = v; - }; - field_wait.set_value(5); - - field_trigger.on_change = [this](int32_t v) { - trigger = v; - }; - field_trigger.set_value(30); - - field_squelch.set_value(receiver_model.squelch_level()); - field_squelch.on_change = [this](int32_t v) { - squelch = v; - receiver_model.set_squelch_level(v); + button_audio_app.on_select = [this](Button&) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); + scan_thread->stop(); + nav_.pop(); + nav_.push(); }; + button_manual_scan.on_select = [this](Button&) { + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + scan_thread->stop(); //STOP SCANNER THREAD + frequency_list.clear(); + description_list.clear(); + def_step = step_mode.selected_index_value(); //Use def_step from manual selector + description_list.push_back( + "M:" + to_string_short_freq(frequency_range.min) + ">" + + to_string_short_freq(frequency_range.max) + " S:" + + to_string_short_freq(def_step) + ); + + rf::Frequency frequency = frequency_range.min; + while (frequency_list.size() < MAX_DB_ENTRY && frequency <= frequency_range.max) { //add manual range + frequency_list.push_back(frequency); + description_list.push_back(""); //If empty, will keep showing the last description + frequency+=def_step; + } + + show_max(); + start_scan_thread(); //RESTART SCANNER THREAD + } + }; + + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::stop(); + scan_thread->stop(); + receiver_model.disable(); + baseband::shutdown(); + chThdSleepMilliseconds(50); + change_mode(v); + start_scan_thread(); + }; + + //PRE-CONFIGURATION: + field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); + field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - audio::output::start(); - - audio::output::mute(); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.enable(); - receiver_model.set_squelch_level(0); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - audio::output::unmute(); - - // TODO: Scanning thread here - scan_thread = std::make_unique(frequency_list); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + // LEARN FREQUENCIES + std::string scanner_txt = "SCANNER"; + if ( load_freqman_file(scanner_txt, database) ) { + for(auto& entry : database) { // READ LINE PER LINE + if (frequency_list.size() < MAX_DB_ENTRY) { //We got space! + if (entry.type == RANGE) { //RANGE + switch (entry.step) { + case AM_US: def_step = 10000; break ; + case AM_EUR:def_step = 9000; break ; + case NFM_1: def_step = 12500; break ; + case NFM_2: def_step = 6250; break ; + case FM_1: def_step = 100000; break ; + case FM_2: def_step = 50000; break ; + case N_1: def_step = 25000; break ; + case N_2: def_step = 250000; break ; + case AIRBAND:def_step= 8330; break ; + } + frequency_list.push_back(entry.frequency_a); //Store starting freq and description + description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) + + ">" + to_string_short_freq(entry.frequency_b) + + " S:" + to_string_short_freq(def_step)); + while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range + entry.frequency_a+=def_step; + frequency_list.push_back(entry.frequency_a); + description_list.push_back(""); //Token (keep showing the last description) + } + } else if ( entry.type == SINGLE) { + frequency_list.push_back(entry.frequency_a); + description_list.push_back("S: " + entry.description); + } + show_max(); + } + else + { + break; //No more space: Stop reading the txt file ! + } + } + } + else + { + desc_cycle.set(" NO SCANNER.TXT FILE ..." ); + } + audio::output::stop(); + step_mode.set_by_value(def_step); //Impose the default step into the manual step selector + start_scan_thread(); } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - int32_t max_db = statistics.max_db; - - if (timer <= wait) - timer++; - - if (max_db < -trigger) { - if (timer == wait) { - //audio::output::stop(); - scan_thread->set_scanning(true); + if (!scan_thread->is_userpause()) + { + if (timer >= (wait * 10) ) + { + timer=0; + scan_resume(); + } + else if (!timer) + { + if (statistics.max_db > -squelch) { //There is something on the air... + scan_pause(); + timer++; + } + } + else + { + timer++; } - } else { - //audio::output::start(); - scan_thread->set_scanning(false); - timer = 0; + } +} + +void ScannerView::scan_pause() { + if (scan_thread->is_scanning()) { + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::start(); + } +} + +void ScannerView::scan_resume() { + if (!scan_thread->is_scanning()) { + audio::output::stop(); + scan_thread->set_scanning(true); // WE RESCAN } } @@ -211,4 +318,60 @@ void ScannerView::on_headphone_volume_changed(int32_t v) { receiver_model.set_headphone_volume(new_volume); } -} /* namespace ui */ +size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread() + using option_t = std::pair; + using options_t = std::vector; + options_t bw; + field_bw.on_change = [this](size_t n, OptionsField::value_t) { }; + + switch (new_mod) { + case NFM: //bw 16k (2) default + bw.emplace_back("8k5", 0); + bw.emplace_back("11k", 0); + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + field_bw.set_selected_index(2); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); + break; + case AM: + bw.emplace_back("DSB", 0); + bw.emplace_back("USB", 0); + bw.emplace_back("LSB", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + field_bw.set_selected_index(0); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(2000000);receiver_model.set_baseband_bandwidth(2000000); + break; + case WFM: + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + field_bw.set_selected_index(0); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000); + break; + } + + return mod_step[new_mod]; + +} + +void ScannerView::start_scan_thread() { + receiver_model.enable(); + receiver_model.set_squelch_level(0); + scan_thread = std::make_unique(frequency_list); +} + +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index b5d17c228..354700af7 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -20,20 +20,38 @@ * Boston, MA 02110-1301, USA. */ +#include "ui.hpp" #include "receiver_model.hpp" #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" +#include "ui_spectrum.hpp" #include "freqman.hpp" +#include "log_file.hpp" +#include "analog_audio_app.hpp" + + +#define MAX_DB_ENTRY 500 namespace ui { +enum modulation_type { AM = 0,WFM,NFM }; + +string const mod_name[3] = {"AM", "WFM", "NFM"}; +size_t const mod_step[3] = {9000, 100000, 12500 }; + class ScannerThread { public: ScannerThread(std::vector frequency_list); ~ScannerThread(); - + void set_scanning(const bool v); + bool is_scanning(); + + void set_userpause(const bool v); + bool is_userpause(); + + void stop(); ScannerThread(const ScannerThread&) = delete; ScannerThread(ScannerThread&&) = delete; @@ -45,38 +63,64 @@ private: Thread* thread { nullptr }; bool _scanning { true }; - + bool _userpause { false }; static msg_t static_fn(void* arg); - void run(); }; class ScannerView : public View { public: - ScannerView(NavigationView&); + ScannerView(NavigationView& nav); ~ScannerView(); void focus() override; + + void big_display_freq(rf::Frequency f); + + const Style style_grey { // scanning + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; - std::string title() const override { return "Scanner"; }; + const Style style_green { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + std::string title() const override { return "SCANNER"; }; + std::vector frequency_list{ }; + std::vector description_list { }; + +//void set_parent_rect(const Rect new_parent_rect) override; private: + NavigationView& nav_; + + void start_scan_thread(); + size_t change_mode(uint8_t mod_type); + void show_max(); + void scan_pause(); + void scan_resume(); + void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); void handle_retune(uint32_t i); - - std::vector frequency_list { }; - std::vector description_list { }; - int32_t trigger { 0 }; + + jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too... int32_t squelch { 0 }; uint32_t timer { 0 }; uint32_t wait { 0 }; + size_t def_step { 0 }; freqman_db database { }; Labels labels { - { { 0 * 8, 0 * 16 }, "LNA: TRIGGER: /99 VOL:", Color::light_grey() }, - { { 0 * 8, 1 * 16 }, "VGA: SQUELCH: /99 AMP:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, " BW: WAIT:", Color::light_grey() }, + { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, + { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, + { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, + { { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, + { { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -84,15 +128,15 @@ private: }; VGAGainField field_vga { - { 4 * 8, 1 * 16 } + { 11 * 8, 0 * 16 } }; RFAmpField field_rf_amp { - { 28 * 8, 1 * 16 } + { 18 * 8, 0 * 16 } }; NumberField field_volume { - { 28 * 8, 0 * 16 }, + { 24 * 8, 0 * 16 }, 2, { 0, 99 }, 1, @@ -100,25 +144,13 @@ private: }; OptionsField field_bw { - { 4 * 8, 2 * 16 }, - 3, - { - { "8k5", 0 }, - { "11k", 0 }, - { "16k", 0 }, - } - }; + { 3 * 8, 1 * 16 }, + 4, + { } + }; - NumberField field_trigger { - { 16 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - NumberField field_squelch { - { 16 * 8, 1 * 16 }, + { 15 * 8, 1 * 16 }, 2, { 0, 99 }, 1, @@ -126,20 +158,84 @@ private: }; NumberField field_wait { - { 16 * 8, 2 * 16 }, + { 26 * 8, 1 * 16 }, 2, { 0, 99 }, 1, ' ', }; + RSSI rssi { + { 0 * 16, 2 * 16, 15 * 16, 8 }, + }; + Text text_cycle { - { 0, 5 * 16, 240, 16 }, - "--/--" + { 0, 3 * 16, 3 * 8, 16 }, }; + + Text text_max { + { 4 * 8, 3 * 16, 18 * 8, 16 }, + }; + Text desc_cycle { - {0, 6 * 16, 240, 16 }, - " " + {0, 4 * 16, 240, 16 }, + }; + + BigFrequency big_display { //Show frequency in glamour + { 4, 6 * 16, 28 * 8, 52 }, + 0 + }; + + Button button_manual_start { + { 0 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_end { + { 12 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_scan { + { 24 * 8, 11 * 16, 6 * 8, 28 }, + "SCAN" + }; + + OptionsField field_mode { + { 5 * 8, 14 * 16 }, + 6, + { + { " AM ", 0 }, + { " WFM ", 1 }, + { " NFM ", 2 }, + } + }; + + OptionsField step_mode { + { 17 * 8, 14 * 16 }, + 12, + { + { "5Khz (SA AM)", 5000 }, + { "9Khz (EU AM)", 9000 }, + { "10Khz(US AM)", 10000 }, + { "50Khz (FM1)", 50000 }, + { "100Khz(FM2)", 100000 }, + { "6.25khz(NFM)", 6250 }, + { "12.5khz(NFM)", 12500 }, + { "25khz (N1)", 25000 }, + { "250khz (N2)", 250000 }, + { "8.33khz(AIR)", 8330 } + } + }; + + Button button_pause { + { 12, 17 * 16, 96, 24 }, + "PAUSE" + }; + + Button button_audio_app { + { 124, 17 * 16, 96, 24 }, + "AUDIO APP" }; std::unique_ptr scan_thread { }; @@ -160,4 +256,4 @@ private: }; }; -} /* namespace ui */ +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/freqman.hpp b/firmware/application/freqman.hpp index daaad1efb..dca1f3645 100644 --- a/firmware/application/freqman.hpp +++ b/firmware/application/freqman.hpp @@ -48,11 +48,28 @@ enum freqman_entry_type { RANGE }; +//Entry step placed for AlainD freqman version (or any other enhanced version) +enum freqman_entry_step { + STEP_DEF = 0, // default + AM_US, // 10 Khz AM/CB + AM_EUR, // 9 Khz LW/MW + NFM_1, // 12,5 Khz (Analogic PMR 446) + NFM_2, // 6,25 Khz (Digital PMR 446) + FM_1, // 100 Khz + FM_2, // 50 Khz + N_1, // 25 Khz + N_2, // 250 Khz + AIRBAND, // AIRBAND 8,33 Khz + ERROR_STEP +}; + +// freqman_entry_step step added, as above, to provide compatibility / future enhancement. struct freqman_entry { rf::Frequency frequency_a { 0 }; rf::Frequency frequency_b { 0 }; std::string description { }; freqman_entry_type type { }; + freqman_entry_step step { }; }; using freqman_db = std::vector; diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index a62ce1ae3..fd7fb1a3c 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -113,7 +113,8 @@ std::string to_string_dec_int( } std::string to_string_short_freq(const uint64_t f) { - auto final_str = to_string_dec_int(f / 1000000, 4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); + //was... to_string_dec_int(f / 1000000,4) + auto final_str = to_string_dec_int(f / 1000000) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); return final_str; } From f50b376cbd3d07eca884e9b206b572719a5f74a5 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 20 Jul 2020 17:43:30 -0300 Subject: [PATCH 2/6] Update ui_scanner.hpp Sorry, I missed this before: These two includes are NOT necessary anymore. --- firmware/application/apps/ui_scanner.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 354700af7..da83e83fe 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -25,9 +25,7 @@ #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" -#include "ui_spectrum.hpp" #include "freqman.hpp" -#include "log_file.hpp" #include "analog_audio_app.hpp" From e8f6e1389e53e8aeb7b6da61331d1a25a3512326 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Sat, 25 Jul 2020 14:07:03 -0300 Subject: [PATCH 3/6] better "squelch" and coloring big numbers When scanner finds a freq with high dbi, it locks into it "listening" a bit more (less than a second) for either confirm or discard it as an actual high dbi or just a spurious thing. The big number frequency changes color accordingly: Grey = just scanning, yellow = locking in, Green = Found something, allowing the user to listen. --- firmware/application/apps/ui_scanner.cpp | 91 +++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 12 +++- 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 1e68d081d..26c5c776f 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -21,7 +21,7 @@ */ #include "ui_scanner.hpp" - +#include "portapack_persistent_memory.hpp" #include "baseband_api.hpp" #include "string_format.hpp" #include "audio.hpp" @@ -66,6 +66,14 @@ bool ScannerThread::is_userpause() { return _userpause; } +void ScannerThread::set_freq_lock(const uint32_t v) { + _freq_lock = v; +} + +uint32_t ScannerThread::is_freq_lock() { + return _freq_lock; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -75,27 +83,44 @@ msg_t ScannerThread::static_fn(void* arg) { void ScannerThread::run() { if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; - uint32_t frequency_index = 0; + uint32_t frequency_index = frequency_list_.size(); + uint32_t freqlock_counter=0; while( !chThdShouldTerminate() ) { if (_scanning) { - // Retune - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); - message.range = frequency_index; + if (_freq_lock == 0) { //normal scanning + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune + } else { + chThdSleepMilliseconds(25); //Extra time ? + } + + message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); - - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; } - chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? + chThdSleepMilliseconds(50); } } } void ScannerView::handle_retune(uint32_t i) { - big_display.set(frequency_list[i]); //Show the big Freq - text_cycle.set( to_string_dec_uint(i + 1,3) ); - if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + switch (scan_thread->is_freq_lock()) + { + case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING + text_cycle.set( to_string_dec_uint(i + 1,3) ); + if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + break; + case 1: + big_display.set_style(&style_yellow); //STARTING LOCK FREQ + break; + case MAX_FREQ_LOCK: + big_display.set_style(&style_green); //FREQ LOCK FULL, GREEN! + break; + default: //freq lock is checking the signal, do not update display + return; + } + big_display.set(frequency_list[i]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) } void ScannerView::focus() { @@ -145,7 +170,14 @@ ScannerView::ScannerView( def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - big_display.set_style(&style_green); //Start with green color + big_display.set_style(&style_grey); //Start with gray color + + //HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + frequency_range.min = stored_freq - 1000000; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + frequency_range.max = stored_freq + 1000000; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); button_manual_start.on_select = [this, &nav](Button& button) { auto new_view = nav_.push(frequency_range.min); @@ -165,10 +197,9 @@ ScannerView::ScannerView( button_pause.on_select = [this](Button&) { if (scan_thread->is_userpause()) { - timer = wait * 10; //Unlock timer pause on_statistics_update + timer = wait * 10; //Unlock timer pause on_statistics_update button_pause.set_text("PAUSE"); //resume scanning (show button for pause) - scan_thread->set_userpause(false); - //scan_resume(); + scan_thread->set_userpause(false); //Signal user's will } else { scan_pause(); scan_thread->set_userpause(true); @@ -190,13 +221,14 @@ ScannerView::ScannerView( } else if (frequency_range.min > frequency_range.max) { nav_.display_modal("Error", "END freq\nis lower than START"); } else { + audio::output::stop(); scan_thread->stop(); //STOP SCANNER THREAD frequency_list.clear(); description_list.clear(); def_step = step_mode.selected_index_value(); //Use def_step from manual selector description_list.push_back( - "M:" + to_string_short_freq(frequency_range.min) + ">" + "M:" + to_string_short_freq(frequency_range.min) + " >" + to_string_short_freq(frequency_range.max) + " S:" + to_string_short_freq(def_step) ); @@ -249,7 +281,7 @@ ScannerView::ScannerView( } frequency_list.push_back(entry.frequency_a); //Store starting freq and description description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) - + ">" + to_string_short_freq(entry.frequency_b) + + " >" + to_string_short_freq(entry.frequency_b) + " S:" + to_string_short_freq(def_step)); while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range entry.frequency_a+=def_step; @@ -287,12 +319,21 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { } else if (!timer) { - if (statistics.max_db > -squelch) { //There is something on the air... - scan_pause(); - timer++; - } + if (statistics.max_db > -squelch) { //There is something on the air... + if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached + scan_pause(); + timer++; + } else { + scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal + } + } else { //There is NOTHING on the air + if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ? + big_display.set_style(&style_grey); //Back to grey color + scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no signal + } + } } - else + else //Ongoing wait time { timer++; } @@ -301,6 +342,7 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { void ScannerView::scan_pause() { if (scan_thread->is_scanning()) { + scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan scan_thread->set_scanning(false); // WE STOP SCANNING audio::output::start(); } @@ -309,6 +351,7 @@ void ScannerView::scan_pause() { void ScannerView::scan_resume() { if (!scan_thread->is_scanning()) { audio::output::stop(); + big_display.set_style(&style_grey); //Back to grey color scan_thread->set_scanning(true); // WE RESCAN } } diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index da83e83fe..f9ee3aa73 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -22,7 +22,6 @@ #include "ui.hpp" #include "receiver_model.hpp" - #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" #include "freqman.hpp" @@ -30,6 +29,7 @@ #define MAX_DB_ENTRY 500 +#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous namespace ui { @@ -49,6 +49,9 @@ public: void set_userpause(const bool v); bool is_userpause(); + void set_freq_lock(const uint32_t v); + uint32_t is_freq_lock(); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -62,6 +65,7 @@ private: bool _scanning { true }; bool _userpause { false }; + uint32_t _freq_lock { 0 }; static msg_t static_fn(void* arg); void run(); }; @@ -81,6 +85,12 @@ public: .foreground = Color::grey(), }; + const Style style_yellow { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::dark_yellow(), + }; + const Style style_green { //Found signal .font = font::fixed_8x16, .background = Color::black(), From 03084251c5c1b901bf43bee2edeae6600b65bb46 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Tue, 28 Jul 2020 01:21:52 -0300 Subject: [PATCH 4/6] Added MIC TX and FREQ DEL buttons MIC TX button :Shortcut for jumping into TX -> MIC app. FREQ DEL button: Deletes currently displayed frequency from temporary scanning memory. Ideal to get rid of those not wanted "noisy" freqs in the middle of a range scan. Also, some code optimizations thrown in. --- firmware/application/apps/ui_scanner.cpp | 123 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 40 ++++++-- 2 files changed, 114 insertions(+), 49 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 26c5c776f..eafdf4d13 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -24,8 +24,6 @@ #include "portapack_persistent_memory.hpp" #include "baseband_api.hpp" #include "string_format.hpp" -#include "audio.hpp" - using namespace portapack; @@ -58,14 +56,6 @@ bool ScannerThread::is_scanning() { return _scanning; } -void ScannerThread::set_userpause(const bool v) { - _userpause = v; -} - -bool ScannerThread::is_userpause() { - return _userpause; -} - void ScannerThread::set_freq_lock(const uint32_t v) { _freq_lock = v; } @@ -74,6 +64,10 @@ uint32_t ScannerThread::is_freq_lock() { return _freq_lock; } +void ScannerThread::set_freq_del(const uint32_t v) { + _freq_del = v; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -81,25 +75,42 @@ msg_t ScannerThread::static_fn(void* arg) { } void ScannerThread::run() { - if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... + if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; uint32_t frequency_index = frequency_list_.size(); - uint32_t freqlock_counter=0; while( !chThdShouldTerminate() ) { - if (_scanning) { - if (_freq_lock == 0) { //normal scanning + if (_scanning) + { //Scanning + if (_freq_lock == 0) { //normal scanning (not performing freq_lock) frequency_index++; if (frequency_index >= frequency_list_.size()) frequency_index = 0; receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune - } else { - chThdSleepMilliseconds(25); //Extra time ? } - message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); + } + else + { //If not scanning, check for user asking to delete a freq: + if (_freq_del != 0) { //There is a frequency to delete + for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete + if (frequency_list_[i] == _freq_del) + { //found: Erase it + frequency_list_.erase(frequency_list_.begin() + i); + if (i==0) //set scan index one place back to compensate + i=frequency_list_.size(); + else + i--; + + break; + } + } + _freq_del = 0; //deleted. + } + } - chThdSleepMilliseconds(50); + chThdSleepMilliseconds(60); //This value on 50, when scan resume, it does not stabilize for the next freq in list, it jumped one more ahead. + //With 100 it worked fine but scanner is slow. This may need fixing } } } @@ -107,9 +118,10 @@ void ScannerThread::run() { void ScannerView::handle_retune(uint32_t i) { switch (scan_thread->is_freq_lock()) { - case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING + case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING text_cycle.set( to_string_dec_uint(i + 1,3) ); - if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + current_index = i; //since it is an ongoing scan, this is a new index + if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description break; case 1: big_display.set_style(&style_yellow); //STARTING LOCK FREQ @@ -120,7 +132,7 @@ void ScannerView::handle_retune(uint32_t i) { default: //freq lock is checking the signal, do not update display return; } - big_display.set(frequency_list[i]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) + big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) } void ScannerView::focus() { @@ -164,7 +176,9 @@ ScannerView::ScannerView( &step_mode, &button_manual_scan, &button_pause, - &button_audio_app + &button_audio_app, + &button_remove, + &button_mic_app }); def_step = change_mode(AM); //Start on AM @@ -196,25 +210,47 @@ ScannerView::ScannerView( }; button_pause.on_select = [this](Button&) { - if (scan_thread->is_userpause()) { - timer = wait * 10; //Unlock timer pause on_statistics_update - button_pause.set_text("PAUSE"); //resume scanning (show button for pause) - scan_thread->set_userpause(false); //Signal user's will - } else { + if ( userpause ) + user_resume(); + else { scan_pause(); - scan_thread->set_userpause(true); - button_pause.set_text("RESUME"); //PAUSED, show resume + button_pause.set_text("RESUME"); //PAUSED, show resume + userpause=true; } }; button_audio_app.on_select = [this](Button&) { - if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); + //if (scan_thread->is_scanning()) + // scan_thread->set_scanning(false); scan_thread->stop(); nav_.pop(); nav_.push(); }; + button_mic_app.on_select = [this](Button&) { + //if (scan_thread->is_scanning()) + // scan_thread->set_scanning(false); + scan_thread->stop(); + nav_.pop(); + nav_.push(); + }; + + button_remove.on_select = [this](Button&) { + if (frequency_list.size() > current_index) { + if (scan_thread->is_scanning()) //STOP Scanning if necessary + scan_thread->set_scanning(false); + scan_thread->set_freq_del(frequency_list[current_index]); + description_list.erase(description_list.begin() + current_index); + frequency_list.erase(frequency_list.begin() + current_index); + show_max(); //UPDATE new list size on screen + chThdSleepMilliseconds(300); //"debouncing" pause so user take finger off + timer = wait * 10; //Unlock timer pause on_statistics_update + scan_thread->set_freq_lock(0); //Reset the scanner lock + if ( userpause ) //If user-paused, resume + user_resume(); + } + }; + button_manual_scan.on_select = [this](Button&) { if (!frequency_range.min || !frequency_range.max) { nav_.display_modal("Error", "Both START and END freqs\nneed a value"); @@ -239,21 +275,25 @@ ScannerView::ScannerView( description_list.push_back(""); //If empty, will keep showing the last description frequency+=def_step; } - show_max(); + if ( userpause ) //If user-paused, resume + user_resume(); + big_display.set_style(&style_grey); //Back to grey color start_scan_thread(); //RESTART SCANNER THREAD } }; field_mode.on_change = [this](size_t, OptionsField::value_t v) { if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); // WE STOP SCANNING + scan_thread->set_scanning(false); //STOP SCANNING audio::output::stop(); scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); chThdSleepMilliseconds(50); change_mode(v); + if ( userpause ) //If user-paused, resume + user_resume(); start_scan_thread(); }; @@ -310,7 +350,7 @@ ScannerView::ScannerView( } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - if (!scan_thread->is_userpause()) + if ( !userpause ) { if (timer >= (wait * 10) ) { @@ -349,11 +389,16 @@ void ScannerView::scan_pause() { } void ScannerView::scan_resume() { - if (!scan_thread->is_scanning()) { - audio::output::stop(); - big_display.set_style(&style_grey); //Back to grey color - scan_thread->set_scanning(true); // WE RESCAN - } + audio::output::stop(); + big_display.set_style(&style_grey); //Back to grey color + if (!scan_thread->is_scanning()) + scan_thread->set_scanning(true); // RESUME! +} + +void ScannerView::user_resume() { + timer = wait * 10; //Unlock timer pause on_statistics_update ( will trigger a scan_resume() ) + button_pause.set_text("PAUSE"); //Show button for pause + userpause=false; //Resume scanning } void ScannerView::on_headphone_volume_changed(int32_t v) { diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index f9ee3aa73..010a28fe1 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -26,6 +26,8 @@ #include "ui_font_fixed_8x16.hpp" #include "freqman.hpp" #include "analog_audio_app.hpp" +#include "audio.hpp" +#include "ui_mictx.hpp" #define MAX_DB_ENTRY 500 @@ -46,12 +48,11 @@ public: void set_scanning(const bool v); bool is_scanning(); - void set_userpause(const bool v); - bool is_userpause(); - void set_freq_lock(const uint32_t v); uint32_t is_freq_lock(); + void set_freq_del(const uint32_t v); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -64,8 +65,8 @@ private: Thread* thread { nullptr }; bool _scanning { true }; - bool _userpause { false }; uint32_t _freq_lock { 0 }; + uint32_t _freq_del { 0 }; static msg_t static_fn(void* arg); void run(); }; @@ -97,6 +98,12 @@ public: .foreground = Color::green(), }; + const Style style_red { //erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + std::string title() const override { return "SCANNER"; }; std::vector frequency_list{ }; std::vector description_list { }; @@ -111,6 +118,7 @@ private: void show_max(); void scan_pause(); void scan_resume(); + void user_resume(); void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); @@ -122,13 +130,15 @@ private: uint32_t wait { 0 }; size_t def_step { 0 }; freqman_db database { }; + uint32_t current_index { 0 }; + bool userpause { false }; Labels labels { { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, - { { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, + { { 0 * 8, 27 * 8 }, "MODE:", Color::light_grey() }, + { { 11 * 8, 27 * 8 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -210,7 +220,7 @@ private: }; OptionsField field_mode { - { 5 * 8, 14 * 16 }, + { 5 * 8, 27 * 8 }, 6, { { " AM ", 0 }, @@ -220,7 +230,7 @@ private: }; OptionsField step_mode { - { 17 * 8, 14 * 16 }, + { 17 * 8, 27 * 8 }, 12, { { "5Khz (SA AM)", 5000 }, @@ -237,14 +247,24 @@ private: }; Button button_pause { - { 12, 17 * 16, 96, 24 }, + { 12, 15 * 16, 96, 24 }, "PAUSE" }; Button button_audio_app { - { 124, 17 * 16, 96, 24 }, + { 124, 15 * 16, 96, 24 }, "AUDIO APP" }; + + Button button_remove { + { 12, 17 * 16, 96, 24 }, + "DEL FREQ" + }; + + Button button_mic_app { + { 124, 17 * 16, 96, 24 }, + "MIC TX APP" + }; std::unique_ptr scan_thread { }; From ee67f74fa7e14225e4535846f9856b6a49b737ca Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Tue, 28 Jul 2020 21:05:10 -0300 Subject: [PATCH 5/6] Added two new buttons and other enhancements Added buttons for: Change scanning direction (ascending / descending) Saving current freq into the SCANNER.TXT file (Please notice that, on the other hand -for safety issues- the DEL FQ button, deletes the frequency only from the temp memory on the actual scanning session, but does NOT erases the freq. inside the SCANNER.TXT) Also there are other bug fixes and scanning speed enhancements. --- firmware/application/apps/ui_scanner.cpp | 132 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 43 +++++--- 2 files changed, 125 insertions(+), 50 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index eafdf4d13..63b5f9bb1 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -21,9 +21,6 @@ */ #include "ui_scanner.hpp" -#include "portapack_persistent_memory.hpp" -#include "baseband_api.hpp" -#include "string_format.hpp" using namespace portapack; @@ -68,6 +65,12 @@ void ScannerThread::set_freq_del(const uint32_t v) { _freq_del = v; } +void ScannerThread::change_scanning_direction() { + _fwd = !_fwd; + chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction + +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -78,39 +81,47 @@ void ScannerThread::run() { if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; uint32_t frequency_index = frequency_list_.size(); + bool restart_scan = false; //Flag whenever scanning is restarting after a pause while( !chThdShouldTerminate() ) { - if (_scanning) - { //Scanning + if (_scanning) { //Scanning if (_freq_lock == 0) { //normal scanning (not performing freq_lock) - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune - } + if (!restart_scan) { //looping at full speed + if (_fwd) { //forward + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + + } else { //reverse + if (frequency_index < 1) + frequency_index = frequency_list_.size(); + frequency_index--; + } + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune + } + else + restart_scan=false; //Effectively skipping first retuning, giving system time + } message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); } - else - { //If not scanning, check for user asking to delete a freq: - if (_freq_del != 0) { //There is a frequency to delete + else { //NOT scanning + restart_scan=true; //Flag the need for skipping a cycle when restarting scan + if (_freq_del != 0) { //There is a frequency to delete for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete if (frequency_list_[i] == _freq_del) - { //found: Erase it + { //found: Erase it frequency_list_.erase(frequency_list_.begin() + i); - if (i==0) //set scan index one place back to compensate + if (i==0) //set scan index one place back to compensate i=frequency_list_.size(); else i--; - break; } } - _freq_del = 0; //deleted. + _freq_del = 0; //deleted. } - } - chThdSleepMilliseconds(60); //This value on 50, when scan resume, it does not stabilize for the next freq in list, it jumped one more ahead. - //With 100 it worked fine but scanner is slow. This may need fixing + chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq } } } @@ -123,11 +134,11 @@ void ScannerView::handle_retune(uint32_t i) { current_index = i; //since it is an ongoing scan, this is a new index if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description break; - case 1: - big_display.set_style(&style_yellow); //STARTING LOCK FREQ + case 1: //STARTING LOCK FREQ + big_display.set_style(&style_yellow); break; - case MAX_FREQ_LOCK: - big_display.set_style(&style_green); //FREQ LOCK FULL, GREEN! + case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update() + big_display.set_style(&style_green); break; default: //freq lock is checking the signal, do not update display return; @@ -146,8 +157,10 @@ ScannerView::~ScannerView() { } void ScannerView::show_max() { //show total number of freqs to scan - if (frequency_list.size() == MAX_DB_ENTRY) + if (frequency_list.size() == MAX_DB_ENTRY) { + text_max.set_style(&style_red); text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); + } else text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); } @@ -176,15 +189,18 @@ ScannerView::ScannerView( &step_mode, &button_manual_scan, &button_pause, + &button_dir, &button_audio_app, - &button_remove, - &button_mic_app + &button_mic_app, + &button_add, + &button_remove + }); def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - big_display.set_style(&style_grey); //Start with gray color + //big_display.set_style(&style_grey); //Start with gray color //HELPER: Pre-setting a manual range, based on stored frequency rf::Frequency stored_freq = persistent_memory::tuned_frequency(); @@ -220,16 +236,12 @@ ScannerView::ScannerView( }; button_audio_app.on_select = [this](Button&) { - //if (scan_thread->is_scanning()) - // scan_thread->set_scanning(false); scan_thread->stop(); nav_.pop(); nav_.push(); }; - button_mic_app.on_select = [this](Button&) { - //if (scan_thread->is_scanning()) - // scan_thread->set_scanning(false); + button_mic_app.on_select = [this](Button&) { scan_thread->stop(); nav_.pop(); nav_.push(); @@ -290,13 +302,59 @@ ScannerView::ScannerView( scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); - chThdSleepMilliseconds(50); change_mode(v); if ( userpause ) //If user-paused, resume user_resume(); start_scan_thread(); }; + button_dir.on_select = [this](Button&) { + scan_thread->change_scanning_direction(); + if ( userpause ) //If user-paused, resume + user_resume(); + big_display.set_style(&style_grey); //Back to grey color + }; + + button_add.on_select = [this](Button&) { //frequency_list[current_index] + File scanner_file; + auto result = scanner_file.open("FREQMAN/SCANNER.TXT"); //First search if freq is already in txt + if (!result.is_valid()) { + std::string frequency_to_add = "f=" + + to_string_dec_uint(frequency_list[current_index] / 1000) + + to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0'); + char one_char[1]; //Read it char by char + std::string line; //and put read line in here + bool found=false; + for (size_t pointer=0; pointer < scanner_file.size();pointer++) { + + scanner_file.seek(pointer); + scanner_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 + if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { + found=true; + break; + } + line.clear(); //Ready for next textline + } + } + if (found) { + nav_.display_modal("Error", "Frequency already exists"); + big_display.set(frequency_list[current_index]); //After showing an error + } + else { + auto result = scanner_file.append("FREQMAN/SCANNER.TXT"); //Second: append if it is not there + scanner_file.write_line(frequency_to_add + ",d=ADD FQ"); + } + } else + { + nav_.display_modal("Error", "Cannot open SCANNER.TXT\nfor appending freq."); + big_display.set(frequency_list[current_index]); //After showing an error + } + }; + //PRE-CONFIGURATION: field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); @@ -350,11 +408,11 @@ ScannerView::ScannerView( } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - if ( !userpause ) + if ( !userpause ) //Scanning not user-paused { if (timer >= (wait * 10) ) { - timer=0; + timer = 0; scan_resume(); } else if (!timer) @@ -396,7 +454,7 @@ void ScannerView::scan_resume() { } void ScannerView::user_resume() { - timer = wait * 10; //Unlock timer pause on_statistics_update ( will trigger a scan_resume() ) + timer = wait * 10; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq. button_pause.set_text("PAUSE"); //Show button for pause userpause=false; //Resume scanning } diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 010a28fe1..35598e12e 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -28,6 +28,10 @@ #include "analog_audio_app.hpp" #include "audio.hpp" #include "ui_mictx.hpp" +#include "portapack_persistent_memory.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "file.hpp" #define MAX_DB_ENTRY 500 @@ -53,6 +57,8 @@ public: void set_freq_del(const uint32_t v); + void change_scanning_direction(); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -65,6 +71,7 @@ private: Thread* thread { nullptr }; bool _scanning { true }; + bool _fwd { true }; uint32_t _freq_lock { 0 }; uint32_t _freq_del { 0 }; static msg_t static_fn(void* arg); @@ -137,8 +144,8 @@ private: { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8, 27 * 8 }, "MODE:", Color::light_grey() }, - { { 11 * 8, 27 * 8 }, "STEP:", Color::light_grey() }, + { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() }, + { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -220,7 +227,7 @@ private: }; OptionsField field_mode { - { 5 * 8, 27 * 8 }, + { 5 * 8, (26 * 8) + 4 }, 6, { { " AM ", 0 }, @@ -230,7 +237,7 @@ private: }; OptionsField step_mode { - { 17 * 8, 27 * 8 }, + { 17 * 8, (26 * 8) + 4 }, 12, { { "5Khz (SA AM)", 5000 }, @@ -247,23 +254,33 @@ private: }; Button button_pause { - { 12, 15 * 16, 96, 24 }, + { 0, (15 * 16) - 4, 72, 28 }, "PAUSE" }; - Button button_audio_app { - { 124, 15 * 16, 96, 24 }, - "AUDIO APP" + Button button_dir { + { 0, (35 * 8) - 4, 72, 28 }, + "FW> scan_thread { }; From 1b2c68b3c0099f09dcd9fc374101bc2f98f2749a Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Sat, 1 Aug 2020 00:58:34 -0300 Subject: [PATCH 6/6] New squelch behavior and fixes Squelch value now goes from -90 to +20 and it's directly compared against the max_db parameter returned from each freq scanned by the radio subsystem, with no adjusts or manipulation (you adjust the number as will be used). Less squelch means weaker signals will trigger it. (as expected). There was a tiny cosmetic bug when you deleted a frequency from the scanning memory: The description was not erased from screen and you could see it while the scan did not resume. There was another bug on the pause button: If you asked for another manual scan range when paused, the button kept the text "RESUME" (its text was not reset to "PAUSE" again). --- firmware/application/apps/ui_scanner.cpp | 28 ++++++++++-------------- firmware/application/apps/ui_scanner.hpp | 6 ++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 63b5f9bb1..65c4788df 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -105,7 +105,6 @@ void ScannerThread::run() { EventDispatcher::send_message(message); } else { //NOT scanning - restart_scan=true; //Flag the need for skipping a cycle when restarting scan if (_freq_del != 0) { //There is a frequency to delete for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete if (frequency_list_[i] == _freq_del) @@ -120,6 +119,9 @@ void ScannerThread::run() { } _freq_del = 0; //deleted. } + else { + restart_scan=true; //Flag the need for skipping a cycle when restarting scan + } } chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq } @@ -161,8 +163,10 @@ void ScannerView::show_max() { //show total number of freqs to scan text_max.set_style(&style_red); text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); } - else + else { + text_max.set_style(&style_grey); text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); + } } ScannerView::ScannerView( @@ -200,8 +204,6 @@ ScannerView::ScannerView( def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - //big_display.set_style(&style_grey); //Start with gray color - //HELPER: Pre-setting a manual range, based on stored frequency rf::Frequency stored_freq = persistent_memory::tuned_frequency(); frequency_range.min = stored_freq - 1000000; @@ -255,8 +257,7 @@ ScannerView::ScannerView( description_list.erase(description_list.begin() + current_index); frequency_list.erase(frequency_list.begin() + current_index); show_max(); //UPDATE new list size on screen - chThdSleepMilliseconds(300); //"debouncing" pause so user take finger off - timer = wait * 10; //Unlock timer pause on_statistics_update + desc_cycle.set(" "); //Clean up description (cosmetic detail) scan_thread->set_freq_lock(0); //Reset the scanner lock if ( userpause ) //If user-paused, resume user_resume(); @@ -296,16 +297,12 @@ ScannerView::ScannerView( }; field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); //STOP SCANNING - audio::output::stop(); - scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); change_mode(v); - if ( userpause ) //If user-paused, resume - user_resume(); - start_scan_thread(); + if ( !scan_thread->is_scanning() ) //for some motive, audio output gets stopped. + audio::output::start(); //So if scan was stopped we resume audio + receiver_model.enable(); }; button_dir.on_select = [this](Button&) { @@ -357,7 +354,7 @@ ScannerView::ScannerView( //PRE-CONFIGURATION: field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); - field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); + field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(-10); field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; // LEARN FREQUENCIES @@ -417,7 +414,7 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { } else if (!timer) { - if (statistics.max_db > -squelch) { //There is something on the air... + if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch) if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached scan_pause(); timer++; @@ -511,7 +508,6 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre } return mod_step[new_mod]; - } void ScannerView::start_scan_thread() { diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 35598e12e..d2befbfe9 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -142,7 +142,7 @@ private: Labels labels { { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, - { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, + { { 0 * 8, 1* 16 }, "BW: SQUELCH: db WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() }, { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() }, @@ -176,8 +176,8 @@ private: NumberField field_squelch { { 15 * 8, 1 * 16 }, - 2, - { 0, 99 }, + 3, + { -90, 20 }, 1, ' ', };