diff --git a/firmware/application/apps/ui_recon.cpp b/firmware/application/apps/ui_recon.cpp index 9a15d502..9d9479b4 100644 --- a/firmware/application/apps/ui_recon.cpp +++ b/firmware/application/apps/ui_recon.cpp @@ -39,1710 +39,1826 @@ 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 ); - } - - ReconThread::~ReconThread() { - stop(); - } - - void ReconThread::stop() { - if( thread ) { - chThdTerminate(thread); - chThdWait(thread); - thread = nullptr; - } - } - - - void ReconThread::set_recon(const bool v) { - _recon = v; - } - - void ReconThread::set_freq_delete(const bool v) { - _freq_delete = v; - } - - void ReconThread::set_stepper( const int64_t v){ - _stepper = v; - } - - void ReconThread::set_lock_duration( const uint32_t v ){ - _lock_duration = v; - } - - uint32_t ReconThread::get_lock_duration() { - return _lock_duration ; - } - - 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 uint32_t v) { - _freq_lock = v; - } - uint32_t ReconThread::is_freq_lock() { - return _freq_lock; - } - int64_t ReconThread::get_current_freq() { - return freq ; - } - - void ReconThread::change_recon_direction() { - _fwd = !_fwd; - // chThdSleepMilliseconds(300); //Give some pause after reversing recon direction - } - - 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 (frequency_list_.size() > 0 ) { //IF THERE IS A FREQUENCY LIST ... - int64_t minfreq = 0 ; - int64_t maxfreq = 0 ; - bool has_looped = false ; - bool entry_has_changed = 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 ) { - if( !_freq_delete ) - { - if( _recon || _stepper != 0 ) - { - if( _freq_lock == 0 || _stepper != 0 ) //normal recon (not performing freq_lock) - { - if( !restart_recon || _stepper != 0 ) - { - has_looped = false ; - entry_has_changed = false ; - - if( last_entry . frequency_a != freq ) - { - last_entry . frequency_a = freq ; - receiver_model.set_tuning_frequency( freq ); // Retune - message.freq = freq ; - message.range = frequency_index ; - EventDispatcher::send_message(message); - } - - // 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 ); - } - - /* 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 - { - restart_recon = false ; - } - } - // 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 ) - { - freq = minfreq ; - } - else if( ( !_fwd && _stepper == 0 ) || _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 ) - { - freq = minfreq ; - } - else if( ( !_fwd && _stepper == 0 ) || _stepper < 0 ) - { - freq = maxfreq ; - } - break; - default: - break; - } - - } - - // send a pause message with the right freq - if( has_looped && !_continuous ) - { - // signal pause to handle_retune - receiver_model.set_tuning_frequency( freq ); // Retune to actual freq - message.freq = freq ; - message.range = MSG_RECON_PAUSE ; - EventDispatcher::send_message(message); - } - if( _stepper < 0 ) _stepper ++ ; - if( _stepper > 0 ) _stepper -- ; - } - else - { - restart_recon = true ; - } - } - chThdSleepMilliseconds( _lock_duration ); //Needed to (eventually) stabilize the receiver into new freq - } - } - } - - bool ReconView::check_sd_card() - { - return (sd_card::status() == sd_card::Status::Mounted) ? true : false; - } - - void ReconView::set_display_freq( int64_t freq ) - { - int64_t freqMHz = ( freq / 1000000 ); - int64_t freqMhzFloatingPart = ( freq - ( 1000000 * freqMHz ) ) / 100 ; - big_display.set( "FREQ: "+to_string_dec_int( freqMHz )+"."+to_string_dec_int( freqMhzFloatingPart )+" 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 ; - } - } - } - } - - uint32_t freq_lock = recon_thread->is_freq_lock(); - - if( freq_lock == 0 ) { - //NO FREQ LOCK, ONGOING STANDARD SCANNING - if( index < 1000 && index < frequency_list.size() ) - { - text_cycle.set( to_string_dec_uint( index + 1 , 3 ) ); - if(frequency_list[index].description.size() > 0) desc_cycle.set( frequency_list[index].description ); //Show new description - } - 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 , recon_lock_duration , 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 && frequency_list.size() > 0 ) - { - int32_t nb_match = recon_thread->is_freq_lock(); - 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 ) ); - } - } - else - { - if( refresh_display ) - { - text_max.set( " " ); - 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, - &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 ; - change_mode(AM_MODULATION); //Start on AM - field_mode.set_by_value(AM_MODULATION); //Reflect the mode into the manual selector - - //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 ); - frequency_range.min = stored_freq - 1000000; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - frequency_range.max = stored_freq + 1000000; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - // 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(); - - //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 , recon_lock_duration , volume ); - - field_volume.set_value( volume ); - - // load auto common app settings - if( sd_card_mounted ) - { - 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_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - file_name.set( "USE:" ); - - field_squelch.set_value( squelch ); - - 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)); - }; - }; - - 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 < 1 ) - { - frequency_range.min = 1 ; - } - 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 ); - }; - - - - 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_manual_start.set_encoder_delta( 0 ); - }; - - 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_freq_lock( 0 ); - recon_thread-> set_stepper( 1 ); - } - else if( button_pause.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "set_freq_lock( 0 ); - recon_thread-> set_stepper( -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) - { - desc_cycle.set( frequency_list[current_index].description ); //Show new description - } - else - { - desc_cycle.set( "no description" ); //Show new description - show_max( true ); //UPDATE new list size on screen - } - text_cycle.set( 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( " " ); - 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 ) + ReconThread::ReconThread( freqman_db *database ) : frequency_list_ { *database } { + thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReconThread::static_fn, this ); + } + + ReconThread::~ReconThread() { + stop(); + } + + void ReconThread::stop() { + if( thread ) { + chThdTerminate(thread); + chThdWait(thread); + thread = nullptr; + } + } + + + void ReconThread::set_recon(const bool v) { + _recon = v; + } + + void ReconThread::set_freq_delete(const bool v) { + _freq_delete = v; + } + + void ReconThread::set_stepper( const int64_t v){ + _stepper = v; + } + + void ReconThread::set_index_stepper( const int64_t v){ + _index_stepper = v; + } + + void ReconThread::set_lock_duration( const uint32_t v ){ + _lock_duration = v; + } + + uint32_t ReconThread::get_lock_duration() { + return _lock_duration ; + } + + 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 uint32_t v) { + _freq_lock = v; + } + uint32_t ReconThread::is_freq_lock() { + return _freq_lock; + } + int64_t ReconThread::get_current_freq() { + return freq ; + } + + void ReconThread::change_recon_direction() { + _fwd = !_fwd; + // chThdSleepMilliseconds(300); //Give some pause after reversing recon direction + } + + 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 (frequency_list_.size() > 0 ) { //IF THERE IS A FREQUENCY LIST ... + 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 + int16_t last_index = -1 ; + + while( !chThdShouldTerminate() && frequency_list_.size() > 0 ) { + has_looped = false ; + entry_has_changed = false ; + if( !_freq_delete ) + { + if( _recon || _stepper != 0 || _index_stepper != 0 || last_index !=frequency_index ) + { + last_index = frequency_index ; + if( last_entry . frequency_a != freq ) + { + last_entry . frequency_a = freq ; + receiver_model.set_tuning_frequency( freq ); // Retune + message.freq = freq ; + message.range = frequency_index ; + EventDispatcher::send_message(message); + } + // 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( _freq_lock == 0 || _stepper != 0 ) //normal recon (not performing freq_lock) + { + if( !restart_recon || _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 + { + restart_recon = false ; + } + } + } + else + { + restart_recon = true ; + } + if( _index_stepper != 0 ) + { + 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 + receiver_model.set_tuning_frequency( freq ); // Retune to actual freq + message.freq = freq ; + message.range = MSG_RECON_PAUSE ; + EventDispatcher::send_message(message); + } + if( _stepper < 0 ) _stepper ++ ; + if( _stepper > 0 ) _stepper -- ; + if( _index_stepper < 0 ) _index_stepper ++ ; + if( _index_stepper > 0 ) _index_stepper -- ; + + } + chThdSleepMilliseconds( _lock_duration ); //Needed to (eventually) stabilize the receiver into new freq + } + } + } + + bool ReconView::check_sd_card() + { + return (sd_card::status() == sd_card::Status::Mounted) ? true : false; + } + + void ReconView::set_display_freq( int64_t freq ) + { + int64_t freqMHz = ( freq / 1000000 ); + int64_t freqMhzFloatingPart = ( freq - ( 1000000 * freqMHz ) ) / 100 ; + big_display.set( "FREQ: "+to_string_dec_int( freqMHz )+"."+to_string_dec_int( freqMhzFloatingPart )+" 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 ) { - user_pause(); + 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 ; + } + } + } + } + + uint32_t freq_lock = recon_thread->is_freq_lock(); + + if( freq_lock == 0 ) { + //NO FREQ LOCK, ONGOING STANDARD SCANNING + if( index < 1000 && index < frequency_list.size() ) + { + 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 ; + } + } + } + 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 , recon_lock_duration , 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 && frequency_list.size() > 0 ) + { + int32_t nb_match = recon_thread->is_freq_lock(); + 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 ) ); + } } else { - user_resume(); + if( refresh_display ) + { + text_max.set( to_string_dec_int( db ) + " db" ); + 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 ; } - 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_freq_lock( 0 ); - recon_thread-> set_stepper( 1 ); - } - else if( button_remove.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "set_freq_lock( 0 ); - recon_thread-> set_stepper( -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( "MANUAL SEARCH" ); - button_scanner_mode.set_style( &style_white ); - button_scanner_mode.set_text( "MSEARCH" ); - file_name.set_style( &style_white ); - file_name.set( "USE: MANUAL RANGE" ); - - 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( 0 ); - } - else if( button_add.get_encoder_delta() < 0 ) - { - fwd = false ; - recon_thread -> set_recon_direction( fwd ); - button_dir.set_text( "set_freq_lock( 0 ); - recon_thread-> set_stepper( -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&) { - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume.value() ); - - user_pause(); - - 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 , recon_lock_duration , 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( 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(); - } - - 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 ; - } - } - - lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ; - lock_wait = lock_wait * 100 ; // poor man's rounding - if( lock_wait < 400 ) - lock_wait = 400 ; - field_lock_wait.set_value( lock_wait ); - show_max(); - return ; - }; - - if( userpause != true ) - { - timer = 0 ; - user_resume(); - } - else - { - RetuneMessage message { }; - message.freq = recon_thread->get_current_freq(); - message.range = current_index ; - EventDispatcher::send_message(message); - } - }; - - //PRE-CONFIGURATION: - 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_wait.set_value(wait); - lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ); - lock_wait = lock_wait / 100 ; lock_wait = lock_wait * 100 ; // poor man's rounding - if( lock_wait < 400 ) - lock_wait = 400 ; - field_lock_wait.set_value(lock_wait); - - field_squelch.on_change = [this](int32_t v) { - squelch = v ; - }; - field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; - 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 ); - - if( filedelete ) - { - delete_file( "FREQMAN/"+output_file+".TXT" ); - } - - frequency_file_load( false ); /* do not stop all at start */ - if( recon_thread && frequency_list.size() > 0 ) - { - recon_thread->set_lock_duration( recon_lock_duration ); - recon_thread->set_lock_nb_match( recon_lock_nb_match ); - 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 ; - } - } - - 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(); - - if( !scanner_mode) - { - def_step = step_mode.selected_index(); //Use def_step from manual selector - frequency_list.clear(); // clear the existing frequency list (expected behavior) - if( !load_freqman_file_ex( input_file , frequency_list, load_freqs, load_ranges, load_hamradios ) ) - { - desc_cycle.set(" NO " + input_file + ".TXT FILE ..." ); - file_name.set_style( &style_white ); - file_name.set( "USE: NO DATA" ); - } - else - { - file_name.set_style( &style_blue ); - file_name.set( "USE: "+input_file ); - } - step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector - } - else - { - def_step = step_mode.selected_index(); //Use def_step from manual selector - frequency_list.clear(); // clear the existing frequency list (expected behavior) - if( !load_freqman_file_ex( output_file , frequency_list, load_freqs, load_ranges, load_hamradios ) ) - { - desc_cycle.set(" NO " + output_file + ".TXT FILE ..." ); - file_name.set_style( &style_white ); - file_name.set( "USE:" ); - } - else - { - file_name.set( "USE: "+output_file ); - file_name.set_style( &style_red ); - } - step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector - } - - start_recon_thread(); - } - - 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( 0 ); //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 // 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 ; - } - } - } - } - 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(); - } - - timer -= 50 ; - if( timer < 0 ) - { - timer = 0 ; - } - } /* 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); - 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); - 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 ); - } - + def_step = 0 ; + change_mode(AM_MODULATION); //Start on AM + field_mode.set_by_value(AM_MODULATION); //Reflect the mode into the manual selector + + //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 ); + frequency_range.min = stored_freq - 1000000; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + frequency_range.max = stored_freq + 1000000; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + // 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(); + + //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 , recon_lock_duration , volume ); + + field_volume.set_value( volume ); + + // load auto common app settings + if( sd_card_mounted ) + { + 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_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + file_name.set( "USE:" ); + + field_squelch.set_value( squelch ); + + 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 / 1000000 ; + if( f >= 1 && f <= frequency_list.size() ) + { + recon_thread-> set_index_stepper( f - 1 - current_index ); + recon_thread->set_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 < 1 ) + { + frequency_range.min = 1 ; + } + 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( 0 ); + } + 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( 0 ); + } + } + 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( 0 ); + } + 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( 0 ); + } + } + 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 ); //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 + show_max( true ); //UPDATE new list size on screen + } + 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( 0 ); + } + 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( 0 ); + } + } + 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( "MANUAL SEARCH" ); + button_scanner_mode.set_style( &style_white ); + button_scanner_mode.set_text( "MSEARCH" ); + file_name.set_style( &style_white ); + file_name.set( "USE: MANUAL RANGE" ); + + 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( 0 ); + } + 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( 0 ); + } + } + 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&) { + + ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume.value() ); + + user_pause(); + + 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 , recon_lock_duration , 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( 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(); + } + + 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 ; + } + } + + lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ; + lock_wait = lock_wait * 100 ; // poor man's rounding + if( lock_wait < 400 ) + lock_wait = 400 ; + field_lock_wait.set_value( lock_wait ); + show_max(); + return ; + }; + + if( userpause != true ) + { + timer = 0 ; + user_resume(); + } + else + { + RetuneMessage message { }; + message.freq = recon_thread->get_current_freq(); + message.range = current_index ; + EventDispatcher::send_message(message); + } + }; + + //PRE-CONFIGURATION: + 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_wait.set_value(wait); + lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ); + lock_wait = lock_wait / 100 ; lock_wait = lock_wait * 100 ; // poor man's rounding + if( lock_wait < 400 ) + lock_wait = 400 ; + field_lock_wait.set_value(lock_wait); + + field_squelch.on_change = [this](int32_t v) { + squelch = v ; + }; + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + 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 ); + + if( filedelete ) + { + delete_file( "FREQMAN/"+output_file+".TXT" ); + } + + frequency_file_load( false ); /* do not stop all at start */ + if( recon_thread && frequency_list.size() > 0 ) + { + recon_thread->set_lock_duration( recon_lock_duration ); + recon_thread->set_lock_nb_match( recon_lock_nb_match ); + 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 ; + } + } + + 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(); + + if( !scanner_mode) + { + def_step = step_mode.selected_index(); //Use def_step from manual selector + frequency_list.clear(); // clear the existing frequency list (expected behavior) + if( !load_freqman_file_ex( input_file , frequency_list, load_freqs, load_ranges, load_hamradios ) ) + { + desc_cycle.set(" NO " + input_file + ".TXT FILE ..." ); + file_name.set_style( &style_white ); + file_name.set( "USE: NO DATA" ); + } + else + { + file_name.set_style( &style_blue ); + file_name.set( "USE: "+input_file + ".TXT" ); + } + step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector + } + else + { + def_step = step_mode.selected_index(); //Use def_step from manual selector + frequency_list.clear(); // clear the existing frequency list (expected behavior) + if( !load_freqman_file_ex( output_file , frequency_list, load_freqs, load_ranges, load_hamradios ) ) + { + desc_cycle.set(" NO " + output_file + ".TXT FILE ..." ); + file_name.set_style( &style_white ); + file_name.set( "USE:" ); + } + else + { + file_name.set( "USE: "+output_file + ".TXT" ); + file_name.set_style( &style_red ); + } + step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector + } + + start_recon_thread(); + } + + 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( 0 ); //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 // 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 ; + } + } + } + } + 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(" "); + } + } } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon.hpp b/firmware/application/apps/ui_recon.hpp index 0fd4b5ce..a7543103 100644 --- a/firmware/application/apps/ui_recon.hpp +++ b/firmware/application/apps/ui_recon.hpp @@ -42,367 +42,388 @@ namespace ui { - class ReconThread { - public: - ReconThread(freqman_db *database ); - ~ReconThread(); + 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_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_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 uint32_t v); - uint32_t is_freq_lock(); - int64_t get_current_freq(); + void set_freq_lock(const uint32_t v); + uint32_t is_freq_lock(); + int64_t get_current_freq(); - void set_stepper(const int64_t v); + 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 change_recon_direction(); + bool get_recon_direction(); + void set_recon_direction( const bool v); - void set_continuous(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 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(); + void run(); + void stop(); - ReconThread(const ReconThread&) = delete; - ReconThread(ReconThread&&) = delete; - ReconThread& operator=(const ReconThread&) = delete; - ReconThread& operator=(ReconThread&&) = delete; + 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 ; + 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 }; - int64_t _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); - }; + 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); - ~ReconView(); + class ReconView : public View { + public: + ReconView(NavigationView& nav); + ~ReconView(); - void focus() override; + void focus() override; - void big_display_freq( int64_t f ); + void big_display_freq( int64_t f ); - const Style style_grey { // recon - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; + const Style style_grey { // recon + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; - const Style style_white { // recon - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::white(), - }; + const Style style_white { // recon + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::white(), + }; - const Style style_yellow { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::yellow(), - }; + const Style style_yellow { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::yellow(), + }; - const Style style_green { //Found signal - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; + const Style style_green { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; - const Style style_red { //erasing freq - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; + const Style style_red { //erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; - const Style style_blue { // quick recon, wait == 0 - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::blue(), - }; + const Style style_blue { // quick recon, wait == 0 + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::blue(), + }; - std::string title() const override { return "Recon"; }; + std::string title() const override { return "Recon"; }; - //void set_parent_rect(const Rect new_parent_rect) override; + //void set_parent_rect(const Rect new_parent_rect) override; - private: - NavigationView& nav_; + private: + NavigationView& nav_; - void start_recon_thread(); - 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 start_recon_thread(); + 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 handle_coded_squelch(const uint32_t value); - jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual recon task too... - int32_t squelch { 0 }; - int32_t db { 0 }; - int32_t timer { 0 }; - int32_t wait { 5000 }; // 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 { 500 }; // 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 }; - freqman_db frequency_list = { }; - uint32_t current_index { 0 }; - bool userpause { false }; - bool continuous_lock { false }; - std::string input_file = { "RECON" }; - std::string output_file = { "RECON_RESULTS" }; - bool autosave = { true }; - bool autostart = { true }; - bool continuous = { true }; - bool filedelete = { true }; - bool load_freqs = { true }; - bool load_ranges = { true }; - bool load_hamradios = { true }; - bool update_ranges = { true }; - bool fwd = { true }; - // maximum usable freq - long long int MAX_UFREQ = { 7200000000 }; - uint32_t recon_lock_nb_match = { 10 }; - uint32_t recon_lock_duration = { 50 }; - uint32_t recon_match_mode = { 0 }; - bool scanner_mode { false }; - bool manual_mode { false }; - bool sd_card_mounted = false ; - int32_t volume = 40 ; + jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual recon task too... + int32_t squelch { 0 }; + int32_t db { 0 }; + int32_t timer { 0 }; + int32_t wait { 5000 }; // 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 { 500 }; // 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 }; + freqman_db frequency_list = { }; + uint32_t current_index { 0 }; + bool userpause { false }; + bool continuous_lock { false }; + std::string input_file = { "RECON" }; + std::string output_file = { "RECON_RESULTS" }; + bool autosave = { true }; + bool autostart = { true }; + bool continuous = { true }; + bool filedelete = { true }; + bool load_freqs = { true }; + bool load_ranges = { true }; + bool load_hamradios = { true }; + bool update_ranges = { true }; + bool fwd = { true }; + // maximum usable freq + long long int MAX_UFREQ = { 7200000000 }; + uint32_t recon_lock_nb_match = { 10 }; + uint32_t recon_lock_duration = { 50 }; + uint32_t recon_match_mode = { 0 }; + bool scanner_mode { false }; + bool manual_mode { false }; + bool sd_card_mounted = false ; + int32_t volume = 40 ; - Labels labels - { - { { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() }, - { { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() }, - { { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() }, - { { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() }, - }; + Labels labels + { + { { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() }, + { { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() }, + { { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() }, + { { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() }, + { { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() }, + }; - LNAGainField field_lna { - { 4 * 8, 0 * 16 } - }; + LNAGainField field_lna { + { 4 * 8, 0 * 16 } + }; - VGAGainField field_vga { - { 11 * 8, 0 * 16 } - }; + VGAGainField field_vga { + { 11 * 8, 0 * 16 } + }; - RFAmpField field_rf_amp { - { 18 * 8, 0 * 16 } - }; + RFAmpField field_rf_amp { + { 18 * 8, 0 * 16 } + }; - NumberField field_volume { - { 24 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; + NumberField field_volume { + { 24 * 8, 0 * 16 }, + 2, + { 0, 99 }, + 1, + ' ', + }; - OptionsField field_bw { - { 3 * 8, 1 * 16 }, - 6, - { } - }; + OptionsField field_bw { + { 3 * 8, 1 * 16 }, + 6, + { } + }; - NumberField field_squelch { - { 12 * 8, 1 * 16 }, - 3, - { -90, 20 }, - 1, - ' ', - }; + NumberField field_squelch { + { 12 * 8, 1 * 16 }, + 3, + { -90, 20 }, + 1, + ' ', + }; - NumberField field_wait { - { 20 * 8, 1 * 16 }, - 5, - { -9000, 9000 }, - 100, - ' ', - }; + NumberField field_wait { + { 20 * 8, 1 * 16 }, + 5, + { -9000, 9000 }, + 100, + ' ', + }; - NumberField field_lock_wait { - { 26 * 8, 1 * 16 }, - 4, - { 100 , 9000 }, - 100, - ' ', - }; + NumberField field_lock_wait { + { 26 * 8, 1 * 16 }, + 4, + { 100 , 9000 }, + 100, + ' ', + }; - RSSI rssi { - { 0 * 16, 2 * 16, 240 - 4 * 8 , 16 }, - }; + RSSI rssi { + { 0 * 16, 2 * 16, 240 - 5 * 8 - 1 , 16 }, + }; - Text text_cycle { - { 0, 3 * 16, 3 * 8, 16 }, - }; + ButtonWithEncoder text_cycle { + { 0, 3 * 16, 3 * 8, 16 }, + "" + }; - Text text_max { - { 3 * 8, 3 * 16, 20 * 8 , 16 }, - }; + Text text_max { + { 3 * 8, 3 * 16, 20 * 8 , 16 }, + }; - Text desc_cycle { - {0, 4 * 16, 240 , 16 }, - }; + Text desc_cycle { + {0, 4 * 16, 240 , 16 }, + }; - /* BigFrequency big_display { //Show frequency in glamour - { 4, 7 * 16 - 8 , 28 * 8, 52 }, - 0 - }; */ + /* BigFrequency big_display { //Show frequency in glamour + { 4, 7 * 16 - 8 , 28 * 8, 52 }, + 0 + }; */ - Text big_display { //Show frequency in text mode - { 0, 5 * 16 , 21 * 8, 16 }, - }; + Text big_display { //Show frequency in text mode + { 0, 5 * 16 , 21 * 8, 16 }, + }; - Text freq_stats { //Show frequency stats in text mode - { 0, 6 * 16 , 21 * 8, 16 }, - }; + Text freq_stats { //Show frequency stats in text mode + { 0, 6 * 16 , 21 * 8, 16 }, + }; - Text text_timer { //Show frequency stats in text mode - { 0, 7 * 16 , 21 * 8, 16 }, - }; + // TIMER: 9999 + Text text_timer { //Show frequency stats in text mode + { 0, 7 * 16 , 11 * 8, 16 }, + }; - Button button_recon_setup { - { 240 - 4 * 8 , 2 * 16 , 4 * 8, 28 }, - "OPT" - }; + // T: Senn. 32.000k + Text text_ctcss { + { 12 * 8 + 4, 7 * 16 , 14 * 8, 1 * 8 }, + "" + }; - Button button_looking_glass { - { 240 - 5 * 8 , 6 * 16 - 4 , 5 * 8, 28 }, - "GLASS" - }; + Button button_recon_setup { + { 240 - 5 * 8 , 2 * 16 , 5 * 8, 28 }, + "OPT" + }; - Button button_scanner_mode { - { 240 - 8 * 8 , 8 * 16 , 8 * 8, 28 }, - "RECON" - }; + Button button_looking_glass { + { 240 - 5 * 8 , 5 * 16 , 5 * 8, 28 }, + "GLASS" + }; - Text file_name { //Show file used - { 0 , 8 * 16 + 6 , 21 * 8, 16 }, - }; + // Button can be RECON or SCANNER + Button button_scanner_mode { + { 240 - 7 * 8 , 8 * 16 , 7 * 8, 28 }, + "RECON" + }; - ButtonWithEncoder button_manual_start { - { 0 * 8, 11 * 16, 11 * 8, 28 }, - "" - }; + Text file_name { //Show file used + { 0 , 8 * 16 + 6 , 240 - 7 * 8, 16 }, + }; - ButtonWithEncoder button_manual_end { - { 12 * 8 - 6, 11 * 16, 11 * 8, 28 }, - "" - }; + ButtonWithEncoder button_manual_start { + { 0 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; - Button button_manual_recon { - { 23 * 8 - 3, 11 * 16, 7 * 8 , 28 }, - "SEARCH" - }; + ButtonWithEncoder button_manual_end { + { 12 * 8 - 6, 11 * 16, 11 * 8, 28 }, + "" + }; - OptionsField field_mode { - { 5 * 8, (26 * 8) + 4 }, - 6, - { - } - }; + Button button_manual_recon { + { 23 * 8 - 3, 11 * 16, 7 * 8 , 28 }, + "SEARCH" + }; - OptionsField step_mode { - { 17 * 8, (26 * 8) + 4 }, - 12, - { - } - }; + OptionsField field_mode { + { 5 * 8, (26 * 8) + 4 }, + 6, + { + } + }; - ButtonWithEncoder button_pause { - { 0, (15 * 16) - 4, 72, 28 }, - "PAUSE" - }; + OptionsField step_mode { + { 17 * 8, (26 * 8) + 4 }, + 12, + { + } + }; + + ButtonWithEncoder button_pause { + { 0, (15 * 16) - 4, 72, 28 }, + "PAUSE" + }; - Button button_audio_app { - { 84, (15 * 16) - 4, 72, 28 }, - "AUDIO" - }; + Button button_audio_app { + { 84, (15 * 16) - 4, 72, 28 }, + "AUDIO" + }; - ButtonWithEncoder button_add { - { 168, (15 * 16) - 4, 72, 28 }, - "" - }; + ButtonWithEncoder button_add { + { 168, (15 * 16) - 4, 72, 28 }, + "" + }; - Button button_dir { - { 0, (35 * 8) - 4, 34, 28 }, - "FW>" - }; + Button button_dir { + { 0, (35 * 8) - 4, 34, 28 }, + "FW>" + }; - Button button_restart { - { 38, (35 * 8) - 4, 34, 28 }, - "RST" - }; + Button button_restart { + { 38, (35 * 8) - 4, 34, 28 }, + "RST" + }; - Button button_mic_app { - { 84, (35 * 8) - 4, 72, 28 }, - "MIC TX" - }; + Button button_mic_app { + { 84, (35 * 8) - 4, 72, 28 }, + "MIC TX" + }; - ButtonWithEncoder button_remove { - { 168, (35 * 8) - 4, 72, 28 }, - "" - }; + ButtonWithEncoder button_remove { + { 168, (35 * 8) - 4, 72, 28 }, + "" + }; - std::unique_ptr recon_thread { }; + std::unique_ptr recon_thread { }; - 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_coded_squelch { + Message::ID::CodedSquelch, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_coded_squelch(message.value); + } + }; - MessageHandlerRegistration message_handler_stats { - Message::ID::ChannelStatistics, - [this](const Message* const p) { - this->on_statistics_update(static_cast(p)->statistics); - } - }; - // app save settings - std::app_settings settings { }; - std::app_settings::AppSettings app_settings { }; - }; + 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) { + this->on_statistics_update(static_cast(p)->statistics); + } + }; + // app save settings + std::app_settings settings { }; + std::app_settings::AppSettings app_settings { }; + }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon_settings.cpp b/firmware/application/apps/ui_recon_settings.cpp index 2a36b485..fbfbf796 100644 --- a/firmware/application/apps/ui_recon_settings.cpp +++ b/firmware/application/apps/ui_recon_settings.cpp @@ -34,268 +34,268 @@ 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 ) - { - File settings_file; - size_t length, file_position = 0; - char * pos; - char * line_start; - char * line_end; - char file_data[257]; + 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 ) + { + File settings_file; + size_t length, file_position = 0; + char * pos; + char * line_start; + char * line_end; + char file_data[257]; - uint32_t it = 0 ; - uint32_t nb_params = 9 ; - std::string params[ 9 ]; + uint32_t it = 0 ; + uint32_t nb_params = 9 ; + std::string params[ 9 ]; - bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ; + bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ; - if( check_sd_card ) - { - auto result = settings_file.open( source ); - if( !result.is_valid() ) - { - while( it < nb_params ) - { - // Read a 256 bytes block from file - settings_file.seek(file_position); - memset(file_data, 0, 257); - auto read_size = settings_file.read(file_data, 256); - if (read_size.is_error()) - break ; - file_position += 256; - // Reset line_start to beginning of buffer - line_start = file_data; - pos=line_start; - while ((line_end = strstr(line_start, "\x0A"))) { - length = line_end - line_start - 1 ; - params[ it ] = string( pos , length ); - it ++ ; - line_start = line_end + 1; - pos=line_start ; - if (line_start - file_data >= 256) - break; - if( it >= nb_params ) - break ; - } - if (read_size.value() != 256) - break; // End of file + if( check_sd_card ) + { + auto result = settings_file.open( source ); + if( !result.is_valid() ) + { + while( it < nb_params ) + { + // Read a 256 bytes block from file + settings_file.seek(file_position); + memset(file_data, 0, 257); + auto read_size = settings_file.read(file_data, 256); + if (read_size.is_error()) + break ; + file_position += 256; + // Reset line_start to beginning of buffer + line_start = file_data; + pos=line_start; + while ((line_end = strstr(line_start, "\x0A"))) { + length = line_end - line_start - 1 ; + params[ it ] = string( pos , length ); + it ++ ; + line_start = line_end + 1; + pos=line_start ; + if (line_start - file_data >= 256) + break; + if( it >= nb_params ) + break ; + } + if (read_size.value() != 256) + break; // End of file - // Restart at beginning of last incomplete line - file_position -= (file_data + 256 - line_start); - } - } + // Restart at beginning of last incomplete line + file_position -= (file_data + 256 - line_start); + } + } - } + } - if( it > 0 ) - input_file = params[ 0 ]; - else - input_file = "RECON" ; + if( it > 0 ) + input_file = params[ 0 ]; + else + input_file = "RECON" ; - if( it > 1 ) - output_file= params[ 1 ]; - else - output_file = "RECON_RESULTS" ; + if( it > 1 ) + output_file= params[ 1 ]; + else + output_file = "RECON_RESULTS" ; - if( it > 2 ) - recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); - else - recon_lock_duration = 50 ; + if( it > 2 ) + recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); + else + recon_lock_duration = 50 ; - if( it > 3 ) - recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); - else - recon_lock_nb_match = 10 ; + if( it > 3 ) + recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); + else + recon_lock_nb_match = 10 ; - if( it > 4 ) - recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 ); - else - recon_squelch_level = -14 ; + if( it > 4 ) + recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 ); + else + recon_squelch_level = -14 ; - if( it > 5 ) - recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 ); - else - recon_match_mode = 0 ; + if( it > 5 ) + recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 ); + else + recon_match_mode = 0 ; - if( it > 6 ) - wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); - else - wait = 5000 ; + if( it > 6 ) + wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); + else + wait = 5000 ; - if( it > 7 ) - lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 ); - else - lock_wait = 1000 ; + if( it > 7 ) + lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 ); + else + lock_wait = 1000 ; - if( it > 8 ) - volume = strtoll( params[ 8 ].c_str() , nullptr , 10 ); - else - volume = 40 ; + if( it > 8 ) + volume = strtoll( params[ 8 ].c_str() , nullptr , 10 ); + else + volume = 40 ; - if( it < nb_params ) - { - /* bad number of params, signal defaults */ - return false ; - } - return true ; - } + if( it < nb_params ) + { + /* bad number of params, signal defaults */ + return false ; + } + 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 ) - { - File settings_file; + 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 ) + { + File settings_file; - auto result = settings_file.create( dest ); - if( result.is_valid() ) - return false ; - settings_file.write_line( input_file ); - settings_file.write_line( output_file ); - settings_file.write_line( to_string_dec_uint( recon_lock_duration ) ); - settings_file.write_line( to_string_dec_uint( recon_lock_nb_match ) ); - 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 ; - } + auto result = settings_file.create( dest ); + if( result.is_valid() ) + return false ; + settings_file.write_line( input_file ); + settings_file.write_line( output_file ); + settings_file.write_line( to_string_dec_uint( recon_lock_duration ) ); + settings_file.write_line( to_string_dec_uint( recon_lock_nb_match ) ); + 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 ; + } - ReconSetupViewMain::ReconSetupViewMain( NavigationView &nav , Rect parent_rect , std::string input_file , std::string output_file ) : View( parent_rect ) , _input_file { input_file } , _output_file { output_file } - { - hidden(true); - add_children({ - &button_load_freqs, - &text_input_file, - &button_save_freqs, - &button_output_file, - &checkbox_autosave_freqs, - &checkbox_autostart_recon, - &checkbox_continuous, - &checkbox_clear_output - }); + ReconSetupViewMain::ReconSetupViewMain( NavigationView &nav , Rect parent_rect , std::string input_file , std::string output_file ) : View( parent_rect ) , _input_file { input_file } , _output_file { output_file } + { + hidden(true); + add_children({ + &button_load_freqs, + &text_input_file, + &button_save_freqs, + &button_output_file, + &checkbox_autosave_freqs, + &checkbox_autostart_recon, + &checkbox_continuous, + &checkbox_clear_output + }); - checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() ); - checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() ); - checkbox_continuous.set_value( persistent_memory::recon_continuous() ); - checkbox_clear_output.set_value( persistent_memory::recon_clear_output() ); + checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() ); + checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() ); + checkbox_continuous.set_value( persistent_memory::recon_continuous() ); + checkbox_clear_output.set_value( persistent_memory::recon_clear_output() ); - text_input_file.set( _input_file ); - button_output_file.set_text( _output_file ); + text_input_file.set( _input_file ); + button_output_file.set_text( _output_file ); - button_load_freqs.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { - std::string dir_filter = "FREQMAN/"; - std::string str_file_path = new_file_path.string(); - if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder - // get the filename without txt extension so we can use load_freqman_file fcn - _input_file = new_file_path.stem().string(); - text_input_file.set( _input_file ); - } else { - nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); - } - }; - }; + button_load_freqs.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { + std::string dir_filter = "FREQMAN/"; + std::string str_file_path = new_file_path.string(); + if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder + // get the filename without txt extension so we can use load_freqman_file fcn + _input_file = new_file_path.stem().string(); + text_input_file.set( _input_file ); + } else { + nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); + } + }; + }; - button_save_freqs.on_select = [this, &nav](Button&) { - auto open_view = nav.push(".TXT"); - open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { - std::string dir_filter = "FREQMAN/"; - std::string str_file_path = new_file_path.string(); - if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder - _output_file = new_file_path.stem().string(); - button_output_file.set_text( _output_file ); - } else { - nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); - } - }; - }; + button_save_freqs.on_select = [this, &nav](Button&) { + auto open_view = nav.push(".TXT"); + open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) { + std::string dir_filter = "FREQMAN/"; + std::string str_file_path = new_file_path.string(); + if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder + _output_file = new_file_path.stem().string(); + button_output_file.set_text( _output_file ); + } else { + nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); + } + }; + }; - button_output_file.on_select =[this, &nav](Button&) { - text_prompt( nav, _output_file , 28 , - [this](std::string& buffer) { - _output_file = buffer ; - button_output_file.set_text( _output_file ); - } ); - }; - }; + button_output_file.on_select =[this, &nav](Button&) { + text_prompt( nav, _output_file , 28 , + [this](std::string& buffer) { + _output_file = buffer ; + button_output_file.set_text( _output_file ); + } ); + }; + }; - void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) { - persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value()); - persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value()); - persistent_memory::set_recon_continuous(checkbox_continuous.value()); - persistent_memory::set_recon_clear_output(checkbox_clear_output.value()); - input_file=_input_file ; - output_file=_output_file ; - }; - void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) { - persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value()); - persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value()); - persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value()); - persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value()); - recon_lock_duration = field_recon_lock_duration.value(); - recon_lock_nb_match = field_recon_lock_nb_match.value(); - recon_match_mode = field_recon_match_mode . selected_index_value() ; - }; + void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) { + persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value()); + persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value()); + persistent_memory::set_recon_continuous(checkbox_continuous.value()); + persistent_memory::set_recon_clear_output(checkbox_clear_output.value()); + input_file=_input_file ; + output_file=_output_file ; + }; + void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) { + persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value()); + persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value()); + persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value()); + persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value()); + recon_lock_duration = field_recon_lock_duration.value(); + recon_lock_nb_match = field_recon_lock_nb_match.value(); + recon_match_mode = field_recon_match_mode . selected_index_value() ; + }; - void ReconSetupViewMain::focus() { - button_load_freqs.focus(); - } + void ReconSetupViewMain::focus() { + button_load_freqs.focus(); + } - ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode } - { - (void)nav; - hidden(true); + ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode } + { + (void)nav; + hidden(true); - add_children({ - &checkbox_load_freqs, - &checkbox_load_ranges, - &checkbox_load_hamradios, - &checkbox_update_ranges_when_recon, - &text_recon_lock_duration, - &field_recon_lock_duration, - &text_recon_lock_nb, - &field_recon_lock_nb_match, - &field_recon_match_mode, - }); + add_children({ + &checkbox_load_freqs, + &checkbox_load_ranges, + &checkbox_load_hamradios, + &checkbox_update_ranges_when_recon, + &text_recon_lock_duration, + &field_recon_lock_duration, + &text_recon_lock_nb, + &field_recon_lock_nb_match, + &field_recon_match_mode, + }); - checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() ); - checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() ); - checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() ); - checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() ); - field_recon_lock_duration.set_value( _recon_lock_duration ); - field_recon_lock_nb_match.set_value( _recon_lock_nb_match ); - field_recon_match_mode.set_by_value( _recon_match_mode ); - }; + checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() ); + checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() ); + checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() ); + checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() ); + field_recon_lock_duration.set_value( _recon_lock_duration ); + field_recon_lock_nb_match.set_value( _recon_lock_nb_match ); + field_recon_match_mode.set_by_value( _recon_match_mode ); + }; - void ReconSetupViewMore::focus() { - checkbox_load_freqs.focus(); - } + void ReconSetupViewMore::focus() { + checkbox_load_freqs.focus(); + } - void ReconSetupView::focus() { - viewMain.focus(); - } + void ReconSetupView::focus() { + viewMain.focus(); + } - ReconSetupView::ReconSetupView( - NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode } - { - add_children({ - &tab_view, - &viewMain, - &viewMore, - &button_save - }); + ReconSetupView::ReconSetupView( + NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode } + { + add_children({ + &tab_view, + &viewMain, + &viewMore, + &button_save + }); - button_save.on_select = [this,&nav](Button&) { - viewMain.Save( input_file , output_file ); - viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode ); - std::vector messages ; - messages.push_back( input_file ); - messages.push_back( output_file ); - messages.push_back( to_string_dec_uint( recon_lock_duration ) ); - messages.push_back( to_string_dec_uint( recon_lock_nb_match ) ); - messages.push_back( to_string_dec_uint( recon_match_mode ) ); - on_changed( messages ); - nav.pop(); - }; - } + button_save.on_select = [this,&nav](Button&) { + viewMain.Save( input_file , output_file ); + viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode ); + std::vector messages ; + messages.push_back( input_file ); + messages.push_back( output_file ); + messages.push_back( to_string_dec_uint( recon_lock_duration ) ); + messages.push_back( to_string_dec_uint( recon_lock_nb_match ) ); + messages.push_back( to_string_dec_uint( recon_match_mode ) ); + on_changed( messages ); + nav.pop(); + }; + } } /* namespace ui */ diff --git a/firmware/application/apps/ui_recon_settings.hpp b/firmware/application/apps/ui_recon_settings.hpp index c475a634..62bcd86d 100644 --- a/firmware/application/apps/ui_recon_settings.hpp +++ b/firmware/application/apps/ui_recon_settings.hpp @@ -29,166 +29,166 @@ 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 , 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 ); - class ReconSetupViewMain : public View { - public: - ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file ); - void Save( std::string &input_file , std::string &output_file ); - void focus() override; + class ReconSetupViewMain : public View { + public: + ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file ); + void Save( std::string &input_file , std::string &output_file ); + void focus() override; - private: - std::string _input_file = { "RECON" }; - std::string _output_file = { "RECON_RESULTS" }; + private: + std::string _input_file = { "RECON" }; + std::string _output_file = { "RECON_RESULTS" }; - Button button_load_freqs { - { 1 * 8 , 12 , 18 * 8 , 22 }, - "select input file" - }; - Text text_input_file { - { 1 * 8 , 4 + 2 * 16, 18 * 8, 22 }, - "RECON" - }; + Button button_load_freqs { + { 1 * 8 , 12 , 18 * 8 , 22 }, + "select input file" + }; + Text text_input_file { + { 1 * 8 , 4 + 2 * 16, 18 * 8, 22 }, + "RECON" + }; - Button button_save_freqs { - { 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 }, - "select output file" - }; - Button button_output_file { - { 1 * 8 , 5 * 16 - 2, 18 * 8, 22 }, - "RECON_RESULTS" - }; + Button button_save_freqs { + { 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 }, + "select output file" + }; + Button button_output_file { + { 1 * 8 , 5 * 16 - 2, 18 * 8, 22 }, + "RECON_RESULTS" + }; - Checkbox checkbox_autosave_freqs { - { 1 * 8, 7 * 16 - 4 }, - 3, - "autosave freqs" - }; + Checkbox checkbox_autosave_freqs { + { 1 * 8, 7 * 16 - 4 }, + 3, + "autosave freqs" + }; - Checkbox checkbox_autostart_recon { - { 1 * 8, 9 * 16 - 4 }, - 3, - "autostart recon" - }; + Checkbox checkbox_autostart_recon { + { 1 * 8, 9 * 16 - 4 }, + 3, + "autostart recon" + }; - Checkbox checkbox_continuous { - { 1 * 8, 11 * 16 - 4 }, - 3, - "continuous" - }; - Checkbox checkbox_clear_output { - { 1 * 8, 13 * 16 - 4 }, - 3, - "clear output at start" - }; - }; + Checkbox checkbox_continuous { + { 1 * 8, 11 * 16 - 4 }, + 3, + "continuous" + }; + Checkbox checkbox_clear_output { + { 1 * 8, 13 * 16 - 4 }, + 3, + "clear output at start" + }; + }; - class ReconSetupViewMore : public View { - public: - ReconSetupViewMore( NavigationView& nav, Rect parent_rect , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode ); + class ReconSetupViewMore : public View { + public: + ReconSetupViewMore( NavigationView& nav, Rect parent_rect , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode ); - void Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ); + void Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ); - void focus() override; + void focus() override; - private: + 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 = 50 ; + const uint32_t _recon_lock_nb_match = 10 ; + const uint32_t _recon_match_mode = 0 ; - Checkbox checkbox_load_freqs { - { 1 * 8, 12 }, - 3, - "input: load freqs" - }; + Checkbox checkbox_load_freqs { + { 1 * 8, 12 }, + 3, + "input: load freqs" + }; - Checkbox checkbox_load_ranges { - { 1 * 8, 42 }, - 3, - "input: load ranges" - }; + Checkbox checkbox_load_ranges { + { 1 * 8, 42 }, + 3, + "input: load ranges" + }; - Checkbox checkbox_load_hamradios { - { 1 * 8, 72 }, - 3, - "input: load hamradios" - }; + Checkbox checkbox_load_hamradios { + { 1 * 8, 72 }, + 3, + "input: load hamradios" + }; - Checkbox checkbox_update_ranges_when_recon { - { 1 * 8, 102 }, - 3, - "auto update m-ranges" - }; - Text text_recon_lock_duration { - { 1 * 8 , 132 , 22 * 8 , 22 }, - " ms (lock duration)" - }; - 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 - ' ', // filling character - false // can loop - }; - Text text_recon_lock_nb { - { 1 * 8 , 162 , 25 * 8 , 22 }, - " x (nb lock to match freq)" - }; - NumberField field_recon_lock_nb_match { - { 1 * 8, 162 }, - 4, - { 1, 99 }, - 1, - ' ', - false - }; - OptionsField field_recon_match_mode { - { 1 * 8, 192 }, - 20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE - { - { "SQL MATCH: CONTINOUS" , 0 }, - { "SQL MATCH: SPARSE" , 1 } - } - }; - }; + Checkbox checkbox_update_ranges_when_recon { + { 1 * 8, 102 }, + 3, + "auto update m-ranges" + }; + Text text_recon_lock_duration { + { 1 * 8 , 132 , 22 * 8 , 22 }, + " ms (lock duration)" + }; + 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 + ' ', // filling character + false // can loop + }; + Text text_recon_lock_nb { + { 1 * 8 , 162 , 25 * 8 , 22 }, + " x (nb lock to match freq)" + }; + NumberField field_recon_lock_nb_match { + { 1 * 8, 162 }, + 4, + { 1, 99 }, + 1, + ' ', + false + }; + OptionsField field_recon_match_mode { + { 1 * 8, 192 }, + 20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE + { + { "SQL MATCH: CONTINOUS" , 0 }, + { "SQL MATCH: SPARSE" , 1 } + } + }; + }; - class ReconSetupView : public View { - public: - ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode ); + class ReconSetupView : public View { + public: + ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode ); - std::function messages )> on_changed { }; + std::function messages )> on_changed { }; - void focus() override; + void focus() override; - std::string title() const override { return "Recon setup"; }; + std::string title() const override { return "Recon setup"; }; - private: + private: - NavigationView& nav_ ; + NavigationView& nav_ ; - 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 ; + 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 ; - Rect view_rect = { 0, 3 * 8, 240, 230 }; + Rect view_rect = { 0, 3 * 8, 240, 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 }; + ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file }; + ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode }; - TabView tab_view { - { "Main", Color::cyan() , &viewMain }, - { "More", Color::green(), &viewMore } - }; - Button button_save { - { 9 * 8, 255, 14 * 8 , 40 }, - "SAVE" - }; - }; + TabView tab_view { + { "Main", Color::cyan() , &viewMain }, + { "More", Color::green(), &viewMore } + }; + Button button_save { + { 9 * 8, 255, 14 * 8 , 40 }, + "SAVE" + }; + }; } /* namespace ui */