From a27881ecd6e1de098440b489eb48231545aaf438 Mon Sep 17 00:00:00 2001 From: gullradriel <3157857+gullradriel@users.noreply.github.com> Date: Sun, 14 May 2023 21:20:10 +0200 Subject: [PATCH] Recon event nothread (#984) * deleted unreliable Thread system and use exclusively the statistics events instead * reducing used variables, reducing number of functions, revamped main drawing and locking system * max speed is fixed at a maximum of 10 scans/secs due to statistics not coming quicker than that --- firmware/application/apps/ui_recon.cpp | 3321 +++++++---------- firmware/application/apps/ui_recon.hpp | 148 +- .../application/apps/ui_recon_settings.cpp | 31 +- .../application/apps/ui_recon_settings.hpp | 55 +- 4 files changed, 1540 insertions(+), 2015 deletions(-) diff --git a/firmware/application/apps/ui_recon.cpp b/firmware/application/apps/ui_recon.cpp index 0764435b..751c636e 100644 --- a/firmware/application/apps/ui_recon.cpp +++ b/firmware/application/apps/ui_recon.cpp @@ -22,1896 +22,1469 @@ #include "ui_recon.hpp" #include "ui_fileman.hpp" -#include "ui_recon_settings.hpp" #include "file.hpp" -// Id's for messages between ReconThread and ReconView -#define MSG_RECON_PAUSE 9999 // for handle_retune to know that recon thread triggered a pause. f is not important with that message -#define MSG_RECON_SET_MODULATION 10000 // for handle_retune to know that recon thread triggered a modulation change. f is the index of the modulation -#define MSG_RECON_SET_BANDWIDTH 20000 // for handle_retune to know that recon thread triggered a bandwidth change. f is the new bandwidth value index for current modulation -#define MSG_RECON_SET_STEP 30000 // for handle_retune to know that recon thread triggered a bandwidth change. f is the new bandwidth value index for current modulation -#define MSG_RECON_SET_RECEIVER_BANDWIDTH 40000 // for handle_retune to know that recon thread triggered a receiver bandwidth change. f is the new bandwidth in hz -#define MSG_RECON_SET_RECEIVER_SAMPLERATE 50000 // for handle_retune to know that recon thread triggered a receiver samplerate change. f is the new samplerate in hz/s - using namespace portapack; using portapack::memory::map::backup_ram; namespace ui { - ReconThread::ReconThread( freqman_db *database ) : frequency_list_ { *database } { - thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReconThread::static_fn, this ); - } + void ReconView::audio_output_start() + { + audio::output::start(); + this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); + } - ReconThread::~ReconThread() { - stop(); - } + bool ReconView::check_sd_card() + { + return (sd_card::status() == sd_card::Status::Mounted) ? true : false; + } - void ReconThread::stop() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } - } + void ReconView::recon_redraw() { + static int32_t last_db = 999999 ; + static uint32_t last_nb_match = 999999 ; + static uint32_t last_freq_lock = 999999 ; + static size_t last_list_size = 0 ; + static int8_t last_rssi_min = -127 ; + static int8_t last_rssi_med = -127 ; + static int8_t last_rssi_max = -127 ; - void ReconThread::set_recon(const bool v) { - _recon = v; - } + if( last_rssi_min != rssi.get_min() || last_rssi_med != rssi.get_avg() || last_rssi_max != rssi.get_max() ) + { + last_rssi_min = rssi.get_min(); + last_rssi_med = rssi.get_avg(); + last_rssi_max = rssi.get_max(); + freq_stats.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+" db" ); + } - void ReconThread::set_freq_delete(const bool v) { - _freq_delete = v; - } + if( last_entry . frequency_a != freq ) + { + last_entry . frequency_a = freq ; + big_display.set( "FREQ: "+to_string_short_freq( freq )+" MHz" ); + } - void ReconThread::set_stepper( const int64_t v){ - _stepper = v; - } + if( last_db != db || last_list_size != frequency_list.size() || last_freq_lock != freq_lock || last_nb_match != recon_lock_nb_match ) + { + last_freq_lock = freq ; + last_list_size = frequency_list.size(); + last_db = db ; + last_nb_match = recon_lock_nb_match ; + text_max.set( "/" + to_string_dec_uint( frequency_list.size() ) + " " + to_string_dec_int( db ) + " db " + to_string_dec_uint( freq_lock ) + "/" + to_string_dec_uint( recon_lock_nb_match ) ); + if( freq_lock == 0 ) { + //NO FREQ LOCK, ONGOING STANDARD SCANNING + big_display.set_style(&style_white); + if( !userpause ) + button_pause.set_text(""); + else + button_pause.set_text(""); + } + else if( freq_lock == 1 && recon_lock_nb_match != 1 ) + { + //STARTING LOCK FREQ + big_display.set_style(&style_yellow); + button_pause.set_text(""); + } + else if( freq_lock >= recon_lock_nb_match ) + { + big_display.set_style( &style_green); + button_pause.set_text(""); - void ReconThread::set_index_stepper( const int64_t v){ - _index_stepper = v; - } + //FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() + if( (!scanner_mode) && autosave && frequency_list.size() > 0 ) { + File recon_file; + std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; + std::string frequency_to_add ; - void ReconThread::set_lock_duration( const uint32_t v ){ - _lock_duration = v; - } + freqman_entry entry = frequency_list[ current_index ] ; + entry . frequency_a = freq ; + entry . frequency_b = 0 ; + entry . modulation = last_entry.modulation ; + entry . bandwidth = last_entry.bandwidth ; + entry . type = SINGLE ; - uint32_t ReconThread::get_lock_duration() { - return _lock_duration ; - } + get_freq_string( entry , frequency_to_add ); - void ReconThread::set_lock_nb_match( const uint32_t v ){ - _lock_nb_match = v; - } - - uint32_t ReconThread::get_lock_nb_match() { - return _lock_nb_match ; - } - - bool ReconThread::is_recon() { - return _recon; - } - - void ReconThread::set_freq_lock(const int32_t v) { - _freq_lock = v; - } - int32_t ReconThread::is_freq_lock() { - return _freq_lock; - } - int64_t ReconThread::get_current_freq() { - return freq ; - } - - void ReconThread::change_recon_direction() { - _fwd = !_fwd; - } - - bool ReconThread::get_recon_direction() { - return _fwd ; - } - void ReconThread::set_recon_direction( const bool v) { - _fwd = v ; - } - - void ReconThread::set_continuous(const bool v) { - _continuous = v; - } - - freqman_index_t ReconThread::get_current_modulation() { - return last_entry.modulation ; - } - - freqman_index_t ReconThread::get_current_bandwidth() { - return last_entry.bandwidth ; - } - - - void ReconThread::set_default_modulation( freqman_index_t index ) { - def_modulation = index ; - } - - void ReconThread::set_default_bandwidth( freqman_index_t index ) { - def_bandwidth = index ; - } - void ReconThread::set_default_step( freqman_index_t index ) { - def_step = index ; - } - void ReconThread::set_freq_index( int16_t index ) { - frequency_index = index ; - } - int16_t ReconThread::get_freq_index() { - return frequency_index ; - } - - msg_t ReconThread::static_fn( void* arg ) { - auto obj = static_cast(arg); - obj->run(); - return 0; - } - - void ReconThread::run() - { - //IF THERE IS A FREQUENCY LIST ... - if (frequency_list_.size() > 0 ) - { - int64_t minfreq = 0 ; - int64_t maxfreq = 0 ; - bool has_looped = false ; - RetuneMessage message { }; - - if( frequency_list_[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list_[ 0 ] . step ); - else - step = freqman_entry_get_step_value( def_step ); - - switch( frequency_list_[ 0 ] . type ){ - case SINGLE: - freq = frequency_list_[ 0 ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list_[ 0 ] . frequency_a ; - maxfreq = frequency_list_[ 0 ] . frequency_b ; - if( _fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - if( frequency_list_[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list_[ 0 ] . step ); - break; - case HAMRADIO: - minfreq = frequency_list_[ 0 ] . frequency_a ; - maxfreq = frequency_list_[ 0 ] . frequency_b ; - if( _fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - break; - default: - break; - } - last_entry . modulation = -1 ; - last_entry . bandwidth = -1 ; - last_entry . step = -1 ; - bool restart_recon = false; //Flag whenever scanning is restarting after a pause - - while( !chThdShouldTerminate() && frequency_list_.size() > 0 ) - { - uint32_t remaining_sleep = _lock_duration ; - if( !_freq_delete ) - { - has_looped = false ; - if( last_entry . frequency_a != freq || entry_has_changed ) - { - last_entry . frequency_a = freq ; - receiver_model.set_tuning_frequency( freq ); // Retune - message.freq = freq ; - message.range = frequency_index ; - EventDispatcher::send_message(message); - } - entry_has_changed = false ; - - // Set modulation if any - if( last_entry . modulation != frequency_list_[ frequency_index ] . modulation && frequency_list_[ frequency_index ] . modulation >= 0 ) - { - last_entry . modulation = frequency_list_[ frequency_index ]. modulation; - message.freq = last_entry . modulation ; - message.range = MSG_RECON_SET_MODULATION ; - EventDispatcher::send_message(message); - } - // Set bandwidth if any - if( last_entry . bandwidth != frequency_list_[ frequency_index ] . bandwidth && frequency_list_[ frequency_index ] . bandwidth >= 0 ) - { - last_entry . bandwidth = frequency_list_[ frequency_index ]. bandwidth; - message.freq = last_entry . bandwidth ; - message.range = MSG_RECON_SET_BANDWIDTH ; - EventDispatcher::send_message(message); - } - if( last_entry . step != frequency_list_[ frequency_index ] . step && frequency_list_[ frequency_index ] . step >= 0 ) - { - last_entry . step = frequency_list_[ frequency_index ]. step ; - message.freq = last_entry . step ; - message.range = MSG_RECON_SET_STEP ; - EventDispatcher::send_message(message); - step = freqman_entry_get_step_value( last_entry . step ); - } - - if( _recon || _stepper != 0 || _index_stepper != 0 ) - { - if( _freq_lock == 0 || _stepper != 0 || _index_stepper != 0 ) //normal recon (not performing freq_lock) - { - if( !restart_recon || _stepper != 0 || _index_stepper != 0 ) - { - - if( _index_stepper == 0 ) - { - /* we are doing a range */ - if( frequency_list_[ frequency_index ] . type == RANGE ) { - if ( ( _fwd && _stepper == 0 ) || _stepper > 0 ) { - //forward - freq += step ; - // if bigger than range max - if (freq > maxfreq ) { - // when going forward we already know that we can skip a whole range => two values in the list - frequency_index ++ ; - entry_has_changed = true ; - // looping - if( (uint32_t)frequency_index >= frequency_list_.size() ) - { - has_looped = true ; - frequency_index = 0 ; - } - } - } - else if( (!_fwd && _stepper == 0 ) || _stepper < 0 ) { - //reverse - freq -= step ; - // if lower than range min - if (freq < minfreq ) { - // when back we have to check one step at a time - frequency_index -- ; - entry_has_changed = true ; - // looping - if( frequency_index < 0 ) - { - has_looped = true ; - frequency_index = frequency_list_.size() - 1 ; - } - } - } - } - else if( frequency_list_[ frequency_index ] . type == SINGLE ) { - if ( (_fwd && _stepper == 0 ) || _stepper > 0 ) { //forward - frequency_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)frequency_index >= frequency_list_.size() ) - { - has_looped = true ; - frequency_index = 0 ; - } - } - else if( (!_fwd && _stepper == 0 ) || _stepper < 0 ) { - //reverse - frequency_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( frequency_index < 0 ) - { - has_looped = true ; - frequency_index = frequency_list_.size() - 1 ; - } - } - } - else if( frequency_list_[ frequency_index ] . type == HAMRADIO ) - { - if ( (_fwd && _stepper == 0 ) || _stepper > 0 ) { //forward - if( ( minfreq != maxfreq ) && freq == minfreq ) - { - freq = maxfreq ; - } - else - { - frequency_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)frequency_index >= frequency_list_.size() ) - { - has_looped = true ; - frequency_index = 0 ; - } - } - } - else if( (!_fwd && _stepper == 0 ) || _stepper < 0 ) { - //reverse - if( ( minfreq != maxfreq ) && freq == maxfreq ) - { - freq = minfreq ; - } - else - { - frequency_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( frequency_index < 0 ) - { - has_looped = true ; - frequency_index = frequency_list_.size() - 1 ; - } - } - } - } - // set index to boundary if !continuous - if( has_looped && !_continuous ) - { - entry_has_changed = true ; - /* prepare values for the next run, when user will resume */ - if( ( _fwd && _stepper == 0 ) || _stepper > 0 ) - { - frequency_index = 0 ; - } - else if( ( !_fwd && _stepper == 0 ) || _stepper < 0 ) - { - frequency_index = frequency_list_.size() - 1 ; - } - } - } - else - { - if( _index_stepper > 0 ) - frequency_index ++ ; - if( _index_stepper < 0 ) - frequency_index -- ; - if( frequency_index < 0 ) - frequency_index = frequency_list_.size() - 1 ; - if( (unsigned)frequency_index >= frequency_list_.size() ) - frequency_index = 0 ; - entry_has_changed = true ; - } - - // reload entry if changed - if( entry_has_changed ){ - switch( frequency_list_[ frequency_index ] . type ){ - case SINGLE: - freq = frequency_list_[ frequency_index ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list_[ frequency_index ] . frequency_a ; - maxfreq = frequency_list_[ frequency_index ] . frequency_b ; - if( ( _fwd && _stepper == 0 ) || _stepper > 0 || _index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !_fwd && _stepper == 0 ) || _stepper < 0 || _index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - case HAMRADIO: - minfreq = frequency_list_[ frequency_index ] . frequency_a ; - maxfreq = frequency_list_[ frequency_index ] . frequency_b ; - if( ( _fwd && _stepper == 0 ) || _stepper > 0 || _index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !_fwd && _stepper == 0 ) || _stepper < 0 || _index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - default: - break; - } - } - // send a pause message with the right freq - if( has_looped && !_continuous ) - { - // signal pause to handle_retune - message.freq = freq ; - message.range = MSG_RECON_PAUSE ; - EventDispatcher::send_message(message); - receiver_model.set_tuning_frequency( freq ); // Retune to actual freq - } - if( _stepper < 0 ) _stepper ++ ; - if( _stepper > 0 ) _stepper -- ; - if( _index_stepper < 0 ) _index_stepper ++ ; - if( _index_stepper > 0 ) _index_stepper -- ; - } // if( !restart_recon || _stepper != 0 || _index_stepper != 0 ) - else - { - restart_recon = false ; - } - } // if( _freq_lock == 0 || _stepper != 0 || _index_stepper != 0 ) - - // this while loop is needed to stabilize consecutive match without skipping - // the little the rest, the more reactive and more CPU hog - // the bigger the rest, less CPU and more delay when switching. I choose 5 msecs. - while( _freq_lock != -1 && _freq_lock < (int32_t)_lock_nb_match && !chThdShouldTerminate() ) - { - chThdSleepMilliseconds( 5 ); - if( remaining_sleep >= 5 ) - remaining_sleep -= 5 ; - else - remaining_sleep = 0 ; // leave some process time + auto result = recon_file.open(freq_file_path); //First recon if freq is already in txt + if (!result.is_valid()) { + 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 < recon_file.size();pointer++) { + recon_file.seek(pointer); + recon_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( _freq_lock == -1 ) - _freq_lock = 0 ; - } // if( _recon || _stepper != 0 || _index_stepper != 0 ) - } // if( !freq_delete ) - if( remaining_sleep ) - chThdSleepMilliseconds( remaining_sleep ); //Needed to (eventually) stabilize the receiver into new freq - } //while( !chThdShouldTerminate() && frequency_list_.size() > 0 ) - }//if (frequency_list_.size() > 0 ) - } //ReconThread::run - - bool ReconView::check_sd_card() - { - return (sd_card::status() == sd_card::Status::Mounted) ? true : false; - } - - void ReconView::set_display_freq( int64_t freq ) - { - big_display.set( "FREQ: "+to_string_short_freq( freq )+" MHz" ); - } - - void ReconView::handle_retune( int64_t freq , uint32_t index ) { - - static int64_t last_freq = 0 ; - static uint32_t last_index = 999999 ; - - switch( index ) { - case MSG_RECON_PAUSE: - user_pause(); - if( update_ranges && current_index < frequency_list.size() ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - return ; - break ; - case MSG_RECON_SET_MODULATION : - receiver_model.disable(); - baseband::shutdown(); - change_mode( freq ); - if ( !recon_thread->is_recon() ) // for some motive, audio output gets stopped. - audio::output::start(); // so if recon was stopped we resume audio - receiver_model.enable(); - field_mode.set_selected_index( freq ); - return ; - break ; - case MSG_RECON_SET_BANDWIDTH: - switch( recon_thread->get_current_modulation() ) - { - case AM_MODULATION: - receiver_model.set_am_configuration( freq ); - break ; - case NFM_MODULATION: - receiver_model.set_nbfm_configuration( freq ); - break ; - case WFM_MODULATION: - receiver_model.set_wfm_configuration( freq ); - default: - break ; - } - field_bw.set_selected_index( freq ); - return ; - break ; - case MSG_RECON_SET_STEP: - step_mode.set_selected_index( freq ); - return ; - break ; - } - - if( last_index != index ) - { - last_index = index ; - current_index = index ; - if( frequency_list.size() && index < frequency_list.size() && frequency_list[ index ] . type == RANGE ) - { - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ index ] . frequency_a ) ); - frequency_range.min = frequency_list[ index ] . frequency_a ; - if( frequency_list[ index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ index ] . frequency_b ) ); - frequency_range.max = frequency_list[ index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ index ] . frequency_a ) ); - frequency_range.max = frequency_list[ index ] . frequency_a ; - } - } - } - text_cycle.set_text( to_string_dec_uint( index + 1 , 3 ) ); - if(frequency_list[index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); //Show new description - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); //Show new description - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); //Show new description - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); //Show new description - } - } - - uint32_t freq_lock = recon_thread->is_freq_lock(); - if( freq_lock == 0 ) { - //NO FREQ LOCK, ONGOING STANDARD SCANNING - big_display.set_style(&style_white); - if( !userpause ) - button_pause.set_text(""); - else - button_pause.set_text(""); - } - else if( freq_lock == 1 && recon_lock_nb_match != 1 ) - { - //STARTING LOCK FREQ - big_display.set_style(&style_yellow); - button_pause.set_text(""); - } - else if( index < 1000 && freq_lock >= recon_thread -> get_lock_nb_match() ) - { - big_display.set_style( &style_green); - button_pause.set_text(""); - - //FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() - if( (!scanner_mode) && autosave && last_freq != freq ) { - File recon_file; - std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = recon_thread->get_current_freq(); - entry . frequency_b = recon_thread->get_current_freq(); - entry . modulation = recon_thread->get_current_modulation(); - entry . bandwidth = recon_thread->get_current_bandwidth(); - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - auto result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - 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 < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_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) { - result = recon_file.append(freq_file_path); //Second: append if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - } - else { - result = recon_file.create( freq_file_path ); //First freq if no output file - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - } - last_freq = freq ; - } - set_display_freq( freq ); //UPDATE the big Freq after 0, 1 or recon_lock_nb_match (at least, for color synching) - } - - - void ReconView::focus() { - button_pause.focus(); - } - - ReconView::~ReconView() { - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , field_volume.value() ); - - // save app settings - settings.save("recon", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); - } - - void ReconView::show_max( bool refresh_display ) { //show total number of freqs to recon - static int32_t last_db = 999999 ; - static int32_t last_nb_match = 999999 ; - static int32_t last_timer = -1 ; - if( recon_thread ) - { - int32_t nb_match = recon_thread->is_freq_lock(); - if( nb_match == -1 ) - nb_match = 0 ; - if( last_db != db ) - { - last_db = db ; - refresh_display = true ; - } - if( last_nb_match != nb_match ) - { - last_nb_match = nb_match ; - refresh_display = true ; - } - if( last_timer != timer ) - { - last_timer = timer ; - refresh_display = true ; - } - if( refresh_display ) - { - text_max.set( "/" + to_string_dec_uint( frequency_list.size() ) + " " + to_string_dec_int( db ) + " db " + to_string_dec_uint( nb_match ) + "/" + to_string_dec_uint( recon_thread->get_lock_nb_match() ) ); - freq_stats.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+" db" ); - text_timer.set( "TIMER: " + to_string_dec_int( timer ) ); - } - } - } - - ReconView::ReconView( NavigationView& nav) : nav_ { nav } { - - add_children( { - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &field_volume, - &field_bw, - &field_squelch, - &field_wait, - &field_lock_wait, - &button_recon_setup, - &button_scanner_mode, - &button_looking_glass, - &file_name, - &rssi, - &text_cycle, - &text_max, - &desc_cycle, - &big_display, - &freq_stats, - &text_timer, - &text_ctcss, - &button_manual_start, - &button_manual_end, - &button_manual_recon, - &field_mode, - &step_mode, - &button_pause, - &button_audio_app, - &button_add, - &button_dir, - &button_restart, - &button_mic_app, - &button_remove - } ); - - // Recon directory - if( check_sd_card() ) { // Check to see if SD Card is mounted - make_new_directory( u"/RECON" ); - sd_card_mounted = true ; - } - def_step = 0 ; - //HELPER: Pre-setting a manual range, based on stored frequency - rf::Frequency stored_freq = persistent_memory::tuned_frequency(); - receiver_model.set_tuning_frequency( stored_freq ); - if( stored_freq - OneMHz > 0 ) - frequency_range.min = stored_freq - OneMHz ; - else - frequency_range.min = 0 ; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - if( stored_freq + OneMHz < MAX_UFREQ ) - frequency_range.max = stored_freq + OneMHz ; - else - frequency_range.max = MAX_UFREQ ; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - // Loading settings - autostart = persistent_memory::recon_autostart_recon(); - autosave = persistent_memory::recon_autosave_freqs(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - - field_volume.set_value( volume ); - if( sd_card_mounted ) - { - // load auto common app settings - auto rc = settings.load("recon", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - receiver_model.set_rf_amp(app_settings.rx_amp); - } - } - - button_manual_start.on_select = [this, &nav](ButtonWithEncoder& 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](ButtonWithEncoder& 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)); - }; - }; - - text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { - if( frequency_list.size() > 0 ) - { - auto new_view = nav_.push(current_index); - new_view->on_changed = [this, &button](rf::Frequency f) { - f = f / OneMHz ; - if( f >= 1 && f <= frequency_list.size() ) - { - recon_thread-> set_index_stepper( f - 1 - current_index ); - recon_thread->set_freq_lock( -1 ); - } - }; - } - }; - - button_manual_start.on_change = [this]() { - frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.min < 0 ) - { - frequency_range.min = 0 ; - } - if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - } - if( frequency_range.min > (frequency_range.max - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.max = frequency_range.min + freqman_entry_get_step_value( def_step ); - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - frequency_range.max = MAX_UFREQ ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_start.set_encoder_delta( 0 ); - }; - - button_manual_end.on_change = [this]() { - frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ); - } - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.max = MAX_UFREQ ; - } - if( frequency_range.max < (frequency_range.min + freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = frequency_range.max - freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.min = 1 ; - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ) ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_end.set_encoder_delta( 0 ); - }; - - text_cycle.on_change = [this]() { - if( frequency_list.size() > 0 ) - { - if( text_cycle.get_encoder_delta() > 0 ) - { - fwd = true ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "FW>" ); - recon_thread-> set_index_stepper( 1 ); - recon_thread->set_freq_lock( -1 ); - } - else if( text_cycle.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( " set_index_stepper( -1 ); - recon_thread->set_freq_lock( -1 ); - } - } - text_cycle.set_encoder_delta( 0 ); - }; - - button_pause.on_select = [this](ButtonWithEncoder&) { - if( recon_thread && frequency_list.size() > 0 ) - { - if( continuous_lock ) - { - if( fwd ) - { - recon_thread-> set_stepper( 1 ); - } - else - { - recon_thread-> set_stepper( -1 ); - } - timer = 0 ; - recon_resume(); - button_pause.set_text(""); //Show button for non continuous stop - continuous_lock = false ; - } - else - { - if( userpause ) - { - user_resume(); - } - else - { - user_pause(); - - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - } - } - }; - - button_pause.on_change = [this]() { - if( recon_thread && frequency_list.size() > 0 ) - { - if( button_pause.get_encoder_delta() > 0 ) - { - fwd = true ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "FW>" ); - recon_thread-> set_stepper( 1 ); - recon_thread->set_freq_lock( -1 ); - } - else if( button_pause.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( " set_stepper( -1 ); - recon_thread->set_freq_lock( -1 ); - } - } - button_pause.set_encoder_delta( 0 ); - }; - - button_audio_app.on_select = [this](Button&) { - recon_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - button_looking_glass.on_select = [this](Button&) { - recon_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - - rssi.set_focusable(true); - rssi.set_peak( true , 500 ); - rssi.on_select = [this](RSSI&) { - recon_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - button_mic_app.on_select = [this](Button&) { - recon_thread->stop(); - nav_.pop(); - nav_.push(); - }; - - button_remove.on_select = [this](ButtonWithEncoder&) { - bool previous_is_recon = false ; - bool previous_userpause = userpause ; - if( recon_thread ) - { - previous_is_recon = recon_thread->is_recon(); - recon_thread->set_recon(false); - recon_thread->set_freq_delete(true); - chThdSleepMilliseconds( recon_lock_duration ); // give some time to Thread::Run to pause - } - if( scanner_mode ) - { - if(frequency_list.size() > 0 ) - { - if( current_index >= frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - frequency_list.erase( frequency_list.begin()+ current_index ); - if( current_index >= frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - if( frequency_list.size() > 0 ) - { - recon_thread->set_freq_index( current_index ); - if(frequency_list[current_index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); - show_max( true ); - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - - File freqman_file; - - std::string freq_file_path = "FREQMAN/" + output_file + ".TXT"; - - delete_file( freq_file_path ); - - auto result = freqman_file.create( freq_file_path ); - if ( !result.is_valid() ) - { - for (size_t n = 0; n < frequency_list.size(); n++) - { - std::string line ; - get_freq_string( frequency_list[ n ] , line ); - freqman_file.write_line( line ); - } - } - } - } - } - else // RECON MODE / MANUAL, only remove matching from output - { - File recon_file; - File tmp_recon_file; - std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - std::string tmp_freq_file_path = "/FREQMAN/"+output_file+"TMP.TXT" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = recon_thread->get_current_freq(); - entry . frequency_b = recon_thread->get_current_freq(); - entry . modulation = recon_thread->get_current_modulation(); - entry . bandwidth = recon_thread->get_current_bandwidth(); - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - delete_file( tmp_freq_file_path ); - auto result = tmp_recon_file.create(tmp_freq_file_path); //First recon if freq is already in txt - // - if (!result.is_valid()) { - bool found=false; - result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - char one_char[1]; //Read it char by char - std::string line; //and put read line in here - for (size_t pointer=0; pointer < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_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; - } - else - { - tmp_recon_file.write_line( frequency_to_add ); - } - line.clear(); //Ready for next textline - } - } - if( found) - { - delete_file( freq_file_path ); - rename_file( tmp_freq_file_path , freq_file_path ); - } - else - { - delete_file( tmp_freq_file_path ); - } - } - } - } - if( frequency_list.size() == 0 ) - { - text_cycle.set_text( " " ); - desc_cycle.set( "no entries in list" ); //Show new description - show_max( true ); //UPDATE new list size on screen - delete_file( "FREQMAN/"+output_file+".TXT" ); - } - - if( recon_thread ) - { - timer = 0 ; - - recon_thread->set_freq_index( current_index ); - - RetuneMessage message { }; - receiver_model.set_tuning_frequency( frequency_list[ current_index ] . frequency_a ); // Retune - message.freq = frequency_list[ current_index ] . frequency_a ; - message.range = current_index ; - EventDispatcher::send_message(message); - - chThdSleepMilliseconds( recon_lock_duration ); // give some time to Thread::Run to pause - - if( previous_userpause ) - { - user_pause(); - } - else - { - user_resume(); - } - - recon_thread->set_freq_delete(false); - recon_thread->set_recon( previous_is_recon ); - } - }; - - button_remove.on_change = [this]() { - if( recon_thread && frequency_list.size() > 0 ) - { - timer = 0 ; - if( button_remove.get_encoder_delta() > 0 ) - { - fwd = true ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "FW>" ); - recon_thread-> set_stepper( 1 ); - recon_thread->set_freq_lock( -1 ); - } - else if( button_remove.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( " set_stepper( -1 ); - recon_thread->set_freq_lock( -1 ); - } - } - button_remove.set_encoder_delta( 0 ); - }; - - button_manual_recon.on_select = [this](Button&) { - scanner_mode = false ; - manual_mode = true ; - 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 { - audio::output::stop(); - recon_thread->stop(); //STOP SCANNER THREAD - - frequency_list.clear(); - freqman_entry manual_freq_entry ; - - def_step = step_mode.selected_index(); // max range val - - manual_freq_entry . type = RANGE ; - manual_freq_entry . description = - "R " + to_string_short_freq(frequency_range.min) + ">" - + to_string_short_freq(frequency_range.max) + " S" // current Manual range - + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0,1) ; //euquiq: lame kludge to reduce spacing in step freq - manual_freq_entry . frequency_a = frequency_range.min ; // min range val - manual_freq_entry . frequency_b = frequency_range.max ; // max range val - manual_freq_entry . modulation = -1 ; - manual_freq_entry . bandwidth = -1 ; - manual_freq_entry . step = def_step ; - - frequency_list . push_back( manual_freq_entry ); - - big_display.set_style(&style_white); //Back to white color - set_display_freq( frequency_range.min ); - - freq_stats.set_style(&style_white); - freq_stats.set( "0/0/0" ); - - show_max(); /* display step information */ - text_cycle.set_text( "1" ); - text_max.set( "/1" ); - button_scanner_mode.set_style( &style_white ); - button_scanner_mode.set_text( "MSEARCH" ); - file_name.set_style( &style_white ); - file_name.set( "MANUAL RANGE RECON" ); - desc_cycle.set_style( &style_white ); - desc_cycle.set( "MANUAL RANGE RECON" ); - - start_recon_thread(); - user_resume(); - } - }; - - field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v != -1 ) - { - receiver_model.disable(); - baseband::shutdown(); - change_mode(v); - if ( !recon_thread->is_recon() ) //for some motive, audio output gets stopped. - audio::output::start(); //So if recon was stopped we resume audio - receiver_model.enable(); - } - }; - - button_dir.on_select = [this](Button&) { - recon_thread->change_recon_direction(); - fwd = recon_thread->get_recon_direction(); - if( fwd ) - { - button_dir.set_text( "FW>" ); - } - else - { - button_dir.set_text( " 0 ) - { - def_step = step_mode.selected_index(); //Use def_step from manual selector - frequency_file_load( true ); - if( recon_thread ) - { - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - } - if( fwd ) - { - button_dir.set_text( "FW>" ); - } - else - { - button_dir.set_text( "get_current_freq(); - bool found=false; - File recon_file; - std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = recon_thread->get_current_freq(); - entry . frequency_b = recon_thread->get_current_freq(); - entry . modulation = recon_thread->get_current_modulation(); - entry . bandwidth = recon_thread->get_current_bandwidth(); - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - auto result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - char one_char[1]; //Read it char by char - std::string line; //and put read line in here - for (size_t pointer=0; pointer < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_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) { - result = recon_file.append(freq_file_path); //Second: append if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - if (found) { - nav_.display_modal("Error", "Frequency already exists"); - set_display_freq( freq ); - } - } - else - { - auto result = recon_file.create(freq_file_path); //third: create if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - } - } - }; - - button_add.on_change = [this]() { - if( recon_thread && frequency_list.size() > 0 ) - { - timer = 0 ; - if( button_add.get_encoder_delta() > 0 ) - { - fwd = true ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "FW>" ); - recon_thread-> set_stepper( 1 ); - recon_thread->set_freq_lock( -1 ); - } - else if( button_add.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( " set_stepper( -1 ); - recon_thread->set_freq_lock( -1 ); - } - } - button_add.set_encoder_delta( 0 ); - }; - - - button_scanner_mode.on_select = [this,&nav](Button&) { - manual_mode = false ; - if( scanner_mode ) - { - scanner_mode = false ; - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - } - else - { - scanner_mode = true ; - button_scanner_mode.set_style( &style_red ); - button_scanner_mode.set_text( "SCANNER" ); - } - frequency_file_load( true ); - if( recon_thread ) - { - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - recon_thread->set_continuous( continuous ); - recon_thread->set_recon_direction( fwd ); - } - if( autostart ) - { - user_resume(); - } - else - { - user_pause(); - } - }; - - button_recon_setup.on_select = [this,&nav](Button&) { - - audio::output::stop(); - recon_thread->stop(); //STOP SCANNER THREAD - frequency_list.clear(); - - auto open_view = nav.push(input_file,output_file,recon_lock_duration,recon_lock_nb_match,recon_match_mode); - open_view -> on_changed = [this](std::vector result) { - input_file = result[0]; - output_file = result[1]; - recon_lock_duration = strtol( result[2].c_str() , nullptr , 10 ); - recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 ); - recon_match_mode = strtol( result[4].c_str() , nullptr , 10 ); - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , field_volume.value() ); - - autosave = persistent_memory::recon_autosave_freqs(); - autostart = persistent_memory::recon_autostart_recon(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - frequency_file_load( false ); - if( recon_thread ) - { - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - recon_thread->set_continuous( continuous ); - recon_thread->set_recon_direction( fwd ); - if( autostart ) - { - user_resume(); - } - else - { - user_pause(); - } - if( update_ranges && frequency_list.size() != 0 ) - { - current_index = 0 ; - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - field_lock_wait.set_value( lock_wait ); - show_max(); - if( userpause != true ) - { - timer = 0 ; - user_resume(); - } - else - { - if( recon_thread && frequency_list.size() != 0 ) - { - RetuneMessage message { }; - message.freq = recon_thread->get_current_freq(); - message.range = current_index ; - EventDispatcher::send_message(message); - } - } - }; - }; - - field_wait.on_change = [this](int32_t v) - { - wait = v ; - if( wait == 0 ) - { - field_wait.set_style( &style_blue ); - } - else if( wait >= 500 ) - { - field_wait.set_style(&style_white); - } - else if( wait > -500 && wait < 500 ) - { - field_wait.set_style( &style_red ); - } - else if( wait <= -500 ) - { - field_wait.set_style( &style_green ); - } - }; - - field_lock_wait.on_change = [this](int32_t v) - { - lock_wait = v ; - - uint32_t lock_white = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ; - lock_white = lock_white * 100 ; - if( lock_white < 400 ) - lock_white = 400 ; - - uint32_t lock_yellow = 300 ; - - if( lock_wait >= lock_white ) - { - field_lock_wait.set_style( &style_white ); - } - else if( lock_wait >= lock_yellow ) - { - field_lock_wait.set_style( &style_yellow); - } - else - { - field_lock_wait.set_style(&style_red); - } - }; - - field_squelch.on_change = [this](int32_t v) { - squelch = v ; - }; - - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - //PRE-CONFIGURATION: - change_mode(AM_MODULATION); //Start on AM - field_mode.set_by_value(AM_MODULATION); //Reflect the mode into the manual selector - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - file_name.set( "=>" ); - - //Loading input and output file from settings - ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , volume ); - - field_squelch.set_value( squelch ); - field_wait.set_value(wait); - field_lock_wait.set_value(lock_wait); - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - - //FILL STEP OPTIONS - freqman_set_modulation_option( field_mode ); - freqman_set_step_option( step_mode ); - - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // Retune - - if( filedelete ) - { - delete_file( "FREQMAN/"+output_file+".TXT" ); - } - - frequency_file_load( false ); /* do not stop all at start */ - if( recon_thread ) - { - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - if( update_ranges && frequency_list.size() > 0 ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - if( autostart ) - { - timer = 0 ; //Will trigger a recon_resume() on_statistics_update, also advancing to next freq. - user_resume(); - } - else - { - user_pause(); - } - show_max(); - } - } - - - void ReconView::frequency_file_load( bool stop_all_before) { - - // stop everything running now if required - if (stop_all_before) { - recon_thread->stop(); - } - audio::output::stop(); - - def_step = step_mode.selected_index(); // use def_step from manual selector - frequency_list.clear(); // clear the existing frequency list (expected behavior) - std::string file_input = input_file ; // default recon mode - if( scanner_mode ) - { - file_input = output_file ; - file_name.set_style( &style_red ); - button_scanner_mode.set_style( &style_red ); - button_scanner_mode.set_text( "SCANNER" ); - } - else - { - file_name.set_style( &style_blue ); - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - } - file_name.set_style( &style_white ); - desc_cycle.set_style( &style_white ); - if( !load_freqman_file_ex( file_input , frequency_list, load_freqs, load_ranges, load_hamradios ) ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set(" NO " + file_input + ".TXT FILE ..." ); - file_name.set( "=> NO DATA" ); - } - else - { - file_name.set( "=> "+file_input ); - if( frequency_list.size() == 0 ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set("/0 no entries in list" ); - file_name.set( "BadOrEmpty "+file_input ); - } - else - { - if( frequency_list.size() > FREQMAN_MAX_PER_FILE ) - { - file_name.set_style( &style_yellow ); - desc_cycle.set_style( &style_yellow ); - } - } - } - step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector - start_recon_thread(); - std::string description = "...no description..." ; - if( frequency_list.size() != 0 ) - { - current_index = 0 ; - recon_thread-> set_freq_index( 0 ); - switch( frequency_list[current_index].type ) - { - case RANGE: - description = "R: " + frequency_list[current_index].description ; - break ; - case HAMRADIO: - description = "H: " + frequency_list[current_index].description ; - break ; - default: - case SINGLE: - description = "S: " + frequency_list[current_index].description ; - break ; - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - } - else - { - text_cycle.set_text( " " ); - } - desc_cycle.set( description ); - } - - void ReconView::on_statistics_update(const ChannelStatistics& statistics) { - - int32_t actual_db = statistics.max_db ; - bool update_stats = false ; - int32_t freq_lock = recon_thread->is_freq_lock(); - int32_t max_lock = recon_thread -> get_lock_nb_match(); - - // 0 recon , 1 locking , 2 locked - static int32_t status = -1 ; - - if( actual_db != 0 && db != actual_db ) - { - db = actual_db ; - update_stats = true ; - } - - if( !userpause ) - { - if( !timer ) - { - if( status != 0 ) - { - status = 0 ; - update_stats = true ; - continuous_lock = false ; - recon_thread->set_freq_lock( -1 ); //in lock period, still analyzing the signal - recon_resume(); // RESUME! - big_display.set_style(&style_white); - } - } - if( freq_lock >= max_lock ) // LOCKED - { - if( status != 2 ) - { - status = 2 ; - update_stats = true ; - - if( wait != 0 ) - { - audio::output::start(); - this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); - } - if( wait >= 0 ) - { - timer = wait ; - } - //Inform freq (for coloring purposes also!) - RetuneMessage message { }; - message.freq = recon_thread->get_current_freq() ; - message.range = current_index ; - EventDispatcher::send_message(message); - } - if( wait < 0 ) - { - if( actual_db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY - { - timer = abs( wait ); - } - } - } - else if( freq_lock >= 0 ) // freq_lock < max_lock , LOCKING - { - if( actual_db > squelch ) //MATCHING LEVEL - { - if( status != 1 ) - { - status = 1 ; - continuous_lock = true ; - if( wait != 0 ) - { - audio::output::stop(); - } - timer = lock_wait ; - //Inform freq (for coloring purposes also!) - RetuneMessage message { }; - message.freq = recon_thread->get_current_freq() ; - current_index = recon_thread->get_freq_index() ; - message.range = current_index ; - EventDispatcher::send_message(message); - } - freq_lock ++ ; - recon_thread->set_freq_lock( freq_lock ); //in lock period, still analyzing the signal - update_stats = true ; - } - else - { - // continuous, direct cut it if not consecutive match - if( recon_match_mode == 0 ) - { - timer = 0 ; - update_stats = true ; - recon_thread->set_freq_lock( -1 ); - } - } - } - } - else - { - if( actual_db > squelch ) - { - if( status != 2 ) //MATCHING LEVEL - { - status = 2 ; - big_display.set_style(&style_yellow); - set_display_freq( recon_thread->get_current_freq() ); - } - } - else - { - if( status != 0 ) - { - status = 0 ; - big_display.set_style(&style_white); - set_display_freq( recon_thread->get_current_freq() ); - } - - } - } - if( update_stats ) - { - show_max(); - } - - if( timer > 0 ) - show_max(); - - timer -= 50 ; - if( timer < 0 ) - { - timer = 0 ; - show_max(); - } - } /* on_statistic_updates */ - - void ReconView::recon_pause() { - if (recon_thread->is_recon()) - { - audio::output::start(); - this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); - recon_thread->set_freq_lock( 0 ); //in lock period, still analyzing the signal - recon_thread ->set_recon(false); // WE STOP SCANNING - } - } - - - void ReconView::recon_resume() { - audio::output::stop(); - if( !recon_thread->is_recon() ) - recon_thread->set_recon(true); // RESUME! - big_display.set_style(&style_white); //Back to grey color - } - - void ReconView::user_pause() { - timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq. - button_pause.set_text(""); //PAUSED, show resume - userpause=true; - continuous_lock=false; - recon_pause(); - } - - void ReconView::user_resume() { - timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq. - button_pause.set_text(""); //Show button for pause - userpause=false; // Resume recon - continuous_lock=false; - recon_resume(); - } - - void ReconView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); - } - - size_t ReconView::change_mode( freqman_index_t new_mod ) { //Before this, do a recon_thread->stop(); After this do a start_recon_thread() - - field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; }; - - switch( new_mod ) { - case AM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw DSB (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - 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(3072000); receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - case NFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 16k (2) default - field_bw.set_selected_index(2); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - 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 WFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 200k (0) only/default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - 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(1750000); - text_ctcss.set(" "); - break; - default: - break; - } - return freqman_entry_get_step_value( def_step ); - } - - void ReconView::start_recon_thread() { - receiver_model.enable(); - receiver_model.set_squelch_level(0); - recon_thread = std::make_unique(&frequency_list); - recon_thread->set_continuous( continuous ); - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - recon_thread->set_recon_direction( fwd ); - } - - void ReconView::handle_coded_squelch(const uint32_t value) { - static int32_t last_idx = -1 ; - - float diff, min_diff = value; - size_t min_idx { 0 }; - size_t c; - - if( field_mode.selected_index() != NFM_MODULATION ) - { - text_ctcss.set(" "); - return ; - } - - // Find nearest match - for (c = 0; c < tone_keys.size(); c++) { - diff = abs(((float)value / 100.0) - tone_keys[c].second); - if (diff < min_diff) { - min_idx = c; - min_diff = diff; - } - } - - // Arbitrary confidence threshold - if( last_idx < 0 || (unsigned)last_idx != min_idx ) - { - last_idx = min_idx ; - if (min_diff < 40) - text_ctcss.set("T: "+tone_keys[min_idx].first); - else - text_ctcss.set(" "); - } - } + if( !found) { + result = recon_file.append(freq_file_path); //Second: append if it is not there + if( !result.is_valid() ) + { + recon_file.write_line( frequency_to_add ); + } + } + } + else { + result = recon_file.create( freq_file_path ); //First freq if no output file + if( !result.is_valid() ) + { + recon_file.write_line( frequency_to_add ); + } + } + } + } + } + } + + void ReconView::handle_retune() { + static int32_t last_index = -1 ; + static int64_t last_freq = 0 ; + if( last_freq != freq ) + { + last_freq = freq ; + receiver_model.set_tuning_frequency( freq ); // Retune + } + if( frequency_list.size() > 0 ) + { + if( last_entry . modulation != frequency_list[ current_index ] . modulation && frequency_list[ current_index ] . modulation >= 0 ) + { + last_entry . modulation = frequency_list[ current_index ]. modulation; + field_mode.set_selected_index( frequency_list[ current_index ]. modulation ); + } + // Set bandwidth if any + if( last_entry . bandwidth != frequency_list[ current_index ] . bandwidth && frequency_list[ current_index ] . bandwidth >= 0 ) + { + last_entry . bandwidth = frequency_list[ current_index ]. bandwidth; + switch( frequency_list[ current_index ].modulation ) + { + case AM_MODULATION: + receiver_model.set_am_configuration( freq ); + break ; + case NFM_MODULATION: + receiver_model.set_nbfm_configuration( freq ); + break ; + case WFM_MODULATION: + receiver_model.set_wfm_configuration( freq ); + default: + break ; + } + field_bw.set_selected_index( freq ); + } + if( last_entry . step != frequency_list[ current_index ] . step && frequency_list[ current_index ] . step >= 0 ) + { + last_entry . step = frequency_list[ current_index ]. step ; + step = freqman_entry_get_step_value( last_entry . step ); + step_mode.set_selected_index( step ); + } + if( last_index != current_index ) + { + last_index = current_index ; + if( (int32_t)frequency_list.size() && current_index < (int32_t)frequency_list.size() && frequency_list[ current_index ] . type == RANGE ) + { + if( update_ranges ) + { + button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.min = frequency_list[ current_index ] . frequency_a ; + if( frequency_list[ current_index ] . frequency_b != 0 ) + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_b ; + } + else + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_a ; + } + } + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + if(frequency_list[current_index].description.size() > 0) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + desc_cycle.set( "R: " + frequency_list[current_index].description ); //Show new description + break ; + case HAMRADIO: + desc_cycle.set( "H: " + frequency_list[current_index].description ); //Show new description + break ; + default: + case SINGLE: + desc_cycle.set( "S: " + frequency_list[current_index].description ); //Show new description + break ; + } + } + else + { + desc_cycle.set( "...no description..." ); //Show new description + } + } + } + } + + + void ReconView::focus() { + button_pause.focus(); + } + + ReconView::~ReconView() { + + ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); + + // save app settings + settings.save("recon", &app_settings); + + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); + } + + + ReconView::ReconView( NavigationView& nav) : nav_ { nav } { + + add_children( { + &labels, + &field_lna, + &field_vga, + &field_rf_amp, + &field_volume, + &field_bw, + &field_squelch, + &field_wait, + &field_lock_wait, + &button_recon_setup, + &button_scanner_mode, + &button_looking_glass, + &file_name, + &rssi, + &text_cycle, + &text_max, + &desc_cycle, + &big_display, + &freq_stats, + &text_timer, + &text_ctcss, + &button_manual_start, + &button_manual_end, + &button_manual_recon, + &field_mode, + &step_mode, + &button_pause, + &button_audio_app, + &button_add, + &button_dir, + &button_restart, + &button_mic_app, + &button_remove + } ); + + // Recon directory + if( check_sd_card() ) { // Check to see if SD Card is mounted + make_new_directory( u"/RECON" ); + sd_card_mounted = true ; + } + def_step = 0 ; + //HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + receiver_model.set_tuning_frequency( stored_freq ); + if( stored_freq - OneMHz > 0 ) + frequency_range.min = stored_freq - OneMHz ; + else + frequency_range.min = 0 ; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + if( stored_freq + OneMHz < MAX_UFREQ ) + frequency_range.max = stored_freq + OneMHz ; + else + frequency_range.max = MAX_UFREQ ; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + // Loading settings + autostart = persistent_memory::recon_autostart_recon(); + autosave = persistent_memory::recon_autosave_freqs(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_volume.set_value( volume ); + if( sd_card_mounted ) + { + // load auto common app settings + auto rc = settings.load("recon", &app_settings); + if(rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + receiver_model.set_rf_amp(app_settings.rx_amp); + } + } + + button_manual_start.on_select = [this, &nav](ButtonWithEncoder& 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](ButtonWithEncoder& 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)); + }; + }; + + text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { + if( frequency_list.size() > 0 ) + { + auto new_view = nav_.push(current_index); + new_view->on_changed = [this, &button](rf::Frequency f) { + f = f / OneMHz ; + if( f >= 1 && f <= frequency_list.size() ) + { + index_stepper = f - 1 - current_index ; + freq_lock = 0 ; + } + }; + } + }; + + button_manual_start.on_change = [this]() { + frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step ); + if( frequency_range.min < 0 ) + { + frequency_range.min = 0 ; + } + if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); + } + if( frequency_range.min > (frequency_range.max - freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.max = frequency_range.min + freqman_entry_get_step_value( def_step ); + if( frequency_range.max > MAX_UFREQ ) + { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); + frequency_range.max = MAX_UFREQ ; + } + } + button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); + button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); + button_manual_start.set_encoder_delta( 0 ); + }; + + button_manual_end.on_change = [this]() { + frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value( def_step ); + if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) + { + frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ); + } + if( frequency_range.max > MAX_UFREQ ) + { + frequency_range.max = MAX_UFREQ ; + } + if( frequency_range.max < (frequency_range.min + freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.min = frequency_range.max - freqman_entry_get_step_value( def_step ); + if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) + { + frequency_range.min = 1 ; + frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ) ; + } + } + button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); + button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); + button_manual_end.set_encoder_delta( 0 ); + }; + + text_cycle.on_change = [this]() { + on_index_delta( text_cycle.get_encoder_delta() ); + text_cycle.set_encoder_delta( 0 ); + }; + + button_pause.on_select = [this](ButtonWithEncoder&) { + if( frequency_list.size() > 0 ) + { + if( freq_lock > 0 ) + { + if( fwd ) + { + on_stepper_delta( 1 ); + } + else + { + on_stepper_delta( -1 ); + } + button_pause.set_text(""); //Show button for non continuous stop + } + else + { + if( userpause ) + { + recon_resume(); + } + else + { + recon_pause(); + } + } + } + }; + + button_pause.on_change = [this]() { + on_stepper_delta( button_pause.get_encoder_delta() ); + button_pause.set_encoder_delta( 0 ); + }; + + button_audio_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_looking_glass.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + + rssi.set_focusable(true); + rssi.set_peak( true , 500 ); + rssi.on_select = [this](RSSI&) { + nav_.pop(); + nav_.push(); + }; + + button_mic_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_remove.on_select = [this](ButtonWithEncoder&) { + if(frequency_list.size() > 0 ) + { + if( scanner_mode ) + { + if( current_index >= (int32_t)frequency_list.size() ) + { + current_index = frequency_list.size() - 1 ; + } + frequency_list.erase( frequency_list.begin()+ current_index ); + if( current_index >= (int32_t)frequency_list.size() ) + { + current_index = frequency_list.size() - 1 ; + } + if( frequency_list.size() > 0 ) + { + if(frequency_list[current_index].description.size() > 0) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + desc_cycle.set( "R: " + frequency_list[current_index].description ); + break ; + case HAMRADIO: + desc_cycle.set( "H: " + frequency_list[current_index].description ); + break ; + default: + case SINGLE: + desc_cycle.set( "S: " + frequency_list[current_index].description ); + break ; + } + } + else + { + desc_cycle.set( "...no description..." ); + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + + File freqman_file; + + std::string freq_file_path = "FREQMAN/" + output_file + ".TXT"; + + delete_file( freq_file_path ); + + auto result = freqman_file.create( freq_file_path ); + if ( !result.is_valid() ) + { + for (size_t n = 0; n < frequency_list.size(); n++) + { + std::string line ; + get_freq_string( frequency_list[ n ] , line ); + freqman_file.write_line( line ); + } + } + } + } + else // RECON MODE / MANUAL, only remove matching from output + { + File recon_file; + File tmp_recon_file; + std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; + std::string tmp_freq_file_path = "/FREQMAN/"+output_file+"TMP.TXT" ; + std::string frequency_to_add ; + + freqman_entry entry = frequency_list[ current_index ] ; + entry . frequency_a = freq ; + entry . frequency_b = 0 ; + entry . modulation = last_entry.modulation ; + entry . bandwidth = last_entry.bandwidth ; + entry . type = SINGLE ; + + get_freq_string( entry , frequency_to_add ); + + delete_file( tmp_freq_file_path ); + auto result = tmp_recon_file.create(tmp_freq_file_path); //First recon if freq is already in txt + // + if (!result.is_valid()) { + bool found=false; + result = recon_file.open(freq_file_path); //First recon if freq is already in txt + if (!result.is_valid()) { + char one_char[1]; //Read it char by char + std::string line; //and put read line in here + for (size_t pointer=0; pointer < recon_file.size();pointer++) { + recon_file.seek(pointer); + recon_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; + } + else + { + tmp_recon_file.write_line( frequency_to_add ); + } + line.clear(); //Ready for next textline + } + } + if( found) + { + delete_file( freq_file_path ); + rename_file( tmp_freq_file_path , freq_file_path ); + } + else + { + delete_file( tmp_freq_file_path ); + } + } + } + } + receiver_model.set_tuning_frequency( frequency_list[ current_index ] . frequency_a ); // Retune + } + if( frequency_list.size() == 0 ) + { + text_cycle.set_text( " " ); + desc_cycle.set( "no entries in list" ); //Show new description + delete_file( "FREQMAN/"+output_file+".TXT" ); + } + timer = 0 ; + }; + + button_remove.on_change = [this]() { + on_stepper_delta( button_remove.get_encoder_delta() ); + button_remove.set_encoder_delta( 0 ); + }; + + button_manual_recon.on_select = [this](Button&) { + scanner_mode = false ; + manual_mode = true ; + recon_pause(); + + 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 { + audio::output::stop(); + + frequency_list.clear(); + freqman_entry manual_freq_entry ; + + def_step = step_mode.selected_index(); // max range val + + manual_freq_entry . type = RANGE ; + manual_freq_entry . description = + "R " + to_string_short_freq(frequency_range.min) + ">" + + to_string_short_freq(frequency_range.max) + " S" // current Manual range + + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0,1) ; //euquiq: lame kludge to reduce spacing in step freq + manual_freq_entry . frequency_a = frequency_range.min ; // min range val + manual_freq_entry . frequency_b = frequency_range.max ; // max range val + manual_freq_entry . modulation = -1 ; + manual_freq_entry . bandwidth = -1 ; + manual_freq_entry . step = def_step ; + + frequency_list . push_back( manual_freq_entry ); + + big_display.set_style(&style_white); //Back to white color + + freq_stats.set_style(&style_white); + freq_stats.set( "0/0/0" ); + + text_cycle.set_text( "1" ); + text_max.set( "/1" ); + button_scanner_mode.set_style( &style_white ); + button_scanner_mode.set_text( "MSEARCH" ); + file_name.set_style( &style_white ); + file_name.set( "MANUAL RANGE RECON" ); + desc_cycle.set_style( &style_white ); + desc_cycle.set( "MANUAL RANGE RECON" ); + + current_index = 0 ; + freq = manual_freq_entry . frequency_a ; + handle_retune(); + recon_redraw(); + recon_resume(); + } + }; + + button_dir.on_select = [this](Button&) { + if( fwd ) + { + fwd = false ; + button_dir.set_text( "" ); + } + timer = 0 ; + if ( userpause ) //If user-paused, resume + recon_resume(); + }; + + button_restart.on_select = [this](Button&) { + if( frequency_list.size() > 0 ) + { + def_step = step_mode.selected_index(); //Use def_step from manual selector + frequency_file_load( true ); + if( fwd ) + { + button_dir.set_text( "FW>" ); + } + else + { + button_dir.set_text( " 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) { + result = recon_file.append(freq_file_path); //Second: append if it is not there + if( !result.is_valid() ) + { + recon_file.write_line( frequency_to_add ); + } + } + if (found) { + nav_.display_modal("Error", "Frequency already exists"); + } + } + else + { + auto result = recon_file.create(freq_file_path); //third: create if it is not there + if( !result.is_valid() ) + { + recon_file.write_line( frequency_to_add ); + } + } + } + } + }; + + button_add.on_change = [this]() { + on_stepper_delta( button_add.get_encoder_delta() ); + button_add.set_encoder_delta( 0 ); + }; + + button_scanner_mode.on_select = [this,&nav](Button&) { + manual_mode = false ; + if( scanner_mode ) + { + scanner_mode = false ; + button_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + } + else + { + scanner_mode = true ; + button_scanner_mode.set_style( &style_red ); + button_scanner_mode.set_text( "SCANNER" ); + } + frequency_file_load( true ); + if( autostart ) + { + recon_resume(); + } + else + { + recon_pause(); + } + }; + + button_recon_setup.on_select = [this,&nav](Button&) { + + audio::output::stop(); + + frequency_list.clear(); + + auto open_view = nav.push(input_file,output_file,recon_lock_duration,recon_lock_nb_match,recon_match_mode); + open_view -> on_changed = [this](std::vector result) { + input_file = result[0]; + output_file = result[1]; + recon_lock_duration = strtol( result[2].c_str() , nullptr , 10 ); + recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 ); + recon_match_mode = strtol( result[4].c_str() , nullptr , 10 ); + + ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); + + autosave = persistent_memory::recon_autosave_freqs(); + autostart = persistent_memory::recon_autostart_recon(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_lock_wait.set_value(recon_lock_duration); + + frequency_file_load( false ); + if( autostart ) + { + recon_resume(); + } + else + { + recon_pause(); + } + if( userpause != true ) + { + recon_resume(); + } + }; + }; + + field_wait.on_change = [this](int32_t v) + { + wait = v ; + if( wait == 0 ) + { + field_wait.set_style( &style_blue ); + } + else if( wait >= 500 ) + { + field_wait.set_style(&style_white); + } + else if( wait > -500 && wait < 500 ) + { + field_wait.set_style( &style_red ); + } + else if( wait <= -500 ) + { + field_wait.set_style( &style_green ); + } + }; + + field_lock_wait.on_change = [this](uint32_t v) + { + recon_lock_duration = v ; + if( recon_match_mode == RECON_MATCH_CONTINUOUS ) + { + if( (v / STATS_UPDATE_INTERVAL ) > recon_lock_nb_match ) + { + field_lock_wait.set_style( &style_white ); + } + else if( (v / STATS_UPDATE_INTERVAL ) == recon_lock_nb_match ) + { + field_lock_wait.set_style(&style_yellow); + } + } + else // RECON_MATCH_SPARSE + { + field_lock_wait.set_style( &style_white ); + } + }; + + field_squelch.on_change = [this](int32_t v) { + squelch = v ; + }; + + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + //PRE-CONFIGURATION: + button_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + file_name.set( "=>" ); + + //Loading input and output file from settings + ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , volume ); + + field_squelch.set_value( squelch ); + field_wait.set_value(wait); + field_lock_wait.set_value(recon_lock_duration); + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + // fill modulation and step options + freqman_set_modulation_option( field_mode ); + freqman_set_step_option( step_mode ); + + // set radio + change_mode(AM_MODULATION); // start on AM. + field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector + receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // first tune + + if( filedelete ) + { + delete_file( "FREQMAN/"+output_file+".TXT" ); + } + + frequency_file_load( false ); /* do not stop all at start */ + if( autostart ) + { + recon_resume(); + } + else + { + recon_pause(); + } + + recon_redraw(); + } + + void ReconView::frequency_file_load( bool stop_all_before) { + + (void)(stop_all_before); + audio::output::stop(); + + def_step = step_mode.selected_index(); // use def_step from manual selector + frequency_list.clear(); // clear the existing frequency list (expected behavior) + std::string file_input = input_file ; // default recon mode + if( scanner_mode ) + { + file_input = output_file ; + file_name.set_style( &style_red ); + button_scanner_mode.set_style( &style_red ); + button_scanner_mode.set_text( "SCANNER" ); + } + else + { + file_name.set_style( &style_blue ); + button_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + } + file_name.set_style( &style_white ); + desc_cycle.set_style( &style_white ); + if( !load_freqman_file_ex( file_input , frequency_list, load_freqs, load_ranges, load_hamradios ) ) + { + file_name.set_style( &style_red ); + desc_cycle.set_style( &style_red ); + desc_cycle.set(" NO " + file_input + ".TXT FILE ..." ); + file_name.set( "=> NO DATA" ); + } + else + { + file_name.set( "=> "+file_input ); + if( frequency_list.size() == 0 ) + { + file_name.set_style( &style_red ); + desc_cycle.set_style( &style_red ); + desc_cycle.set("/0 no entries in list" ); + file_name.set( "BadOrEmpty "+file_input ); + } + else + { + if( frequency_list.size() > FREQMAN_MAX_PER_FILE ) + { + file_name.set_style( &style_yellow ); + desc_cycle.set_style( &style_yellow ); + } + } + } + + if( frequency_list[ 0 ] . step >= 0 ) + step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); + else + step = freqman_entry_get_step_value( def_step ); + + switch( frequency_list[ 0 ] . type ){ + case SINGLE: + freq = frequency_list[ 0 ] . frequency_a ; + break; + case RANGE: + minfreq = frequency_list[ 0 ] . frequency_a ; + maxfreq = frequency_list[ 0 ] . frequency_b ; + if( fwd ) + { + freq = minfreq ; + } + else + { + freq = maxfreq ; + } + if( frequency_list[ 0 ] . step >= 0 ) + step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); + break; + case HAMRADIO: + minfreq = frequency_list[ 0 ] . frequency_a ; + maxfreq = frequency_list[ 0 ] . frequency_b ; + if( fwd ) + { + freq = minfreq ; + } + else + { + freq = maxfreq ; + } + break; + default: + break; + } + last_entry . modulation = -1 ; + last_entry . bandwidth = -1 ; + last_entry . step = -1 ; + + step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector + receiver_model.enable(); + receiver_model.set_squelch_level(0); + std::string description = "...no description..." ; + current_index = 0 ; + if( frequency_list.size() != 0 ) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + description = "R: " + frequency_list[current_index].description ; + break ; + case HAMRADIO: + description = "H: " + frequency_list[current_index].description ; + break ; + default: + case SINGLE: + description = "S: " + frequency_list[current_index].description ; + break ; + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + if( update_ranges ) + { + button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.min = frequency_list[ current_index ] . frequency_a ; + if( frequency_list[ current_index ] . frequency_b != 0 ) + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_b ; + } + else + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_a ; + } + } + } + else + { + text_cycle.set_text( " " ); + } + desc_cycle.set( description ); + handle_retune(); + } + + void ReconView::on_statistics_update(const ChannelStatistics& statistics) { + // 0 recon , 1 locking , 2 locked + static int32_t status = -1 ; + static int32_t last_timer = -1 ; + + db = statistics.max_db ; + + if( !userpause ) + { + if( !timer ) + { + status = 0 ; + continuous_lock = false ; + freq_lock = 0 ; + timer = recon_lock_duration ; + big_display.set_style(&style_white); + } + if( freq_lock < recon_lock_nb_match ) // LOCKING + { + if( status != 1 ) + { + status = 1 ; + if( wait != 0 ) + { + audio::output::stop(); + } + } + if( db > squelch ) //MATCHING LEVEL + { + continuous_lock = true ; + freq_lock ++ ; + } + else + { + // continuous, direct cut it if not consecutive match after 1 first match + if( recon_match_mode == RECON_MATCH_CONTINUOUS ) + { + if( freq_lock > 0 ) + { + timer = 0 ; + continuous_lock = false ; + } + } + } + } + if( freq_lock >= recon_lock_nb_match ) // LOCKED + { + if( status != 2 ) + { + continuous_lock = false ; + status = 2 ; + if( wait != 0 ) + { + audio_output_start(); + } + if( wait >= 0 ) + { + timer = wait ; + } + } + if( wait < 0 ) + { + if( db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY + { + timer = abs( wait ); + } + } + } + } + if( last_timer != timer ) + { + last_timer = timer ; + text_timer.set( "TIMER: " + to_string_dec_int( timer ) ); + } + if( timer ) + { + if( !continuous_lock ) + timer -= STATS_UPDATE_INTERVAL ; + if( timer < 0 ) + { + timer = 0 ; + } + } + if( recon || stepper != 0 || index_stepper != 0 ) + { + if( !timer || stepper != 0 || index_stepper != 0 ) + { + //IF THERE IS A FREQUENCY LIST ... + if (frequency_list.size() > 0 ) + { + has_looped = false ; + entry_has_changed = false ; + + if( recon || stepper != 0 || index_stepper != 0 ) + { + if( index_stepper == 0 ) + { + /* we are doing a range */ + if( frequency_list[ current_index ] . type == RANGE ) { + if ( ( fwd && stepper == 0 ) || stepper > 0 ) { + //forward + freq += step ; + // if bigger than range max + if (freq > maxfreq ) { + // when going forward we already know that we can skip a whole range => two values in the list + current_index ++ ; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + freq -= step ; + // if lower than range min + if (freq < minfreq ) { + // when back we have to check one step at a time + current_index -- ; + entry_has_changed = true ; + // looping + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + } + else if( frequency_list[ current_index ] . type == SINGLE ) { + if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward + current_index++; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + current_index--; + entry_has_changed = true ; + // if previous if under the list => go back from end + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + else if( frequency_list[ current_index ] . type == HAMRADIO ) + { + if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward + if( ( minfreq != maxfreq ) && freq == minfreq ) + { + freq = maxfreq ; + } + else + { + current_index++; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + if( ( minfreq != maxfreq ) && freq == maxfreq ) + { + freq = minfreq ; + } + else + { + current_index--; + entry_has_changed = true ; + // if previous if under the list => go back from end + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + } + // set index to boundary if !continuous + if( has_looped && !continuous ) + { + entry_has_changed = true ; + /* prepare values for the next run, when user will resume */ + if( ( fwd && stepper == 0 ) || stepper > 0 ) + { + current_index = 0 ; + } + else if( ( !fwd && stepper == 0 ) || stepper < 0 ) + { + current_index = frequency_list.size() - 1 ; + } + } + } + else + { + current_index += index_stepper ; + if( current_index < 0 ) + current_index += frequency_list.size(); + if( (unsigned)current_index >= frequency_list.size() ) + current_index -= frequency_list.size() ; + + entry_has_changed = true ; + + if( !recon ) // for some motive, audio output gets stopped. + audio_output_start(); + } + // reload entry if changed + if( entry_has_changed ){ + timer = 0 ; + switch( frequency_list[ current_index ] . type ){ + case SINGLE: + freq = frequency_list[ current_index ] . frequency_a ; + break; + case RANGE: + minfreq = frequency_list[ current_index ] . frequency_a ; + maxfreq = frequency_list[ current_index ] . frequency_b ; + if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) + { + freq = minfreq ; + } + else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) + { + freq = maxfreq ; + } + break; + case HAMRADIO: + minfreq = frequency_list[ current_index ] . frequency_a ; + maxfreq = frequency_list[ current_index ] . frequency_b ; + if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) + { + freq = minfreq ; + } + else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) + { + freq = maxfreq ; + } + break; + default: + break; + } + } + // send a pause message with the right freq + if( has_looped && !continuous ) + { + recon_pause(); + } + index_stepper = 0 ; + if( stepper < 0 ) stepper ++ ; + if( stepper > 0 ) stepper -- ; + } // if( recon || stepper != 0 || index_stepper != 0 ) + }//if (frequency_list.size() > 0 ) + } /* on_statistic_updates */ + } + handle_retune(); + recon_redraw(); + } + + void ReconView::recon_pause() { + timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq. + freq_lock = 0 ; + userpause = true; + continuous_lock=false; + recon = false ; + + audio_output_start(); + + big_display.set_style(&style_white); + button_pause.set_text(""); //PAUSED, show resume + } + + void ReconView::recon_resume() { + timer = 0 ; + freq_lock = 0 ; + userpause = false; + continuous_lock = false ; + recon = true ; + + audio::output::stop(); + + big_display.set_style(&style_white); + button_pause.set_text(""); + } + + void ReconView::on_index_delta(int32_t v) + { + if( v > 0 ) + { + fwd = true ; + button_dir.set_text( "FW>" ); + } + else + { + + fwd = false ; + button_dir.set_text( " 0 ) + index_stepper = v ; + + freq_lock = 0 ; + timer = 0 ; + } + + void ReconView::on_stepper_delta(int32_t v) + { + if( v > 0 ) + { + fwd = true ; + button_dir.set_text( "FW>" ); + } + else + { + + fwd = false ; + button_dir.set_text( " 0 ) + stepper = v ; + + freq_lock = 0 ; + timer = 0 ; + } + + void ReconView::on_headphone_volume_changed(int32_t v) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); + } + + size_t ReconView::change_mode( freqman_index_t new_mod ) { + + field_mode.on_change = [this](size_t , OptionsField::value_t v) { (void)v; }; + field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; }; + + receiver_model.disable(); + baseband::shutdown(); + + switch( new_mod ) { + case AM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw DSB (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + 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(3072000); receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + case NFM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw 16k (2) default + field_bw.set_selected_index(2); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + 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 WFM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw 200k (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + 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(1750000); + text_ctcss.set(" "); + break; + default: + break; + } + field_mode.set_selected_index( new_mod ); + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if( v != -1 ) + { + change_mode(v); + } + }; + + if ( !recon ) //for some motive, audio output gets stopped. + audio::output::start(); //So if recon was stopped we resume audio + receiver_model.enable(); + + return freqman_entry_get_step_value( def_step ); + } + + void ReconView::handle_coded_squelch(const uint32_t value) { + static int32_t last_idx = -1 ; + + float diff, min_diff = value; + size_t min_idx { 0 }; + size_t c; + + if( field_mode.selected_index() != NFM_MODULATION ) + { + text_ctcss.set(" "); + return ; + } + + // Find nearest match + for (c = 0; c < tone_keys.size(); c++) { + diff = abs(((float)value / 100.0) - tone_keys[c].second); + if (diff < min_diff) { + min_idx = c; + min_diff = diff; + } + } + + // Arbitrary confidence threshold + if( last_idx < 0 || (unsigned)last_idx != min_idx ) + { + last_idx = min_idx ; + if (min_diff < 40) + text_ctcss.set("T: "+tone_keys[min_idx].first); + else + text_ctcss.set(" "); + } + } } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon.hpp b/firmware/application/apps/ui_recon.hpp index 8d11a6f7..e8dd5ae7 100644 --- a/firmware/application/apps/ui_recon.hpp +++ b/firmware/application/apps/ui_recon.hpp @@ -38,87 +38,10 @@ #include "string_format.hpp" #include "file.hpp" #include "app_settings.hpp" - -// maximum usable freq -#define MAX_UFREQ 7200000000 - -// 1Mhz helper -#ifdef OneMHz - #undef OneMHz -#endif -#define OneMHz 1000000 +#include "ui_recon_settings.hpp" namespace ui { - class ReconThread { - public: - ReconThread(freqman_db *database ); - ~ReconThread(); - - void set_recon(const bool v); - void set_freq_delete(const bool v); - bool is_recon(); - - void set_lock_duration( const uint32_t v ); - uint32_t get_lock_duration(); - void set_lock_nb_match( const uint32_t v ); - void set_match_mode( const uint32_t v ); - uint32_t get_lock_nb_match(); - - void set_freq_lock(const int32_t v); - int32_t is_freq_lock(); - int64_t get_current_freq(); - - void set_stepper(const int64_t v); - void set_index_stepper(const int64_t v); - - void change_recon_direction(); - bool get_recon_direction(); - void set_recon_direction( const bool v); - - void set_continuous(const bool v); - - void set_default_modulation( freqman_index_t index ); - freqman_index_t get_current_modulation(); - void set_default_bandwidth( freqman_index_t index ); - freqman_index_t get_current_bandwidth(); - void set_default_step( freqman_index_t index ); - void set_freq_index( int16_t index ); - int16_t get_freq_index(); - - void run(); - void stop(); - - ReconThread(const ReconThread&) = delete; - ReconThread(ReconThread&&) = delete; - ReconThread& operator=(const ReconThread&) = delete; - ReconThread& operator=(ReconThread&&) = delete; - - private: - freqman_db &frequency_list_ ; - Thread* thread { nullptr }; - int64_t freq = 0 ; - uint32_t step = 0 ; - freqman_index_t def_modulation = 0 ; - freqman_index_t def_bandwidth = 0 ; - freqman_index_t def_step = 0 ; - tone_index tone = 0 ; - freqman_entry last_entry = { } ; - int16_t frequency_index = 0 ; - - bool _recon { true }; - bool _freq_delete { false }; - bool _fwd { true }; - bool _continuous { true }; - bool entry_has_changed = false ; - int64_t _stepper { 0 }; - int64_t _index_stepper { 0 }; - int32_t _freq_lock { 0 }; - uint32_t _lock_duration { 50 }; - uint32_t _lock_nb_match { 10 }; - static msg_t static_fn(void* arg); - }; - class ReconView : public View { public: ReconView(NavigationView& nav); @@ -169,30 +92,28 @@ namespace ui { private: NavigationView& nav_; - void start_recon_thread(); + void audio_output_start(); + bool check_sd_card(); size_t change_mode( freqman_index_t mod_type); void show_max( bool refresh_display = false ); void recon_pause(); void recon_resume(); - void user_pause(); - void user_resume(); void frequency_file_load( bool stop_all_before = false); void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); - void set_display_freq( int64_t freq ); - void handle_retune( int64_t freq , uint32_t index ); - bool check_sd_card(); + void on_index_delta(int32_t v); + void on_stepper_delta(int32_t v); + void recon_redraw(); + void handle_retune(); void handle_coded_squelch(const uint32_t value); jammer::jammer_range_t frequency_range { false, 0, MAX_UFREQ }; //perfect for manual recon task too... int32_t squelch { 0 }; int32_t db { 0 }; int32_t timer { 0 }; - int32_t wait { 1000 }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity - uint32_t lock_wait { 1000 }; // in msec. Represent the maximum amount of time we will wait for a lock to complete before switching to next - int32_t def_step { 0 }; + int32_t wait { RECON_DEF_WAIT_DURATION }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity freqman_db frequency_list = { }; - uint32_t current_index { 0 }; + int32_t current_index { 0 }; bool userpause { false }; bool continuous_lock { false }; std::string input_file = { "RECON" }; @@ -206,13 +127,28 @@ namespace ui { bool load_hamradios = { true }; bool update_ranges = { true }; bool fwd = { true }; + bool recon = true ; uint32_t recon_lock_nb_match = { 3 }; - uint32_t recon_lock_duration = { 50 }; - uint32_t recon_match_mode = { 0 }; + uint32_t recon_lock_duration = { RECON_DEF_LOCK_DURATION }; + uint32_t recon_match_mode = { RECON_MATCH_CONTINUOUS }; bool scanner_mode { false }; bool manual_mode { false }; bool sd_card_mounted = false ; int32_t volume = 40 ; + int32_t stepper = 0 ; + int32_t index_stepper = 0 ; + int64_t freq = 0 ; + uint32_t step = 0 ; + freqman_index_t def_modulation = 0 ; + freqman_index_t def_bandwidth = 0 ; + freqman_index_t def_step = 0 ; + tone_index tone = 0 ; + freqman_entry last_entry = { } ; + bool entry_has_changed = false ; + uint32_t freq_lock { 0 }; + int64_t minfreq = 0 ; + int64_t maxfreq = 0 ; + bool has_looped = false ; Labels labels { @@ -260,21 +196,21 @@ namespace ui { NumberField field_wait { { 20 * 8, 1 * 16 }, 5, - { -9000, 9000 }, - 100, + { -(10000-STATS_UPDATE_INTERVAL) , (10000-STATS_UPDATE_INTERVAL) }, + STATS_UPDATE_INTERVAL, ' ', }; NumberField field_lock_wait { { 26 * 8, 1 * 16 }, 4, - { 100 , 9000 }, - 100, + { RECON_DEF_LOCK_DURATION , (10000-RECON_DEF_LOCK_DURATION) }, + RECON_DEF_LOCK_DURATION, ' ', }; RSSI rssi { - { 0 * 16, 2 * 16, 240 - 8 * 8 + 4 , 16 }, + { 0 * 16, 2 * 16, SCREEN_W - 8 * 8 + 4 , 16 }, }; ButtonWithEncoder text_cycle { @@ -283,11 +219,11 @@ namespace ui { }; Text text_max { - { 4 * 8, 3 * 16, 240 - 7 * 8 - 4 * 8 , 16 }, + { 4 * 8, 3 * 16, SCREEN_W - 7 * 8 - 4 * 8 , 16 }, }; Text desc_cycle { - {0, 4 * 16, 240 , 16 }, + {0, 4 * 16, SCREEN_W , 16 }, }; /* BigFrequency big_display { //Show frequency in glamour @@ -315,23 +251,23 @@ namespace ui { }; Button button_recon_setup { - { 240 - 7 * 8 , 2 * 16 , 7 * 8, 28 }, + { SCREEN_W - 7 * 8 , 2 * 16 , 7 * 8, 28 }, "CONFIG" }; Button button_looking_glass { - { 240 - 7 * 8 , 5 * 16 , 7 * 8, 28 }, + { SCREEN_W - 7 * 8 , 5 * 16 , 7 * 8, 28 }, "GLASS" }; // Button can be RECON or SCANNER Button button_scanner_mode { - { 240 - 7 * 8 , 8 * 16 , 7 * 8, 28 }, + { SCREEN_W - 7 * 8 , 8 * 16 , 7 * 8, 28 }, "RECON" }; Text file_name { //Show file used - { 0 , 8 * 16 + 6 , 240 - 7 * 8, 16 }, + { 0 , 8 * 16 + 6 , SCREEN_W - 7 * 8, 16 }, }; ButtonWithEncoder button_manual_start { @@ -400,8 +336,6 @@ namespace ui { "" }; - std::unique_ptr recon_thread { }; - MessageHandlerRegistration message_handler_coded_squelch { Message::ID::CodedSquelch, [this](const Message* const p) { @@ -410,14 +344,6 @@ namespace ui { } }; - MessageHandlerRegistration message_handler_retune { - Message::ID::Retune, - [this](const Message* const p) { - const auto message = *reinterpret_cast(p); - this->handle_retune(message.freq,message.range); - } - }; - MessageHandlerRegistration message_handler_stats { Message::ID::ChannelStatistics, [this](const Message* const p) { diff --git a/firmware/application/apps/ui_recon_settings.cpp b/firmware/application/apps/ui_recon_settings.cpp index 5025fce0..5bd30b36 100644 --- a/firmware/application/apps/ui_recon_settings.cpp +++ b/firmware/application/apps/ui_recon_settings.cpp @@ -34,7 +34,7 @@ using namespace portapack; namespace ui { - bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume ) + bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ) { File settings_file; size_t length, file_position = 0; @@ -44,8 +44,8 @@ namespace ui { char file_data[257]; uint32_t it = 0 ; - uint32_t nb_params = 9 ; - std::string params[ 9 ]; + uint32_t nb_params = RECON_SETTINGS_NB_PARAMS ; + std::string params[ RECON_SETTINGS_NB_PARAMS ]; bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ; @@ -91,12 +91,11 @@ namespace ui { /* bad number of params, signal defaults */ input_file = "RECON" ; output_file = "RECON_RESULTS" ; - recon_lock_duration = 50 ; - recon_lock_nb_match = 3 ; + recon_lock_duration = RECON_DEF_LOCK_DURATION ; + recon_lock_nb_match = RECON_DEF_NB_MATCH ; recon_squelch_level = -14 ; - recon_match_mode = 0 ; - wait = 1000 ; - lock_wait = 1000 ; + recon_match_mode = RECON_MATCH_CONTINUOUS ; + wait = RECON_DEF_WAIT_DURATION ; volume = 40 ; return false ; } @@ -114,12 +113,12 @@ namespace ui { if( it > 2 ) recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); else - recon_lock_duration = 50 ; + recon_lock_duration = RECON_DEF_LOCK_DURATION ; if( it > 3 ) recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); else - recon_lock_nb_match = 3 ; + recon_lock_nb_match = RECON_DEF_NB_MATCH ; if( it > 4 ) recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 ); @@ -129,17 +128,12 @@ namespace ui { if( it > 5 ) recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 ); else - recon_match_mode = 0 ; + recon_match_mode = RECON_MATCH_CONTINUOUS ; if( it > 6 ) wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); else - wait = 1000 ; - - if( it > 7 ) - lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 ); - else - lock_wait = 1000 ; + wait = RECON_DEF_WAIT_DURATION ; if( it > 8 ) volume = strtoll( params[ 8 ].c_str() , nullptr , 10 ); @@ -149,7 +143,7 @@ namespace ui { return true ; } - bool ReconSetupSaveStrings( std::string dest, std::string input_file , std::string output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume ) + bool ReconSetupSaveStrings( std::string dest, std::string input_file , std::string output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ) { File settings_file; @@ -163,7 +157,6 @@ namespace ui { settings_file.write_line( to_string_dec_int( recon_squelch_level ) ); settings_file.write_line( to_string_dec_uint( recon_match_mode ) ); settings_file.write_line( to_string_dec_int( wait ) ); - settings_file.write_line( to_string_dec_uint( lock_wait ) ); settings_file.write_line( to_string_dec_int( volume ) ); return true ; } diff --git a/firmware/application/apps/ui_recon_settings.hpp b/firmware/application/apps/ui_recon_settings.hpp index 62bcd86d..ab02f426 100644 --- a/firmware/application/apps/ui_recon_settings.hpp +++ b/firmware/application/apps/ui_recon_settings.hpp @@ -20,6 +20,9 @@ * Boston, MA 02110-1301, USA. */ +#ifndef _UI_RECON_SETTINGS +#define _UI_RECON_SETTINGS + #include "serializer.hpp" #include "ui.hpp" #include "ui_widget.hpp" @@ -27,10 +30,38 @@ #include "ui_navigation.hpp" #include "string_format.hpp" +// maximum usable freq +#define MAX_UFREQ 7200000000 + +// 1Mhz helper +#ifdef OneMHz + #undef OneMHz +#endif +#define OneMHz 1000000 + +// modes +#define RECON_MATCH_CONTINUOUS 0 +#define RECON_MATCH_SPARSE 1 + +// statistics update interval (change here if it's evolving) in ms +#define STATS_UPDATE_INTERVAL 100 + +// default number of match to have a lock +#define RECON_DEF_NB_MATCH 3 +#define RECON_DEF_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL +#define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps + +// screen size helper +#define SCREEN_W 240 +//#define SCREEN_H 320 + +// recon settings nb params +#define RECON_SETTINGS_NB_PARAMS 8 + namespace ui { - bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume ); - bool ReconSetupSaveStrings( std::string dest, const std::string input_file , const std::string output_file , const uint32_t recon_lock_duration , const uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume ); + bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ); + bool ReconSetupSaveStrings( std::string dest, const std::string input_file , const std::string output_file , const uint32_t recon_lock_duration , const uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ); class ReconSetupViewMain : public View { public: @@ -94,9 +125,9 @@ namespace ui { private: - const uint32_t _recon_lock_duration = 50 ; - const uint32_t _recon_lock_nb_match = 10 ; - const uint32_t _recon_match_mode = 0 ; + const uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL ; + const uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH ; + const uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS ; Checkbox checkbox_load_freqs { { 1 * 8, 12 }, @@ -128,8 +159,8 @@ namespace ui { NumberField field_recon_lock_duration { { 1 * 8, 132 }, // position X , Y 4, // number of displayed digits (even empty) - { 50 , 990 }, // range of number - 10, // rotary encoder increment + { STATS_UPDATE_INTERVAL , 10000-STATS_UPDATE_INTERVAL }, // range of number + STATS_UPDATE_INTERVAL, // rotary encoder increment ' ', // filling character false // can loop }; @@ -172,11 +203,11 @@ namespace ui { std::string input_file = { "RECON" }; std::string output_file = { "RECON_RESULTS" }; - uint32_t recon_lock_duration = 50 ; - uint32_t recon_lock_nb_match = 10 ; - uint32_t recon_match_mode = 0 ; + uint32_t recon_lock_duration = STATS_UPDATE_INTERVAL ; + uint32_t recon_lock_nb_match = RECON_DEF_NB_MATCH ; + uint32_t recon_match_mode = RECON_MATCH_CONTINUOUS ; - Rect view_rect = { 0, 3 * 8, 240, 230 }; + Rect view_rect = { 0, 3 * 8, SCREEN_W, 230 }; ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file }; ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode }; @@ -192,3 +223,5 @@ namespace ui { }; } /* namespace ui */ + +#endif