diff --git a/firmware/application/apps/ui_recon.cpp b/firmware/application/apps/ui_recon.cpp index 751c636e..6f705fa2 100644 --- a/firmware/application/apps/ui_recon.cpp +++ b/firmware/application/apps/ui_recon.cpp @@ -29,1462 +29,1527 @@ using portapack::memory::map::backup_ram; namespace ui { - void ReconView::audio_output_start() - { - audio::output::start(); - this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); - } - - bool ReconView::check_sd_card() - { - return (sd_card::status() == sd_card::Status::Mounted) ? true : false; - } - - void ReconView::recon_redraw() { - - static int32_t last_db = 999999 ; - static uint32_t last_nb_match = 999999 ; - static uint32_t last_freq_lock = 999999 ; - static size_t last_list_size = 0 ; - static int8_t last_rssi_min = -127 ; - static int8_t last_rssi_med = -127 ; - static int8_t last_rssi_max = -127 ; - - if( last_rssi_min != rssi.get_min() || last_rssi_med != rssi.get_avg() || last_rssi_max != rssi.get_max() ) - { - last_rssi_min = rssi.get_min(); - last_rssi_med = rssi.get_avg(); - last_rssi_max = rssi.get_max(); - freq_stats.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+" db" ); - } - - if( last_entry . frequency_a != freq ) - { - last_entry . frequency_a = freq ; - big_display.set( "FREQ: "+to_string_short_freq( freq )+" MHz" ); - } - - if( last_db != db || last_list_size != frequency_list.size() || last_freq_lock != freq_lock || last_nb_match != recon_lock_nb_match ) - { - last_freq_lock = freq ; - last_list_size = frequency_list.size(); - last_db = db ; - last_nb_match = recon_lock_nb_match ; - text_max.set( "/" + to_string_dec_uint( frequency_list.size() ) + " " + to_string_dec_int( db ) + " db " + to_string_dec_uint( freq_lock ) + "/" + to_string_dec_uint( recon_lock_nb_match ) ); - if( freq_lock == 0 ) { - //NO FREQ LOCK, ONGOING STANDARD SCANNING - big_display.set_style(&style_white); - if( !userpause ) - button_pause.set_text(""); - else - button_pause.set_text(""); - } - else if( freq_lock == 1 && recon_lock_nb_match != 1 ) - { - //STARTING LOCK FREQ - big_display.set_style(&style_yellow); - button_pause.set_text(""); - } - else if( freq_lock >= recon_lock_nb_match ) - { - big_display.set_style( &style_green); - button_pause.set_text(""); - - //FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() - if( (!scanner_mode) && autosave && frequency_list.size() > 0 ) { - File recon_file; - std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = freq ; - entry . frequency_b = 0 ; - entry . modulation = last_entry.modulation ; - entry . bandwidth = last_entry.bandwidth ; - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - 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 ); - } - } - } - } - } - } - - void ReconView::handle_retune() { - static int32_t last_index = -1 ; - static int64_t last_freq = 0 ; - if( last_freq != freq ) - { - last_freq = freq ; - receiver_model.set_tuning_frequency( freq ); // Retune - } - if( frequency_list.size() > 0 ) - { - if( last_entry . modulation != frequency_list[ current_index ] . modulation && frequency_list[ current_index ] . modulation >= 0 ) - { - last_entry . modulation = frequency_list[ current_index ]. modulation; - field_mode.set_selected_index( frequency_list[ current_index ]. modulation ); - } - // Set bandwidth if any - if( last_entry . bandwidth != frequency_list[ current_index ] . bandwidth && frequency_list[ current_index ] . bandwidth >= 0 ) - { - last_entry . bandwidth = frequency_list[ current_index ]. bandwidth; - switch( frequency_list[ current_index ].modulation ) - { - case AM_MODULATION: - receiver_model.set_am_configuration( freq ); - break ; - case NFM_MODULATION: - receiver_model.set_nbfm_configuration( freq ); - break ; - case WFM_MODULATION: - receiver_model.set_wfm_configuration( freq ); - default: - break ; - } - field_bw.set_selected_index( freq ); - } - if( last_entry . step != frequency_list[ current_index ] . step && frequency_list[ current_index ] . step >= 0 ) - { - last_entry . step = frequency_list[ current_index ]. step ; - step = freqman_entry_get_step_value( last_entry . step ); - step_mode.set_selected_index( step ); - } - if( last_index != current_index ) - { - last_index = current_index ; - if( (int32_t)frequency_list.size() && current_index < (int32_t)frequency_list.size() && frequency_list[ current_index ] . type == RANGE ) - { - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - if(frequency_list[current_index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); //Show new description - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); //Show new description - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); //Show new description - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); //Show new description - } - } - } - } - - - void ReconView::focus() { - button_pause.focus(); - } - - ReconView::~ReconView() { - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); - - // save app settings - settings.save("recon", &app_settings); - - audio::output::stop(); - receiver_model.disable(); - baseband::shutdown(); - } - - - ReconView::ReconView( NavigationView& nav) : nav_ { nav } { - - add_children( { - &labels, - &field_lna, - &field_vga, - &field_rf_amp, - &field_volume, - &field_bw, - &field_squelch, - &field_wait, - &field_lock_wait, - &button_recon_setup, - &button_scanner_mode, - &button_looking_glass, - &file_name, - &rssi, - &text_cycle, - &text_max, - &desc_cycle, - &big_display, - &freq_stats, - &text_timer, - &text_ctcss, - &button_manual_start, - &button_manual_end, - &button_manual_recon, - &field_mode, - &step_mode, - &button_pause, - &button_audio_app, - &button_add, - &button_dir, - &button_restart, - &button_mic_app, - &button_remove - } ); - - // Recon directory - if( check_sd_card() ) { // Check to see if SD Card is mounted - make_new_directory( u"/RECON" ); - sd_card_mounted = true ; - } - def_step = 0 ; - //HELPER: Pre-setting a manual range, based on stored frequency - rf::Frequency stored_freq = persistent_memory::tuned_frequency(); - receiver_model.set_tuning_frequency( stored_freq ); - if( stored_freq - OneMHz > 0 ) - frequency_range.min = stored_freq - OneMHz ; - else - frequency_range.min = 0 ; - button_manual_start.set_text(to_string_short_freq(frequency_range.min)); - if( stored_freq + OneMHz < MAX_UFREQ ) - frequency_range.max = stored_freq + OneMHz ; - else - frequency_range.max = MAX_UFREQ ; - button_manual_end.set_text(to_string_short_freq(frequency_range.max)); - // Loading settings - autostart = persistent_memory::recon_autostart_recon(); - autosave = persistent_memory::recon_autosave_freqs(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - - field_volume.set_value( volume ); - if( sd_card_mounted ) - { - // load auto common app settings - auto rc = settings.load("recon", &app_settings); - if(rc == SETTINGS_OK) { - field_lna.set_value(app_settings.lna); - field_vga.set_value(app_settings.vga); - field_rf_amp.set_value(app_settings.rx_amp); - receiver_model.set_rf_amp(app_settings.rx_amp); - } - } - - button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) { - auto new_view = nav_.push(frequency_range.min); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.min = f; - button_manual_start.set_text(to_string_short_freq(f)); - }; - }; - - button_manual_end.on_select = [this, &nav](ButtonWithEncoder& button) { - auto new_view = nav.push(frequency_range.max); - new_view->on_changed = [this, &button](rf::Frequency f) { - frequency_range.max = f; - button_manual_end.set_text(to_string_short_freq(f)); - }; - }; - - text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { - if( frequency_list.size() > 0 ) - { - auto new_view = nav_.push(current_index); - new_view->on_changed = [this, &button](rf::Frequency f) { - f = f / OneMHz ; - if( f >= 1 && f <= frequency_list.size() ) - { - index_stepper = f - 1 - current_index ; - freq_lock = 0 ; - } - }; - } - }; - - button_manual_start.on_change = [this]() { - frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.min < 0 ) - { - frequency_range.min = 0 ; - } - if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - } - if( frequency_range.min > (frequency_range.max - freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.max = frequency_range.min + freqman_entry_get_step_value( def_step ); - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); - frequency_range.max = MAX_UFREQ ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_start.set_encoder_delta( 0 ); - }; - - button_manual_end.on_change = [this]() { - frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ); - } - if( frequency_range.max > MAX_UFREQ ) - { - frequency_range.max = MAX_UFREQ ; - } - if( frequency_range.max < (frequency_range.min + freqman_entry_get_step_value( def_step ) ) ) - { - frequency_range.min = frequency_range.max - freqman_entry_get_step_value( def_step ); - if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) - { - frequency_range.min = 1 ; - frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ) ; - } - } - button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); - button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); - button_manual_end.set_encoder_delta( 0 ); - }; - - text_cycle.on_change = [this]() { - on_index_delta( text_cycle.get_encoder_delta() ); - text_cycle.set_encoder_delta( 0 ); - }; - - button_pause.on_select = [this](ButtonWithEncoder&) { - if( frequency_list.size() > 0 ) - { - if( freq_lock > 0 ) - { - if( fwd ) - { - on_stepper_delta( 1 ); - } - else - { - on_stepper_delta( -1 ); - } - button_pause.set_text(""); //Show button for non continuous stop - } - else - { - if( userpause ) - { - recon_resume(); - } - else - { - recon_pause(); - } - } - } - }; - - button_pause.on_change = [this]() { - on_stepper_delta( button_pause.get_encoder_delta() ); - button_pause.set_encoder_delta( 0 ); - }; - - button_audio_app.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - button_looking_glass.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - - rssi.set_focusable(true); - rssi.set_peak( true , 500 ); - rssi.on_select = [this](RSSI&) { - nav_.pop(); - nav_.push(); - }; - - button_mic_app.on_select = [this](Button&) { - nav_.pop(); - nav_.push(); - }; - - button_remove.on_select = [this](ButtonWithEncoder&) { - if(frequency_list.size() > 0 ) - { - if( scanner_mode ) - { - if( current_index >= (int32_t)frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - frequency_list.erase( frequency_list.begin()+ current_index ); - if( current_index >= (int32_t)frequency_list.size() ) - { - current_index = frequency_list.size() - 1 ; - } - if( frequency_list.size() > 0 ) - { - if(frequency_list[current_index].description.size() > 0) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - desc_cycle.set( "R: " + frequency_list[current_index].description ); - break ; - case HAMRADIO: - desc_cycle.set( "H: " + frequency_list[current_index].description ); - break ; - default: - case SINGLE: - desc_cycle.set( "S: " + frequency_list[current_index].description ); - break ; - } - } - else - { - desc_cycle.set( "...no description..." ); - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - - File freqman_file; - - std::string freq_file_path = "FREQMAN/" + output_file + ".TXT"; - - delete_file( freq_file_path ); - - auto result = freqman_file.create( freq_file_path ); - if ( !result.is_valid() ) - { - for (size_t n = 0; n < frequency_list.size(); n++) - { - std::string line ; - get_freq_string( frequency_list[ n ] , line ); - freqman_file.write_line( line ); - } - } - } - } - else // RECON MODE / MANUAL, only remove matching from output - { - File recon_file; - File tmp_recon_file; - std::string freq_file_path = "/FREQMAN/"+output_file+".TXT" ; - std::string tmp_freq_file_path = "/FREQMAN/"+output_file+"TMP.TXT" ; - std::string frequency_to_add ; - - freqman_entry entry = frequency_list[ current_index ] ; - entry . frequency_a = freq ; - entry . frequency_b = 0 ; - entry . modulation = last_entry.modulation ; - entry . bandwidth = last_entry.bandwidth ; - entry . type = SINGLE ; - - get_freq_string( entry , frequency_to_add ); - - delete_file( tmp_freq_file_path ); - auto result = tmp_recon_file.create(tmp_freq_file_path); //First recon if freq is already in txt - // - if (!result.is_valid()) { - bool found=false; - result = recon_file.open(freq_file_path); //First recon if freq is already in txt - if (!result.is_valid()) { - char one_char[1]; //Read it char by char - std::string line; //and put read line in here - for (size_t pointer=0; pointer < recon_file.size();pointer++) { - recon_file.seek(pointer); - recon_file.read(one_char, 1); - if ((int)one_char[0] > 31) { //ascii space upwards - line += one_char[0]; //Add it to the textline - } - else if (one_char[0] == '\n') { //New Line - if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { - found=true; - } - else - { - tmp_recon_file.write_line( frequency_to_add ); - } - line.clear(); //Ready for next textline - } - } - if( found) - { - delete_file( freq_file_path ); - rename_file( tmp_freq_file_path , freq_file_path ); - } - else - { - delete_file( tmp_freq_file_path ); - } - } - } - } - receiver_model.set_tuning_frequency( frequency_list[ current_index ] . frequency_a ); // Retune - } - if( frequency_list.size() == 0 ) - { - text_cycle.set_text( " " ); - desc_cycle.set( "no entries in list" ); //Show new description - delete_file( "FREQMAN/"+output_file+".TXT" ); - } - timer = 0 ; - }; - - button_remove.on_change = [this]() { - on_stepper_delta( button_remove.get_encoder_delta() ); - button_remove.set_encoder_delta( 0 ); - }; - - button_manual_recon.on_select = [this](Button&) { - scanner_mode = false ; - manual_mode = true ; - recon_pause(); - - if (!frequency_range.min || !frequency_range.max) { - nav_.display_modal("Error", "Both START and END freqs\nneed a value"); - } else if (frequency_range.min > frequency_range.max) { - nav_.display_modal("Error", "END freq\nis lower than START"); - } else { - audio::output::stop(); - - frequency_list.clear(); - freqman_entry manual_freq_entry ; - - def_step = step_mode.selected_index(); // max range val - - manual_freq_entry . type = RANGE ; - manual_freq_entry . description = - "R " + to_string_short_freq(frequency_range.min) + ">" - + to_string_short_freq(frequency_range.max) + " S" // current Manual range - + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0,1) ; //euquiq: lame kludge to reduce spacing in step freq - manual_freq_entry . frequency_a = frequency_range.min ; // min range val - manual_freq_entry . frequency_b = frequency_range.max ; // max range val - manual_freq_entry . modulation = -1 ; - manual_freq_entry . bandwidth = -1 ; - manual_freq_entry . step = def_step ; - - frequency_list . push_back( manual_freq_entry ); - - big_display.set_style(&style_white); //Back to white color - - freq_stats.set_style(&style_white); - freq_stats.set( "0/0/0" ); - - text_cycle.set_text( "1" ); - text_max.set( "/1" ); - button_scanner_mode.set_style( &style_white ); - button_scanner_mode.set_text( "MSEARCH" ); - file_name.set_style( &style_white ); - file_name.set( "MANUAL RANGE RECON" ); - desc_cycle.set_style( &style_white ); - desc_cycle.set( "MANUAL RANGE RECON" ); - - current_index = 0 ; - freq = manual_freq_entry . frequency_a ; - handle_retune(); - recon_redraw(); - recon_resume(); - } - }; - - button_dir.on_select = [this](Button&) { - if( fwd ) - { - fwd = false ; - button_dir.set_text( "" ); - } - timer = 0 ; - if ( userpause ) //If user-paused, resume - recon_resume(); - }; - - button_restart.on_select = [this](Button&) { - if( frequency_list.size() > 0 ) - { - def_step = step_mode.selected_index(); //Use def_step from manual selector - frequency_file_load( true ); - if( fwd ) - { - button_dir.set_text( "FW>" ); - } - else - { - button_dir.set_text( " 31) { //ascii space upwards - line += one_char[0]; //Add it to the textline - } - else if (one_char[0] == '\n') { //New Line - if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { - found=true; - break; - } - line.clear(); //Ready for next textline - } - } - if( !found) { - result = recon_file.append(freq_file_path); //Second: append if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - if (found) { - nav_.display_modal("Error", "Frequency already exists"); - } - } - else - { - auto result = recon_file.create(freq_file_path); //third: create if it is not there - if( !result.is_valid() ) - { - recon_file.write_line( frequency_to_add ); - } - } - } - } - }; - - button_add.on_change = [this]() { - on_stepper_delta( button_add.get_encoder_delta() ); - button_add.set_encoder_delta( 0 ); - }; - - button_scanner_mode.on_select = [this,&nav](Button&) { - manual_mode = false ; - if( scanner_mode ) - { - scanner_mode = false ; - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - } - else - { - scanner_mode = true ; - button_scanner_mode.set_style( &style_red ); - button_scanner_mode.set_text( "SCANNER" ); - } - frequency_file_load( true ); - if( autostart ) - { - recon_resume(); - } - else - { - recon_pause(); - } - }; - - button_recon_setup.on_select = [this,&nav](Button&) { - - audio::output::stop(); - - frequency_list.clear(); - - auto open_view = nav.push(input_file,output_file,recon_lock_duration,recon_lock_nb_match,recon_match_mode); - open_view -> on_changed = [this](std::vector result) { - input_file = result[0]; - output_file = result[1]; - recon_lock_duration = strtol( result[2].c_str() , nullptr , 10 ); - recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 ); - recon_match_mode = strtol( result[4].c_str() , nullptr , 10 ); - - ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); - - autosave = persistent_memory::recon_autosave_freqs(); - autostart = persistent_memory::recon_autostart_recon(); - continuous = persistent_memory::recon_continuous(); - filedelete = persistent_memory::recon_clear_output(); - load_freqs = persistent_memory::recon_load_freqs(); - load_ranges = persistent_memory::recon_load_ranges(); - load_hamradios = persistent_memory::recon_load_hamradios(); - - update_ranges = persistent_memory::recon_update_ranges_when_recon(); - - field_lock_wait.set_value(recon_lock_duration); - - frequency_file_load( false ); - if( autostart ) - { - recon_resume(); - } - else - { - recon_pause(); - } - if( userpause != true ) - { - recon_resume(); - } - }; - }; - - field_wait.on_change = [this](int32_t v) - { - wait = v ; - if( wait == 0 ) - { - field_wait.set_style( &style_blue ); - } - else if( wait >= 500 ) - { - field_wait.set_style(&style_white); - } - else if( wait > -500 && wait < 500 ) - { - field_wait.set_style( &style_red ); - } - else if( wait <= -500 ) - { - field_wait.set_style( &style_green ); - } - }; - - field_lock_wait.on_change = [this](uint32_t v) - { - recon_lock_duration = v ; - if( recon_match_mode == RECON_MATCH_CONTINUOUS ) - { - if( (v / STATS_UPDATE_INTERVAL ) > recon_lock_nb_match ) - { - field_lock_wait.set_style( &style_white ); - } - else if( (v / STATS_UPDATE_INTERVAL ) == recon_lock_nb_match ) - { - field_lock_wait.set_style(&style_yellow); - } - } - else // RECON_MATCH_SPARSE - { - field_lock_wait.set_style( &style_white ); - } - }; - - field_squelch.on_change = [this](int32_t v) { - squelch = v ; - }; - - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - //PRE-CONFIGURATION: - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - file_name.set( "=>" ); - - //Loading input and output file from settings - ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , volume ); - - field_squelch.set_value( squelch ); - field_wait.set_value(wait); - field_lock_wait.set_value(recon_lock_duration); - field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - - // fill modulation and step options - freqman_set_modulation_option( field_mode ); - freqman_set_step_option( step_mode ); - - // set radio - change_mode(AM_MODULATION); // start on AM. - field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector - receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // first tune - - if( filedelete ) - { - delete_file( "FREQMAN/"+output_file+".TXT" ); - } - - frequency_file_load( false ); /* do not stop all at start */ - if( autostart ) - { - recon_resume(); - } - else - { - recon_pause(); - } - - recon_redraw(); - } - - void ReconView::frequency_file_load( bool stop_all_before) { - - (void)(stop_all_before); - audio::output::stop(); - - def_step = step_mode.selected_index(); // use def_step from manual selector - frequency_list.clear(); // clear the existing frequency list (expected behavior) - std::string file_input = input_file ; // default recon mode - if( scanner_mode ) - { - file_input = output_file ; - file_name.set_style( &style_red ); - button_scanner_mode.set_style( &style_red ); - button_scanner_mode.set_text( "SCANNER" ); - } - else - { - file_name.set_style( &style_blue ); - button_scanner_mode.set_style( &style_blue ); - button_scanner_mode.set_text( "RECON" ); - } - file_name.set_style( &style_white ); - desc_cycle.set_style( &style_white ); - if( !load_freqman_file_ex( file_input , frequency_list, load_freqs, load_ranges, load_hamradios ) ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set(" NO " + file_input + ".TXT FILE ..." ); - file_name.set( "=> NO DATA" ); - } - else - { - file_name.set( "=> "+file_input ); - if( frequency_list.size() == 0 ) - { - file_name.set_style( &style_red ); - desc_cycle.set_style( &style_red ); - desc_cycle.set("/0 no entries in list" ); - file_name.set( "BadOrEmpty "+file_input ); - } - else - { - if( frequency_list.size() > FREQMAN_MAX_PER_FILE ) - { - file_name.set_style( &style_yellow ); - desc_cycle.set_style( &style_yellow ); - } - } - } - - if( frequency_list[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); - else - step = freqman_entry_get_step_value( def_step ); - - switch( frequency_list[ 0 ] . type ){ - case SINGLE: - freq = frequency_list[ 0 ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list[ 0 ] . frequency_a ; - maxfreq = frequency_list[ 0 ] . frequency_b ; - if( fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - if( frequency_list[ 0 ] . step >= 0 ) - step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); - break; - case HAMRADIO: - minfreq = frequency_list[ 0 ] . frequency_a ; - maxfreq = frequency_list[ 0 ] . frequency_b ; - if( fwd ) - { - freq = minfreq ; - } - else - { - freq = maxfreq ; - } - break; - default: - break; - } - last_entry . modulation = -1 ; - last_entry . bandwidth = -1 ; - last_entry . step = -1 ; - - step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector - receiver_model.enable(); - receiver_model.set_squelch_level(0); - std::string description = "...no description..." ; - current_index = 0 ; - if( frequency_list.size() != 0 ) - { - switch( frequency_list[current_index].type ) - { - case RANGE: - description = "R: " + frequency_list[current_index].description ; - break ; - case HAMRADIO: - description = "H: " + frequency_list[current_index].description ; - break ; - default: - case SINGLE: - description = "S: " + frequency_list[current_index].description ; - break ; - } - text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); - if( update_ranges ) - { - button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.min = frequency_list[ current_index ] . frequency_a ; - if( frequency_list[ current_index ] . frequency_b != 0 ) - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_b ; - } - else - { - button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); - frequency_range.max = frequency_list[ current_index ] . frequency_a ; - } - } - } - else - { - text_cycle.set_text( " " ); - } - desc_cycle.set( description ); - handle_retune(); - } - - void ReconView::on_statistics_update(const ChannelStatistics& statistics) { - // 0 recon , 1 locking , 2 locked - static int32_t status = -1 ; - static int32_t last_timer = -1 ; - - db = statistics.max_db ; - - if( !userpause ) - { - if( !timer ) - { - status = 0 ; - continuous_lock = false ; - freq_lock = 0 ; - timer = recon_lock_duration ; - big_display.set_style(&style_white); - } - if( freq_lock < recon_lock_nb_match ) // LOCKING - { - if( status != 1 ) - { - status = 1 ; - if( wait != 0 ) - { - audio::output::stop(); - } - } - if( db > squelch ) //MATCHING LEVEL - { - continuous_lock = true ; - freq_lock ++ ; - } - else - { - // continuous, direct cut it if not consecutive match after 1 first match - if( recon_match_mode == RECON_MATCH_CONTINUOUS ) - { - if( freq_lock > 0 ) - { - timer = 0 ; - continuous_lock = false ; - } - } - } - } - if( freq_lock >= recon_lock_nb_match ) // LOCKED - { - if( status != 2 ) - { - continuous_lock = false ; - status = 2 ; - if( wait != 0 ) - { - audio_output_start(); - } - if( wait >= 0 ) - { - timer = wait ; - } - } - if( wait < 0 ) - { - if( db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY - { - timer = abs( wait ); - } - } - } - } - if( last_timer != timer ) - { - last_timer = timer ; - text_timer.set( "TIMER: " + to_string_dec_int( timer ) ); - } - if( timer ) - { - if( !continuous_lock ) - timer -= STATS_UPDATE_INTERVAL ; - if( timer < 0 ) - { - timer = 0 ; - } - } - if( recon || stepper != 0 || index_stepper != 0 ) - { - if( !timer || stepper != 0 || index_stepper != 0 ) - { - //IF THERE IS A FREQUENCY LIST ... - if (frequency_list.size() > 0 ) - { - has_looped = false ; - entry_has_changed = false ; - - if( recon || stepper != 0 || index_stepper != 0 ) - { - if( index_stepper == 0 ) - { - /* we are doing a range */ - if( frequency_list[ current_index ] . type == RANGE ) { - if ( ( fwd && stepper == 0 ) || stepper > 0 ) { - //forward - freq += step ; - // if bigger than range max - if (freq > maxfreq ) { - // when going forward we already know that we can skip a whole range => two values in the list - current_index ++ ; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - freq -= step ; - // if lower than range min - if (freq < minfreq ) { - // when back we have to check one step at a time - current_index -- ; - entry_has_changed = true ; - // looping - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - } - else if( frequency_list[ current_index ] . type == SINGLE ) { - if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward - current_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - current_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - else if( frequency_list[ current_index ] . type == HAMRADIO ) - { - if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward - if( ( minfreq != maxfreq ) && freq == minfreq ) - { - freq = maxfreq ; - } - else - { - current_index++; - entry_has_changed = true ; - // looping - if( (uint32_t)current_index >= frequency_list.size() ) - { - has_looped = true ; - current_index = 0 ; - } - } - } - else if( (!fwd && stepper == 0 ) || stepper < 0 ) { - //reverse - if( ( minfreq != maxfreq ) && freq == maxfreq ) - { - freq = minfreq ; - } - else - { - current_index--; - entry_has_changed = true ; - // if previous if under the list => go back from end - if( current_index < 0 ) - { - has_looped = true ; - current_index = frequency_list.size() - 1 ; - } - } - } - } - // set index to boundary if !continuous - if( has_looped && !continuous ) - { - entry_has_changed = true ; - /* prepare values for the next run, when user will resume */ - if( ( fwd && stepper == 0 ) || stepper > 0 ) - { - current_index = 0 ; - } - else if( ( !fwd && stepper == 0 ) || stepper < 0 ) - { - current_index = frequency_list.size() - 1 ; - } - } - } - else - { - current_index += index_stepper ; - if( current_index < 0 ) - current_index += frequency_list.size(); - if( (unsigned)current_index >= frequency_list.size() ) - current_index -= frequency_list.size() ; - - entry_has_changed = true ; - - if( !recon ) // for some motive, audio output gets stopped. - audio_output_start(); - } - // reload entry if changed - if( entry_has_changed ){ - timer = 0 ; - switch( frequency_list[ current_index ] . type ){ - case SINGLE: - freq = frequency_list[ current_index ] . frequency_a ; - break; - case RANGE: - minfreq = frequency_list[ current_index ] . frequency_a ; - maxfreq = frequency_list[ current_index ] . frequency_b ; - if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - case HAMRADIO: - minfreq = frequency_list[ current_index ] . frequency_a ; - maxfreq = frequency_list[ current_index ] . frequency_b ; - if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) - { - freq = minfreq ; - } - else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) - { - freq = maxfreq ; - } - break; - default: - break; - } - } - // send a pause message with the right freq - if( has_looped && !continuous ) - { - recon_pause(); - } - index_stepper = 0 ; - if( stepper < 0 ) stepper ++ ; - if( stepper > 0 ) stepper -- ; - } // if( recon || stepper != 0 || index_stepper != 0 ) - }//if (frequency_list.size() > 0 ) - } /* on_statistic_updates */ - } - handle_retune(); - recon_redraw(); - } - - void ReconView::recon_pause() { - timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq. - freq_lock = 0 ; - userpause = true; - continuous_lock=false; - recon = false ; - - audio_output_start(); - - big_display.set_style(&style_white); - button_pause.set_text(""); //PAUSED, show resume - } - - void ReconView::recon_resume() { - timer = 0 ; - freq_lock = 0 ; - userpause = false; - continuous_lock = false ; - recon = true ; - - audio::output::stop(); - - big_display.set_style(&style_white); - button_pause.set_text(""); - } - - void ReconView::on_index_delta(int32_t v) - { - if( v > 0 ) - { - fwd = true ; - button_dir.set_text( "FW>" ); - } - else - { - - fwd = false ; - button_dir.set_text( " 0 ) - index_stepper = v ; - - freq_lock = 0 ; - timer = 0 ; - } - - void ReconView::on_stepper_delta(int32_t v) - { - if( v > 0 ) - { - fwd = true ; - button_dir.set_text( "FW>" ); - } - else - { - - fwd = false ; - button_dir.set_text( " 0 ) - stepper = v ; - - freq_lock = 0 ; - timer = 0 ; - } - - void ReconView::on_headphone_volume_changed(int32_t v) { - const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; - receiver_model.set_headphone_volume(new_volume); - } - - size_t ReconView::change_mode( freqman_index_t new_mod ) { - - field_mode.on_change = [this](size_t , OptionsField::value_t v) { (void)v; }; - field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; }; - - receiver_model.disable(); - baseband::shutdown(); - - switch( new_mod ) { - case AM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw DSB (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_am_audio); - receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - receiver_model.set_am_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - case NFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 16k (2) default - field_bw.set_selected_index(2); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - break; - case WFM_MODULATION: - freqman_set_bandwidth_option( new_mod , field_bw ); - //bw 200k (0) default - field_bw.set_selected_index(0); - baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); - receiver_model.set_wfm_configuration(field_bw.selected_index()); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; - receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); - text_ctcss.set(" "); - break; - default: - break; - } - field_mode.set_selected_index( new_mod ); - field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if( v != -1 ) - { - change_mode(v); - } - }; - - if ( !recon ) //for some motive, audio output gets stopped. - audio::output::start(); //So if recon was stopped we resume audio - receiver_model.enable(); - - return freqman_entry_get_step_value( def_step ); - } - - void ReconView::handle_coded_squelch(const uint32_t value) { - static int32_t last_idx = -1 ; - - float diff, min_diff = value; - size_t min_idx { 0 }; - size_t c; - - if( field_mode.selected_index() != NFM_MODULATION ) - { - text_ctcss.set(" "); - return ; - } - - // Find nearest match - for (c = 0; c < tone_keys.size(); c++) { - diff = abs(((float)value / 100.0) - tone_keys[c].second); - if (diff < min_diff) { - min_idx = c; - min_diff = diff; - } - } - - // Arbitrary confidence threshold - if( last_idx < 0 || (unsigned)last_idx != min_idx ) - { - last_idx = min_idx ; - if (min_diff < 40) - text_ctcss.set("T: "+tone_keys[min_idx].first); - else - text_ctcss.set(" "); - } - } + bool ReconView::ReconSaveFreq( const std::string &freq_file_path , size_t freq_index , bool warn_if_exists ) + { + File recon_file; + + if( frequency_list.size() == 0 || ( frequency_list.size() && current_index > (int32_t)frequency_list.size() ) ) + return false ; + + freqman_entry entry = frequency_list[ freq_index ] ; + entry . frequency_a = freq ; + entry . frequency_b = 0 ; + entry . modulation = last_entry.modulation ; + entry . bandwidth = last_entry.bandwidth ; + entry . type = SINGLE ; + + std::string frequency_to_add ; + 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 ); + } + } + if(found && warn_if_exists) + { + nav_.display_modal("Error", "Frequency already exists"); + } + } + 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 ); + } + } + return true ; + } + + bool ReconView::ReconSetupLoadStrings( const std::string &source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ) + { + File settings_file; + size_t length, file_position = 0; + char * pos; + char * line_start; + char * line_end; + char file_data[257]; + + uint32_t it = 0 ; + uint32_t nb_params = RECON_SETTINGS_NB_PARAMS ; + std::string params[ RECON_SETTINGS_NB_PARAMS ]; + + bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ; + + 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); + } + } + } + + if( it < nb_params ) + { + /* bad number of params, signal defaults */ + input_file = "RECON" ; + output_file = "RECON_RESULTS" ; + recon_lock_duration = RECON_MIN_LOCK_DURATION ; + recon_lock_nb_match = RECON_DEF_NB_MATCH ; + recon_squelch_level = -14 ; + recon_match_mode = RECON_MATCH_CONTINUOUS ; + wait = RECON_DEF_WAIT_DURATION ; + volume = 40 ; + return false ; + } + + 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 > 2 ) + recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); + else + recon_lock_duration = RECON_MIN_LOCK_DURATION ; + + if( it > 3 ) + recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); + else + recon_lock_nb_match = RECON_DEF_NB_MATCH ; + + 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 = RECON_MATCH_CONTINUOUS ; + + if( it > 6 ) + wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); + else + wait = RECON_DEF_WAIT_DURATION ; + + if( it > 7 ) + volume = strtoll( params[ 7 ].c_str() , nullptr , 10 ); + else + volume = 40 ; + + return true ; + } + + bool ReconView::ReconSetupSaveStrings( const std::string &dest, const std::string &input_file , const std::string &output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ) + { + File settings_file; + + 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_int( volume ) ); + return true ; + } + + void ReconView::audio_output_start() + { + audio::output::start(); + this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 ); + } + + bool ReconView::check_sd_card() + { + return (sd_card::status() == sd_card::Status::Mounted) ? true : false; + } + + void ReconView::recon_redraw() { + if( last_rssi_min != rssi.get_min() || last_rssi_med != rssi.get_avg() || last_rssi_max != rssi.get_max() ) + { + last_rssi_min = rssi.get_min(); + last_rssi_med = rssi.get_avg(); + last_rssi_max = rssi.get_max(); + freq_stats.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+" db" ); + } + if( last_entry . frequency_a != freq ) + { + last_entry . frequency_a = freq ; + big_display.set( "FREQ: "+to_string_short_freq( freq )+" MHz" ); + } + if( last_db != db || last_list_size != frequency_list.size() || last_freq_lock != freq_lock || last_nb_match != recon_lock_nb_match ) + { + last_freq_lock = freq ; + last_list_size = frequency_list.size(); + last_db = db ; + last_nb_match = recon_lock_nb_match ; + text_max.set( "/" + to_string_dec_uint( frequency_list.size() ) + " " + to_string_dec_int( db ) + " db " + to_string_dec_uint( freq_lock ) + "/" + to_string_dec_uint( recon_lock_nb_match ) ); + if( freq_lock == 0 ) { + //NO FREQ LOCK, ONGOING STANDARD SCANNING + big_display.set_style(&style_white); + if( !userpause ) + button_pause.set_text(""); + else + button_pause.set_text(""); + } + else if( freq_lock == 1 && recon_lock_nb_match != 1 ) + { + //STARTING LOCK FREQ + big_display.set_style(&style_yellow); + button_pause.set_text(""); + } + else if( freq_lock >= recon_lock_nb_match ) + { + big_display.set_style( &style_green); + button_pause.set_text(""); + + //FREQ IS STRONG: GREEN and recon will pause when on_statistics_update() + if( (!scanner_mode) && autosave && frequency_list.size() > 0 ) { + ReconSaveFreq( freq_file_path , current_index , false ); + } + } + } + } + + void ReconView::handle_retune() { + if( last_freq != freq ) + { + last_freq = freq ; + receiver_model.set_tuning_frequency( freq ); // Retune + } + if( frequency_list.size() > 0 ) + { + if( last_entry . modulation != frequency_list[ current_index ] . modulation && frequency_list[ current_index ] . modulation >= 0 ) + { + last_entry . modulation = frequency_list[ current_index ]. modulation; + field_mode.set_selected_index( frequency_list[ current_index ]. modulation ); + } + // Set bandwidth if any + if( last_entry . bandwidth != frequency_list[ current_index ] . bandwidth && frequency_list[ current_index ] . bandwidth >= 0 ) + { + last_entry . bandwidth = frequency_list[ current_index ]. bandwidth; + switch( frequency_list[ current_index ].modulation ) + { + case AM_MODULATION: + receiver_model.set_am_configuration( freq ); + break ; + case NFM_MODULATION: + receiver_model.set_nbfm_configuration( freq ); + break ; + case WFM_MODULATION: + receiver_model.set_wfm_configuration( freq ); + default: + break ; + } + field_bw.set_selected_index( freq ); + } + if( last_entry . step != frequency_list[ current_index ] . step && frequency_list[ current_index ] . step >= 0 ) + { + last_entry . step = frequency_list[ current_index ]. step ; + step = freqman_entry_get_step_value( last_entry . step ); + step_mode.set_selected_index( step ); + } + if( last_index != current_index ) + { + last_index = current_index ; + if( (int32_t)frequency_list.size() && current_index < (int32_t)frequency_list.size() && frequency_list[ current_index ] . type == RANGE ) + { + if( update_ranges ) + { + button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.min = frequency_list[ current_index ] . frequency_a ; + if( frequency_list[ current_index ] . frequency_b != 0 ) + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_b ; + } + else + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_a ; + } + } + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + if(frequency_list[current_index].description.size() > 0) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + desc_cycle.set( "R: " + frequency_list[current_index].description ); //Show new description + break ; + case HAMRADIO: + desc_cycle.set( "H: " + frequency_list[current_index].description ); //Show new description + break ; + default: + case SINGLE: + desc_cycle.set( "S: " + frequency_list[current_index].description ); //Show new description + break ; + } + } + else + { + desc_cycle.set( "...no description..." ); //Show new description + } + } + } + } + + + void ReconView::focus() { + button_pause.focus(); + } + + ReconView::~ReconView() { + + ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); + + // save app settings + settings.save("recon", &app_settings); + + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); + } + + + ReconView::ReconView( NavigationView& nav) : nav_ { nav } { + + add_children( { + &labels, + &field_lna, + &field_vga, + &field_rf_amp, + &field_volume, + &field_bw, + &field_squelch, + &field_wait, + &field_lock_wait, + &button_recon_setup, + &button_scanner_mode, + &button_looking_glass, + &file_name, + &rssi, + &text_cycle, + &text_max, + &desc_cycle, + &big_display, + &freq_stats, + &text_timer, + &text_ctcss, + &button_manual_start, + &button_manual_end, + &button_manual_recon, + &field_mode, + &step_mode, + &button_pause, + &button_audio_app, + &button_add, + &button_dir, + &button_restart, + &button_mic_app, + &button_remove + } ); + + // Recon directory + if( check_sd_card() ) { // Check to see if SD Card is mounted + make_new_directory( u"/RECON" ); + sd_card_mounted = true ; + } + def_step = 0 ; + //HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + receiver_model.set_tuning_frequency( stored_freq ); + if( stored_freq - OneMHz > 0 ) + frequency_range.min = stored_freq - OneMHz ; + else + frequency_range.min = 0 ; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + if( stored_freq + OneMHz < MAX_UFREQ ) + frequency_range.max = stored_freq + OneMHz ; + else + frequency_range.max = MAX_UFREQ ; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); + // Loading settings + autostart = persistent_memory::recon_autostart_recon(); + autosave = persistent_memory::recon_autosave_freqs(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_volume.set_value( volume ); + if( sd_card_mounted ) + { + // load auto common app settings + auto rc = settings.load("recon", &app_settings); + if(rc == SETTINGS_OK) { + field_lna.set_value(app_settings.lna); + field_vga.set_value(app_settings.vga); + field_rf_amp.set_value(app_settings.rx_amp); + receiver_model.set_rf_amp(app_settings.rx_amp); + } + } + + button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + button_manual_end.on_select = [this, &nav](ButtonWithEncoder& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + text_cycle.on_select = [this, &nav](ButtonWithEncoder& button) { + if( frequency_list.size() > 0 ) + { + auto new_view = nav_.push(current_index); + new_view->on_changed = [this, &button](rf::Frequency f) { + f = f / OneMHz ; + if( f >= 1 && f <= frequency_list.size() ) + { + index_stepper = f - 1 - current_index ; + freq_lock = 0 ; + } + }; + } + }; + + button_manual_start.on_change = [this]() { + frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step ); + if( frequency_range.min < 0 ) + { + frequency_range.min = 0 ; + } + if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); + } + if( frequency_range.min > (frequency_range.max - freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.max = frequency_range.min + freqman_entry_get_step_value( def_step ); + if( frequency_range.max > MAX_UFREQ ) + { + frequency_range.min = MAX_UFREQ - freqman_entry_get_step_value( def_step ); + frequency_range.max = MAX_UFREQ ; + } + } + button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); + button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); + button_manual_start.set_encoder_delta( 0 ); + }; + + button_manual_end.on_change = [this]() { + frequency_range.max = frequency_range.max + button_manual_end.get_encoder_delta() * freqman_entry_get_step_value( def_step ); + if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) + { + frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ); + } + if( frequency_range.max > MAX_UFREQ ) + { + frequency_range.max = MAX_UFREQ ; + } + if( frequency_range.max < (frequency_range.min + freqman_entry_get_step_value( def_step ) ) ) + { + frequency_range.min = frequency_range.max - freqman_entry_get_step_value( def_step ); + if( frequency_range.max < ( freqman_entry_get_step_value( def_step ) + 1 ) ) + { + frequency_range.min = 1 ; + frequency_range.max = ( freqman_entry_get_step_value( def_step ) + 1 ) ; + } + } + button_manual_start.set_text( to_string_short_freq(frequency_range.min) ); + button_manual_end.set_text( to_string_short_freq(frequency_range.max) ); + button_manual_end.set_encoder_delta( 0 ); + }; + + text_cycle.on_change = [this]() { + on_index_delta( text_cycle.get_encoder_delta() ); + text_cycle.set_encoder_delta( 0 ); + }; + + button_pause.on_select = [this](ButtonWithEncoder&) { + if( frequency_list.size() > 0 ) + { + if( freq_lock > 0 ) + { + if( fwd ) + { + on_stepper_delta( 1 ); + } + else + { + on_stepper_delta( -1 ); + } + button_pause.set_text(""); //Show button for non continuous stop + } + else + { + if( userpause ) + { + recon_resume(); + } + else + { + recon_pause(); + } + } + } + }; + + button_pause.on_change = [this]() { + on_stepper_delta( button_pause.get_encoder_delta() ); + button_pause.set_encoder_delta( 0 ); + }; + + button_audio_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_looking_glass.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + + rssi.set_focusable(true); + rssi.set_peak( true , 500 ); + rssi.on_select = [this](RSSI&) { + nav_.pop(); + nav_.push(); + }; + + button_mic_app.on_select = [this](Button&) { + nav_.pop(); + nav_.push(); + }; + + button_remove.on_select = [this](ButtonWithEncoder&) { + if(frequency_list.size() > 0 ) + { + if( scanner_mode ) + { + if( current_index >= (int32_t)frequency_list.size() ) + { + current_index = frequency_list.size() - 1 ; + } + frequency_list.erase( frequency_list.begin()+ current_index ); + if( current_index >= (int32_t)frequency_list.size() ) + { + current_index = frequency_list.size() - 1 ; + } + if( frequency_list.size() > 0 ) + { + if(frequency_list[current_index].description.size() > 0) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + desc_cycle.set( "R: " + frequency_list[current_index].description ); + break ; + case HAMRADIO: + desc_cycle.set( "H: " + frequency_list[current_index].description ); + break ; + default: + case SINGLE: + desc_cycle.set( "S: " + frequency_list[current_index].description ); + break ; + } + } + else + { + desc_cycle.set( "...no description..." ); + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + + File freqman_file; + + 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 tmp_freq_file_path = freq_file_path+".TMP" ; + std::string frequency_to_add ; + + freqman_entry entry = frequency_list[ current_index ] ; + entry . frequency_a = freq ; + entry . frequency_b = 0 ; + entry . modulation = last_entry.modulation ; + entry . bandwidth = last_entry.bandwidth ; + entry . type = SINGLE ; + + get_freq_string( entry , frequency_to_add ); + + delete_file( tmp_freq_file_path ); + auto result = tmp_recon_file.create(tmp_freq_file_path); //First recon if freq is already in txt + if (!result.is_valid()) { + bool found=false; + result = recon_file.open(freq_file_path); //First recon if freq is already in txt + if (!result.is_valid()) { + char one_char[1]; //Read it char by char + std::string line; //and put read line in here + for (size_t pointer=0; pointer < recon_file.size();pointer++) { + recon_file.seek(pointer); + recon_file.read(one_char, 1); + if ((int)one_char[0] > 31) { //ascii space upwards + line += one_char[0]; //add it to the textline + } + else if (one_char[0] == '\n') { //new Line + if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { + found=true; + } + else + { + tmp_recon_file.write_line( frequency_to_add ); + } + line.clear(); // ready for next textline + } + } + if( found) + { + delete_file( freq_file_path ); + rename_file( tmp_freq_file_path , freq_file_path ); + } + else + { + delete_file( tmp_freq_file_path ); + } + } + } + } + receiver_model.set_tuning_frequency( frequency_list[ current_index ] . frequency_a ); // retune + } + if( frequency_list.size() == 0 ) + { + text_cycle.set_text( " " ); + desc_cycle.set( "no entries in list" ); //Show new description + delete_file( freq_file_path ); + } + timer = 0 ; + freq_lock = 0 ; + }; + + button_remove.on_change = [this]() { + on_stepper_delta( button_remove.get_encoder_delta() ); + button_remove.set_encoder_delta( 0 ); + }; + + button_manual_recon.on_select = [this](Button&) { + scanner_mode = false ; + manual_mode = true ; + recon_pause(); + + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + audio::output::stop(); + + frequency_list.clear(); + freqman_entry manual_freq_entry ; + + def_step = step_mode.selected_index(); // max range val + + manual_freq_entry . type = RANGE ; + manual_freq_entry . description = + "R " + to_string_short_freq(frequency_range.min) + ">" + + to_string_short_freq(frequency_range.max) + " S" // current Manual range + + to_string_short_freq(freqman_entry_get_step_value(def_step)).erase(0,1) ; //euquiq: lame kludge to reduce spacing in step freq + manual_freq_entry . frequency_a = frequency_range.min ; // min range val + manual_freq_entry . frequency_b = frequency_range.max ; // max range val + manual_freq_entry . modulation = -1 ; + manual_freq_entry . bandwidth = -1 ; + manual_freq_entry . step = def_step ; + + frequency_list . push_back( manual_freq_entry ); + + big_display.set_style(&style_white); //Back to white color + + freq_stats.set_style(&style_white); + freq_stats.set( "0/0/0" ); + + text_cycle.set_text( "1" ); + text_max.set( "/1" ); + button_scanner_mode.set_style( &style_white ); + button_scanner_mode.set_text( "MSEARCH" ); + file_name.set_style( &style_white ); + file_name.set( "MANUAL RANGE RECON" ); + desc_cycle.set_style( &style_white ); + desc_cycle.set( "MANUAL RANGE RECON" ); + + current_index = 0 ; + freq = manual_freq_entry . frequency_a ; + handle_retune(); + recon_redraw(); + recon_resume(); + } + }; + + button_dir.on_select = [this](Button&) { + if( fwd ) + { + fwd = false ; + button_dir.set_text( "" ); + } + timer = 0 ; + if ( userpause ) + recon_resume(); + }; + + button_restart.on_select = [this](Button&) { + if( frequency_list.size() > 0 ) + { + def_step = step_mode.selected_index(); //Use def_step from manual selector + frequency_file_load( true ); + if( fwd ) + { + button_dir.set_text( "FW>" ); + } + else + { + button_dir.set_text( "(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]; + freq_file_path = "/FREQMAN/"+output_file+".TXT" ; + recon_lock_duration = strtol( result[2].c_str() , nullptr , 10 ); + recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 ); + recon_match_mode = strtol( result[4].c_str() , nullptr , 10 ); + + ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , field_volume.value() ); + + autosave = persistent_memory::recon_autosave_freqs(); + autostart = persistent_memory::recon_autostart_recon(); + continuous = persistent_memory::recon_continuous(); + filedelete = persistent_memory::recon_clear_output(); + load_freqs = persistent_memory::recon_load_freqs(); + load_ranges = persistent_memory::recon_load_ranges(); + load_hamradios = persistent_memory::recon_load_hamradios(); + + update_ranges = persistent_memory::recon_update_ranges_when_recon(); + + field_lock_wait.set_value(recon_lock_duration); + + frequency_file_load( false ); + if( autostart ) + { + recon_resume(); + } + else + { + recon_pause(); + } + if( userpause != true ) + { + recon_resume(); + } + }; + }; + + field_wait.on_change = [this](int32_t v) + { + wait = v ; + if( wait == 0 ) + { + field_wait.set_style( &style_blue ); + } + else if( wait >= 500 ) + { + field_wait.set_style(&style_white); + } + else if( wait > -500 && wait < 500 ) + { + field_wait.set_style( &style_red ); + } + else if( wait <= -500 ) + { + field_wait.set_style( &style_green ); + } + }; + + field_lock_wait.on_change = [this](uint32_t v) + { + recon_lock_duration = v ; + if( recon_match_mode == RECON_MATCH_CONTINUOUS ) + { + if( (v / STATS_UPDATE_INTERVAL ) > recon_lock_nb_match ) + { + field_lock_wait.set_style( &style_white ); + } + else if( (v / STATS_UPDATE_INTERVAL ) == recon_lock_nb_match ) + { + field_lock_wait.set_style(&style_yellow); + } + } + else // RECON_MATCH_SPARSE + { + field_lock_wait.set_style( &style_white ); + } + }; + + field_squelch.on_change = [this](int32_t v) { + squelch = v ; + }; + + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + //PRE-CONFIGURATION: + button_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + file_name.set( "=>" ); + + //Loading input and output file from settings + ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , volume ); + freq_file_path = "/FREQMAN/"+output_file+".TXT" ; + + field_squelch.set_value( squelch ); + field_wait.set_value(wait); + field_lock_wait.set_value(recon_lock_duration); + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + // fill modulation and step options + freqman_set_modulation_option( field_mode ); + freqman_set_step_option( step_mode ); + + // set radio + change_mode(AM_MODULATION); // start on AM. + field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector + receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() ); // first tune + + if( filedelete ) + { + delete_file( freq_file_path ); + } + + frequency_file_load( false ); /* do not stop all at start */ + if( autostart ) + { + recon_resume(); + } + else + { + recon_pause(); + } + + recon_redraw(); + } + + void ReconView::frequency_file_load( bool stop_all_before) { + + (void)(stop_all_before); + audio::output::stop(); + + def_step = step_mode.selected_index(); // use def_step from manual selector + frequency_list.clear(); // clear the existing frequency list (expected behavior) + std::string file_input = input_file ; // default recon mode + if( scanner_mode ) + { + file_input = output_file ; + file_name.set_style( &style_red ); + button_scanner_mode.set_style( &style_red ); + button_scanner_mode.set_text( "SCANNER" ); + } + else + { + file_name.set_style( &style_blue ); + button_scanner_mode.set_style( &style_blue ); + button_scanner_mode.set_text( "RECON" ); + } + file_name.set_style( &style_white ); + desc_cycle.set_style( &style_white ); + if( !load_freqman_file_ex( file_input , frequency_list, load_freqs, load_ranges, load_hamradios ) ) + { + file_name.set_style( &style_red ); + desc_cycle.set_style( &style_red ); + desc_cycle.set(" NO " + file_input + ".TXT FILE ..." ); + file_name.set( "=> NO DATA" ); + } + else + { + file_name.set( "=> "+file_input ); + if( frequency_list.size() == 0 ) + { + file_name.set_style( &style_red ); + desc_cycle.set_style( &style_red ); + desc_cycle.set("/0 no entries in list" ); + file_name.set( "BadOrEmpty "+file_input ); + } + else + { + if( frequency_list.size() > FREQMAN_MAX_PER_FILE ) + { + file_name.set_style( &style_yellow ); + desc_cycle.set_style( &style_yellow ); + } + } + } + + if( frequency_list[ 0 ] . step >= 0 ) + step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); + else + step = freqman_entry_get_step_value( def_step ); + + switch( frequency_list[ 0 ] . type ){ + case SINGLE: + freq = frequency_list[ 0 ] . frequency_a ; + break; + case RANGE: + minfreq = frequency_list[ 0 ] . frequency_a ; + maxfreq = frequency_list[ 0 ] . frequency_b ; + if( fwd ) + { + freq = minfreq ; + } + else + { + freq = maxfreq ; + } + if( frequency_list[ 0 ] . step >= 0 ) + step = freqman_entry_get_step_value( frequency_list[ 0 ] . step ); + break; + case HAMRADIO: + minfreq = frequency_list[ 0 ] . frequency_a ; + maxfreq = frequency_list[ 0 ] . frequency_b ; + if( fwd ) + { + freq = minfreq ; + } + else + { + freq = maxfreq ; + } + break; + default: + break; + } + last_entry . modulation = -1 ; + last_entry . bandwidth = -1 ; + last_entry . step = -1 ; + + step_mode.set_selected_index(def_step); //Impose the default step into the manual step selector + receiver_model.enable(); + receiver_model.set_squelch_level(0); + std::string description = "...no description..." ; + current_index = 0 ; + if( frequency_list.size() != 0 ) + { + switch( frequency_list[current_index].type ) + { + case RANGE: + description = "R: " + frequency_list[current_index].description ; + break ; + case HAMRADIO: + description = "H: " + frequency_list[current_index].description ; + break ; + default: + case SINGLE: + description = "S: " + frequency_list[current_index].description ; + break ; + } + text_cycle.set_text( to_string_dec_uint( current_index + 1 , 3 ) ); + if( update_ranges ) + { + button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.min = frequency_list[ current_index ] . frequency_a ; + if( frequency_list[ current_index ] . frequency_b != 0 ) + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_b ; + } + else + { + button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) ); + frequency_range.max = frequency_list[ current_index ] . frequency_a ; + } + } + } + else + { + text_cycle.set_text( " " ); + } + desc_cycle.set( description ); + handle_retune(); + } + + void ReconView::on_statistics_update(const ChannelStatistics& statistics) { + db = statistics.max_db ; + if( !userpause ) + { + if( !timer ) + { + status = 0 ; + continuous_lock = false ; + freq_lock = 0 ; + timer = recon_lock_duration ; + big_display.set_style(&style_white); + } + if( freq_lock < recon_lock_nb_match ) // LOCKING + { + if( status != 1 ) + { + status = 1 ; + if( wait != 0 ) + { + audio::output::stop(); + } + } + if( db > squelch ) //MATCHING LEVEL + { + continuous_lock = true ; + freq_lock ++ ; + } + else + { + // continuous, direct cut it if not consecutive match after 1 first match + if( recon_match_mode == RECON_MATCH_CONTINUOUS ) + { + if( freq_lock > 0 ) + { + timer = 0 ; + continuous_lock = false ; + } + } + } + } + if( freq_lock >= recon_lock_nb_match ) // LOCKED + { + if( status != 2 ) + { + continuous_lock = false ; + status = 2 ; + if( wait != 0 ) + { + audio_output_start(); + } + if( wait >= 0 ) + { + timer = wait ; + } + } + if( wait < 0 ) + { + if( db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY + { + timer = abs( wait ); + } + } + } + } + if( last_timer != timer ) + { + last_timer = timer ; + text_timer.set( "TIMER: " + to_string_dec_int( timer ) ); + } + if( timer ) + { + if( !continuous_lock ) + timer -= STATS_UPDATE_INTERVAL ; + if( timer < 0 ) + { + timer = 0 ; + } + } + if( recon || stepper != 0 || index_stepper != 0 ) + { + if( !timer || stepper != 0 || index_stepper != 0 ) + { + //IF THERE IS A FREQUENCY LIST ... + if (frequency_list.size() > 0 ) + { + has_looped = false ; + entry_has_changed = false ; + + if( recon || stepper != 0 || index_stepper != 0 ) + { + if( index_stepper == 0 ) + { + /* we are doing a range */ + if( frequency_list[ current_index ] . type == RANGE ) { + if ( ( fwd && stepper == 0 ) || stepper > 0 ) { + //forward + freq += step ; + // if bigger than range max + if (freq > maxfreq ) { + // when going forward we already know that we can skip a whole range => two values in the list + current_index ++ ; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + freq -= step ; + // if lower than range min + if (freq < minfreq ) { + // when back we have to check one step at a time + current_index -- ; + entry_has_changed = true ; + // looping + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + } + else if( frequency_list[ current_index ] . type == SINGLE ) { + if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward + current_index++; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + current_index--; + entry_has_changed = true ; + // if previous if under the list => go back from end + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + else if( frequency_list[ current_index ] . type == HAMRADIO ) + { + if ( (fwd && stepper == 0 ) || stepper > 0 ) { //forward + if( ( minfreq != maxfreq ) && freq == minfreq ) + { + freq = maxfreq ; + } + else + { + current_index++; + entry_has_changed = true ; + // looping + if( (uint32_t)current_index >= frequency_list.size() ) + { + has_looped = true ; + current_index = 0 ; + } + } + } + else if( (!fwd && stepper == 0 ) || stepper < 0 ) { + //reverse + if( ( minfreq != maxfreq ) && freq == maxfreq ) + { + freq = minfreq ; + } + else + { + current_index--; + entry_has_changed = true ; + // if previous if under the list => go back from end + if( current_index < 0 ) + { + has_looped = true ; + current_index = frequency_list.size() - 1 ; + } + } + } + } + // set index to boundary if !continuous + if( has_looped && !continuous ) + { + entry_has_changed = true ; + /* prepare values for the next run, when user will resume */ + if( ( fwd && stepper == 0 ) || stepper > 0 ) + { + current_index = 0 ; + } + else if( ( !fwd && stepper == 0 ) || stepper < 0 ) + { + current_index = frequency_list.size() - 1 ; + } + } + } + else + { + current_index += index_stepper ; + if( current_index < 0 ) + current_index += frequency_list.size(); + if( (unsigned)current_index >= frequency_list.size() ) + current_index -= frequency_list.size() ; + + entry_has_changed = true ; + + if( !recon ) // for some motive, audio output gets stopped. + audio_output_start(); + } + // reload entry if changed + if( entry_has_changed ){ + timer = 0 ; + switch( frequency_list[ current_index ] . type ){ + case SINGLE: + freq = frequency_list[ current_index ] . frequency_a ; + break; + case RANGE: + minfreq = frequency_list[ current_index ] . frequency_a ; + maxfreq = frequency_list[ current_index ] . frequency_b ; + if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) + { + freq = minfreq ; + } + else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) + { + freq = maxfreq ; + } + break; + case HAMRADIO: + minfreq = frequency_list[ current_index ] . frequency_a ; + maxfreq = frequency_list[ current_index ] . frequency_b ; + if( ( fwd && !stepper && !index_stepper ) || stepper > 0 || index_stepper > 0 ) + { + freq = minfreq ; + } + else if( ( !fwd && !stepper && !index_stepper ) || stepper < 0 || index_stepper < 0 ) + { + freq = maxfreq ; + } + break; + default: + break; + } + } + // send a pause message with the right freq + if( has_looped && !continuous ) + { + recon_pause(); + } + index_stepper = 0 ; + if( stepper < 0 ) stepper ++ ; + if( stepper > 0 ) stepper -- ; + } // if( recon || stepper != 0 || index_stepper != 0 ) + }//if (frequency_list.size() > 0 ) + } /* on_statistic_updates */ + } + handle_retune(); + recon_redraw(); + } + + void ReconView::recon_pause() { + timer = 0 ; + freq_lock = 0 ; + userpause = true; + continuous_lock=false; + recon = false ; + + audio_output_start(); + + big_display.set_style(&style_white); + button_pause.set_text(""); //PAUSED, show resume + } + + void ReconView::recon_resume() { + timer = 0 ; + freq_lock = 0 ; + userpause = false; + continuous_lock = false ; + recon = true ; + + audio::output::stop(); + + big_display.set_style(&style_white); + button_pause.set_text(""); + } + + void ReconView::on_index_delta(int32_t v) + { + if( v > 0 ) + { + fwd = true ; + button_dir.set_text( "FW>" ); + } + else + { + + fwd = false ; + button_dir.set_text( " 0 ) + index_stepper = v ; + + freq_lock = 0 ; + timer = 0 ; + } + + void ReconView::on_stepper_delta(int32_t v) + { + if( v > 0 ) + { + fwd = true ; + button_dir.set_text( "FW>" ); + } + else + { + + fwd = false ; + button_dir.set_text( " 0 ) + stepper = v ; + + freq_lock = 0 ; + timer = 0 ; + } + + void ReconView::on_headphone_volume_changed(int32_t v) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); + } + + size_t ReconView::change_mode( freqman_index_t new_mod ) { + + field_mode.on_change = [this](size_t , OptionsField::value_t) { }; + field_bw.on_change = [this](size_t , OptionsField::value_t) { }; + + receiver_model.disable(); + baseband::shutdown(); + + switch( new_mod ) { + case AM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw DSB (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + case NFM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw 16k (2) default + field_bw.set_selected_index(2); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); + break; + case WFM_MODULATION: + freqman_set_bandwidth_option( new_mod , field_bw ); + //bw 200k (0) default + field_bw.set_selected_index(0); + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000);receiver_model.set_baseband_bandwidth(1750000); + text_ctcss.set(" "); + break; + default: + break; + } + field_mode.set_selected_index( new_mod ); + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if( v != -1 ) + { + change_mode(v); + } + }; + + if ( !recon ) //for some motive, audio output gets stopped. + audio::output::start(); //so if recon was stopped we resume audio + receiver_model.enable(); + + return freqman_entry_get_step_value( def_step ); + } + + void ReconView::handle_coded_squelch(const uint32_t value) { + 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_squelch_index < 0 || (unsigned)last_squelch_index != min_idx ) + { + last_squelch_index = 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 e8dd5ae7..af8c8da2 100644 --- a/firmware/application/apps/ui_recon.hpp +++ b/firmware/application/apps/ui_recon.hpp @@ -49,37 +49,37 @@ namespace ui { void focus() override; - const Style style_grey { // recon + const Style style_grey { // recon .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::grey(), }; - const Style style_white { // recon + const Style style_white { // recon .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::white(), }; - const Style style_yellow { //Found signal + const Style style_yellow { //found signal .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::yellow(), }; - const Style style_green { //Found signal + const Style style_green { //Found signal .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::green(), }; - const Style style_red { //erasing freq + const Style style_red { //erasing freq .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::red(), }; - const Style style_blue { // quick recon, wait == 0 + const Style style_blue { // quick recon, wait == 0 .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::blue(), @@ -92,7 +92,7 @@ namespace ui { private: NavigationView& nav_; - void audio_output_start(); + void audio_output_start(); bool check_sd_card(); size_t change_mode( freqman_index_t mod_type); void show_max( bool refresh_display = false ); @@ -106,6 +106,9 @@ namespace ui { void recon_redraw(); void handle_retune(); void handle_coded_squelch(const uint32_t value); + bool ReconSetupLoadStrings( const std::string &source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume ); + bool ReconSetupSaveStrings( const std::string &dest, const std::string &input_file , const std::string &output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ); + bool ReconSaveFreq( const std::string &freq_file_path , size_t index , bool warn_if_exists ); jammer::jammer_range_t frequency_range { false, 0, MAX_UFREQ }; //perfect for manual recon task too... int32_t squelch { 0 }; @@ -127,16 +130,16 @@ namespace ui { bool load_hamradios = { true }; bool update_ranges = { true }; bool fwd = { true }; - bool recon = true ; + bool recon = true ; uint32_t recon_lock_nb_match = { 3 }; - uint32_t recon_lock_duration = { RECON_DEF_LOCK_DURATION }; + uint32_t recon_lock_duration = { RECON_MIN_LOCK_DURATION }; uint32_t recon_match_mode = { RECON_MATCH_CONTINUOUS }; bool scanner_mode { false }; bool manual_mode { false }; bool sd_card_mounted = false ; int32_t volume = 40 ; - int32_t stepper = 0 ; - int32_t index_stepper = 0 ; + int32_t stepper = 0 ; + int32_t index_stepper = 0 ; int64_t freq = 0 ; uint32_t step = 0 ; freqman_index_t def_modulation = 0 ; @@ -146,9 +149,22 @@ namespace ui { freqman_entry last_entry = { } ; bool entry_has_changed = false ; uint32_t freq_lock { 0 }; - int64_t minfreq = 0 ; + int64_t minfreq = 0 ; int64_t maxfreq = 0 ; bool has_looped = false ; + int8_t status = -1 ; // 0 recon , 1 locking , 2 locked + int32_t last_timer = -1 ; + int8_t last_db = -127 ; + uint16_t last_nb_match = 999 ; + uint16_t last_freq_lock = 999 ; + size_t last_list_size = 0 ; + int8_t last_rssi_min = -127 ; + int8_t last_rssi_med = -127 ; + int8_t last_rssi_max = -127 ; + int32_t last_index = -1 ; + int32_t last_squelch_index = -1 ; + int64_t last_freq = 0 ; + std::string freq_file_path = { } ; Labels labels { @@ -183,7 +199,7 @@ namespace ui { { 3 * 8, 1 * 16 }, 6, { } - }; + }; NumberField field_squelch { { 12 * 8, 1 * 16 }, @@ -196,7 +212,7 @@ namespace ui { NumberField field_wait { { 20 * 8, 1 * 16 }, 5, - { -(10000-STATS_UPDATE_INTERVAL) , (10000-STATS_UPDATE_INTERVAL) }, + { -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, STATS_UPDATE_INTERVAL, ' ', }; @@ -204,8 +220,8 @@ namespace ui { NumberField field_lock_wait { { 26 * 8, 1 * 16 }, 4, - { RECON_DEF_LOCK_DURATION , (10000-RECON_DEF_LOCK_DURATION) }, - RECON_DEF_LOCK_DURATION, + { RECON_MIN_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, + STATS_UPDATE_INTERVAL, ' ', }; @@ -223,24 +239,19 @@ namespace ui { }; Text desc_cycle { - {0, 4 * 16, SCREEN_W , 16 }, + {0, 4 * 16, SCREEN_W , 16 }, }; - /* BigFrequency big_display { //Show frequency in glamour - { 4, 7 * 16 - 8 , 28 * 8, 52 }, - 0 - }; */ - - Text big_display { //Show frequency in text mode + Text big_display { //Show frequency in text mode { 0, 5 * 16 , 23 * 8, 16 }, }; - Text freq_stats { //Show frequency stats in text mode + Text freq_stats { //Show frequency stats in text mode { 0, 6 * 16 , 23 * 8, 16 }, }; // TIMER: 9999 - Text text_timer { //Show frequency stats in text mode + Text text_timer { //Show frequency stats in text mode { 0, 7 * 16 , 11 * 8, 16 }, }; @@ -266,7 +277,7 @@ namespace ui { "RECON" }; - Text file_name { //Show file used + Text file_name { //show file used { 0 , 8 * 16 + 6 , SCREEN_W - 7 * 8, 16 }, }; @@ -351,7 +362,7 @@ namespace ui { } }; // app save settings - std::app_settings settings { }; + std::app_settings settings { }; std::app_settings::AppSettings app_settings { }; }; diff --git a/firmware/application/apps/ui_recon_settings.cpp b/firmware/application/apps/ui_recon_settings.cpp index 5bd30b36..81a9d832 100644 --- a/firmware/application/apps/ui_recon_settings.cpp +++ b/firmware/application/apps/ui_recon_settings.cpp @@ -34,133 +34,6 @@ 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 , 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 = RECON_SETTINGS_NB_PARAMS ; - std::string params[ RECON_SETTINGS_NB_PARAMS ]; - - 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 - - // Restart at beginning of last incomplete line - file_position -= (file_data + 256 - line_start); - } - } - } - - if( it < nb_params ) - { - /* bad number of params, signal defaults */ - input_file = "RECON" ; - output_file = "RECON_RESULTS" ; - recon_lock_duration = RECON_DEF_LOCK_DURATION ; - recon_lock_nb_match = RECON_DEF_NB_MATCH ; - recon_squelch_level = -14 ; - recon_match_mode = RECON_MATCH_CONTINUOUS ; - wait = RECON_DEF_WAIT_DURATION ; - volume = 40 ; - return false ; - } - - 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 > 2 ) - recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 ); - else - recon_lock_duration = RECON_DEF_LOCK_DURATION ; - - if( it > 3 ) - recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 ); - else - recon_lock_nb_match = RECON_DEF_NB_MATCH ; - - 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 = RECON_MATCH_CONTINUOUS ; - - if( it > 6 ) - wait = strtoll( params[ 6 ].c_str() , nullptr , 10 ); - else - wait = RECON_DEF_WAIT_DURATION ; - - if( it > 8 ) - volume = strtoll( params[ 8 ].c_str() , nullptr , 10 ); - else - volume = 40 ; - - 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 , 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_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); @@ -180,8 +53,8 @@ namespace ui { 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"); @@ -205,7 +78,7 @@ namespace ui { 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 ); + button_output_file.set_text( _output_file ); } else { nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); } diff --git a/firmware/application/apps/ui_recon_settings.hpp b/firmware/application/apps/ui_recon_settings.hpp index ab02f426..cd00be52 100644 --- a/firmware/application/apps/ui_recon_settings.hpp +++ b/firmware/application/apps/ui_recon_settings.hpp @@ -35,7 +35,7 @@ // 1Mhz helper #ifdef OneMHz - #undef OneMHz +#undef OneMHz #endif #define OneMHz 1000000 @@ -43,12 +43,15 @@ #define RECON_MATCH_CONTINUOUS 0 #define RECON_MATCH_SPARSE 1 -// statistics update interval (change here if it's evolving) in ms +// statistics update interval in ms (change here if the statistics API is changing it's pace) #define STATS_UPDATE_INTERVAL 100 +// maximum lock duration +#define RECON_MAX_LOCK_DURATION 9900 + // default number of match to have a lock #define RECON_DEF_NB_MATCH 3 -#define RECON_DEF_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL +#define RECON_MIN_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL #define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps // screen size helper @@ -60,9 +63,6 @@ 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 , int32_t &volume ); - bool ReconSetupSaveStrings( std::string dest, const std::string input_file , const std::string output_file , const uint32_t recon_lock_duration , const uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume ); - class ReconSetupViewMain : public View { public: ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file ); @@ -88,7 +88,7 @@ namespace ui { }; Button button_output_file { { 1 * 8 , 5 * 16 - 2, 18 * 8, 22 }, - "RECON_RESULTS" + "RECON_RESULTS" }; Checkbox checkbox_autosave_freqs { @@ -117,7 +117,7 @@ namespace ui { 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 ); + ReconSetupViewMore( NavigationView& nav, Rect parent_rect , 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 ); @@ -125,9 +125,9 @@ namespace ui { private: - const uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL ; - const uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH ; - const uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS ; + uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL ; + uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH ; + uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS ; Checkbox checkbox_load_freqs { { 1 * 8, 12 }, @@ -159,10 +159,10 @@ namespace ui { NumberField field_recon_lock_duration { { 1 * 8, 132 }, // position X , Y 4, // number of displayed digits (even empty) - { STATS_UPDATE_INTERVAL , 10000-STATS_UPDATE_INTERVAL }, // range of number - STATS_UPDATE_INTERVAL, // rotary encoder increment - ' ', // filling character - false // can loop + { -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, // range of number + STATS_UPDATE_INTERVAL, // rotary encoder increment + ' ', // filling character + false // can loop }; Text text_recon_lock_nb { { 1 * 8 , 162 , 25 * 8 , 22 }, @@ -189,7 +189,7 @@ namespace ui { 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 ); + 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 ); std::function messages )> on_changed { };