2022-09-11 14:07:08 +00:00
/*
* Copyright ( C ) 2015 Jared Boone , ShareBrained Technology , Inc .
* Copyright ( C ) 2018 Furrtek
*
* This file is part of PortaPack .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; see the file COPYING . If not , write to
* the Free Software Foundation , Inc . , 51 Franklin Street ,
* Boston , MA 02110 - 1301 , USA .
*/
# include "ui_recon.hpp"
# include "ui_fileman.hpp"
# include "ui_recon_settings.hpp"
# include "file.hpp"
// Id's for messages between ReconThread and ReconView
2022-10-11 10:48:42 +00:00
# define MSG_RECON_PAUSE 9999 // for handle_retune to know that recon thread triggered a pause. f is not important with that message
# define MSG_RECON_SET_MODULATION 10000 // for handle_retune to know that recon thread triggered a modulation change. f is the index of the modulation
# define MSG_RECON_SET_BANDWIDTH 20000 // for handle_retune to know that recon thread triggered a bandwidth change. f is the new bandwidth value index for current modulation
# define MSG_RECON_SET_STEP 30000 // for handle_retune to know that recon thread triggered a bandwidth change. f is the new bandwidth value index for current modulation
2023-05-04 13:20:12 +00:00
# define MSG_RECON_SET_RECEIVER_BANDWIDTH 40000 // for handle_retune to know that recon thread triggered a receiver bandwidth change. f is the new bandwidth in hz
2022-10-11 10:48:42 +00:00
# define MSG_RECON_SET_RECEIVER_SAMPLERATE 50000 // for handle_retune to know that recon thread triggered a receiver samplerate change. f is the new samplerate in hz/s
2022-09-11 14:07:08 +00:00
using namespace portapack ;
using portapack : : memory : : map : : backup_ram ;
namespace ui {
2023-03-24 22:44:16 +00:00
ReconThread : : ReconThread ( freqman_db * database ) : frequency_list_ { * database } {
thread = chThdCreateFromHeap ( NULL , 1024 , NORMALPRIO + 10 , ReconThread : : static_fn , this ) ;
}
ReconThread : : ~ ReconThread ( ) {
stop ( ) ;
}
void ReconThread : : stop ( ) {
if ( thread ) {
chThdTerminate ( thread ) ;
chThdWait ( thread ) ;
thread = nullptr ;
}
}
void ReconThread : : set_recon ( const bool v ) {
_recon = v ;
}
void ReconThread : : set_freq_delete ( const bool v ) {
_freq_delete = v ;
}
void ReconThread : : set_stepper ( const int64_t v ) {
_stepper = v ;
}
2023-03-26 13:11:27 +00:00
void ReconThread : : set_index_stepper ( const int64_t v ) {
_index_stepper = v ;
}
2023-03-24 22:44:16 +00:00
void ReconThread : : set_lock_duration ( const uint32_t v ) {
_lock_duration = v ;
}
uint32_t ReconThread : : get_lock_duration ( ) {
return _lock_duration ;
}
void ReconThread : : set_lock_nb_match ( const uint32_t v ) {
_lock_nb_match = v ;
}
uint32_t ReconThread : : get_lock_nb_match ( ) {
return _lock_nb_match ;
}
bool ReconThread : : is_recon ( ) {
return _recon ;
}
void ReconThread : : set_freq_lock ( const uint32_t v ) {
_freq_lock = v ;
}
uint32_t ReconThread : : is_freq_lock ( ) {
return _freq_lock ;
}
int64_t ReconThread : : get_current_freq ( ) {
return freq ;
}
void ReconThread : : change_recon_direction ( ) {
_fwd = ! _fwd ;
2023-05-04 13:20:12 +00:00
//chThdSleepMilliseconds(300); //Give some pause after reversing recon direction
2023-03-24 22:44:16 +00:00
}
bool ReconThread : : get_recon_direction ( ) {
return _fwd ;
}
void ReconThread : : set_recon_direction ( const bool v ) {
_fwd = v ;
}
void ReconThread : : set_continuous ( const bool v ) {
_continuous = v ;
}
freqman_index_t ReconThread : : get_current_modulation ( ) {
return last_entry . modulation ;
}
freqman_index_t ReconThread : : get_current_bandwidth ( ) {
return last_entry . bandwidth ;
}
void ReconThread : : set_default_modulation ( freqman_index_t index ) {
def_modulation = index ;
}
void ReconThread : : set_default_bandwidth ( freqman_index_t index ) {
def_bandwidth = index ;
}
void ReconThread : : set_default_step ( freqman_index_t index ) {
def_step = index ;
}
void ReconThread : : set_freq_index ( int16_t index ) {
frequency_index = index ;
}
int16_t ReconThread : : get_freq_index ( ) {
return frequency_index ;
}
msg_t ReconThread : : static_fn ( void * arg ) {
auto obj = static_cast < ReconThread * > ( arg ) ;
obj - > run ( ) ;
return 0 ;
}
2023-04-01 17:56:44 +00:00
void ReconThread : : run ( )
{
//IF THERE IS A FREQUENCY LIST ...
if ( frequency_list_ . size ( ) > 0 )
{
2023-03-24 22:44:16 +00:00
int64_t minfreq = 0 ;
int64_t maxfreq = 0 ;
bool has_looped = false ;
RetuneMessage message { } ;
if ( frequency_list_ [ 0 ] . step > = 0 )
step = freqman_entry_get_step_value ( frequency_list_ [ 0 ] . step ) ;
else
step = freqman_entry_get_step_value ( def_step ) ;
switch ( frequency_list_ [ 0 ] . type ) {
case SINGLE :
freq = frequency_list_ [ 0 ] . frequency_a ;
break ;
case RANGE :
minfreq = frequency_list_ [ 0 ] . frequency_a ;
maxfreq = frequency_list_ [ 0 ] . frequency_b ;
if ( _fwd )
{
freq = minfreq ;
}
else
{
freq = maxfreq ;
}
if ( frequency_list_ [ 0 ] . step > = 0 )
step = freqman_entry_get_step_value ( frequency_list_ [ 0 ] . step ) ;
break ;
case HAMRADIO :
minfreq = frequency_list_ [ 0 ] . frequency_a ;
maxfreq = frequency_list_ [ 0 ] . frequency_b ;
if ( _fwd )
{
freq = minfreq ;
}
else
{
freq = maxfreq ;
}
break ;
default :
break ;
}
last_entry . modulation = - 1 ;
last_entry . bandwidth = - 1 ;
last_entry . step = - 1 ;
bool restart_recon = false ; //Flag whenever scanning is restarting after a pause
2023-04-01 17:56:44 +00:00
while ( ! chThdShouldTerminate ( ) & & frequency_list_ . size ( ) > 0 )
{
2023-03-24 22:44:16 +00:00
if ( ! _freq_delete )
{
2023-04-01 17:56:44 +00:00
has_looped = false ;
if ( last_entry . frequency_a ! = freq | | entry_has_changed )
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
last_entry . frequency_a = freq ;
receiver_model . set_tuning_frequency ( freq ) ; // Retune
message . freq = freq ;
message . range = frequency_index ;
EventDispatcher : : send_message ( message ) ;
}
entry_has_changed = false ;
// Set modulation if any
if ( last_entry . modulation ! = frequency_list_ [ frequency_index ] . modulation & & frequency_list_ [ frequency_index ] . modulation > = 0 )
{
last_entry . modulation = frequency_list_ [ frequency_index ] . modulation ;
message . freq = last_entry . modulation ;
message . range = MSG_RECON_SET_MODULATION ;
EventDispatcher : : send_message ( message ) ;
}
// Set bandwidth if any
if ( last_entry . bandwidth ! = frequency_list_ [ frequency_index ] . bandwidth & & frequency_list_ [ frequency_index ] . bandwidth > = 0 )
{
last_entry . bandwidth = frequency_list_ [ frequency_index ] . bandwidth ;
message . freq = last_entry . bandwidth ;
message . range = MSG_RECON_SET_BANDWIDTH ;
EventDispatcher : : send_message ( message ) ;
}
if ( last_entry . step ! = frequency_list_ [ frequency_index ] . step & & frequency_list_ [ frequency_index ] . step > = 0 )
{
last_entry . step = frequency_list_ [ frequency_index ] . step ;
message . freq = last_entry . step ;
message . range = MSG_RECON_SET_STEP ;
EventDispatcher : : send_message ( message ) ;
step = freqman_entry_get_step_value ( last_entry . step ) ;
}
if ( _recon | | _stepper ! = 0 | | _index_stepper ! = 0 )
{
if ( _freq_lock = = 0 | | _stepper ! = 0 | | _index_stepper ! = 0 ) //normal recon (not performing freq_lock)
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
if ( ! restart_recon | | _stepper ! = 0 | | _index_stepper ! = 0 )
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
if ( _index_stepper = = 0 )
{
/* we are doing a range */
if ( frequency_list_ [ frequency_index ] . type = = RANGE ) {
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 ) {
//forward
freq + = step ;
// if bigger than range max
if ( freq > maxfreq ) {
// when going forward we already know that we can skip a whole range => two values in the list
frequency_index + + ;
entry_has_changed = true ;
// looping
if ( ( uint32_t ) frequency_index > = frequency_list_ . size ( ) )
{
has_looped = true ;
frequency_index = 0 ;
}
}
}
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 ) {
//reverse
freq - = step ;
// if lower than range min
if ( freq < minfreq ) {
// when back we have to check one step at a time
frequency_index - - ;
entry_has_changed = true ;
// looping
if ( frequency_index < 0 )
{
has_looped = true ;
frequency_index = frequency_list_ . size ( ) - 1 ;
}
}
}
}
else if ( frequency_list_ [ frequency_index ] . type = = SINGLE ) {
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 ) { //forward
frequency_index + + ;
2023-03-24 22:44:16 +00:00
entry_has_changed = true ;
// looping
if ( ( uint32_t ) frequency_index > = frequency_list_ . size ( ) )
{
has_looped = true ;
2023-04-01 17:56:44 +00:00
frequency_index = 0 ;
2023-03-24 22:44:16 +00:00
}
}
2023-04-01 17:56:44 +00:00
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 ) {
//reverse
frequency_index - - ;
2023-03-24 22:44:16 +00:00
entry_has_changed = true ;
2023-04-01 17:56:44 +00:00
// if previous if under the list => go back from end
2023-03-24 22:44:16 +00:00
if ( frequency_index < 0 )
{
has_looped = true ;
2023-04-01 17:56:44 +00:00
frequency_index = frequency_list_ . size ( ) - 1 ;
2023-03-24 22:44:16 +00:00
}
}
}
2023-04-01 17:56:44 +00:00
else if ( frequency_list_ [ frequency_index ] . type = = HAMRADIO )
{
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 ) { //forward
if ( ( minfreq ! = maxfreq ) & & freq = = minfreq )
{
freq = maxfreq ;
}
else
{
frequency_index + + ;
entry_has_changed = true ;
// looping
if ( ( uint32_t ) frequency_index > = frequency_list_ . size ( ) )
{
has_looped = true ;
frequency_index = 0 ;
}
}
}
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 ) {
//reverse
if ( ( minfreq ! = maxfreq ) & & freq = = maxfreq )
{
freq = minfreq ;
}
else
{
frequency_index - - ;
entry_has_changed = true ;
// if previous if under the list => go back from end
if ( frequency_index < 0 )
{
has_looped = true ;
frequency_index = frequency_list_ . size ( ) - 1 ;
}
}
}
}
// set index to boundary if !continuous
if ( has_looped & & ! _continuous )
{
2023-03-24 22:44:16 +00:00
entry_has_changed = true ;
2023-04-01 17:56:44 +00:00
/* prepare values for the next run, when user will resume */
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 )
2023-03-24 22:44:16 +00:00
{
frequency_index = 0 ;
}
2023-04-01 17:56:44 +00:00
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 )
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
frequency_index = frequency_list_ . size ( ) - 1 ;
2023-03-24 22:44:16 +00:00
}
}
}
2023-04-01 17:56:44 +00:00
else
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
if ( _index_stepper > 0 )
frequency_index + + ;
if ( _index_stepper < 0 )
frequency_index - - ;
if ( frequency_index < 0 )
frequency_index = frequency_list_ . size ( ) - 1 ;
if ( ( unsigned ) frequency_index > = frequency_list_ . size ( ) )
frequency_index = 0 ;
entry_has_changed = true ;
}
// reload entry if changed
if ( entry_has_changed ) {
switch ( frequency_list_ [ frequency_index ] . type ) {
case SINGLE :
freq = frequency_list_ [ frequency_index ] . frequency_a ;
break ;
case RANGE :
minfreq = frequency_list_ [ frequency_index ] . frequency_a ;
maxfreq = frequency_list_ [ frequency_index ] . frequency_b ;
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 | | _index_stepper > 0 )
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
freq = minfreq ;
2023-03-24 22:44:16 +00:00
}
2023-04-01 17:56:44 +00:00
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 | | _index_stepper < 0 )
2023-03-24 22:44:16 +00:00
{
2023-04-01 17:56:44 +00:00
freq = maxfreq ;
2023-03-24 22:44:16 +00:00
}
2023-04-01 17:56:44 +00:00
break ;
case HAMRADIO :
minfreq = frequency_list_ [ frequency_index ] . frequency_a ;
maxfreq = frequency_list_ [ frequency_index ] . frequency_b ;
if ( ( _fwd & & _stepper = = 0 ) | | _stepper > 0 | | _index_stepper > 0 )
{
freq = minfreq ;
}
else if ( ( ! _fwd & & _stepper = = 0 ) | | _stepper < 0 | | _index_stepper < 0 )
{
freq = maxfreq ;
}
break ;
default :
break ;
2023-03-24 22:44:16 +00:00
}
2023-04-01 17:56:44 +00:00
}
// send a pause message with the right freq
2023-03-24 22:44:16 +00:00
if ( has_looped & & ! _continuous )
{
2023-04-01 17:56:44 +00:00
// signal pause to handle_retune
receiver_model . set_tuning_frequency ( freq ) ; // Retune to actual freq
message . freq = freq ;
message . range = MSG_RECON_PAUSE ;
EventDispatcher : : send_message ( message ) ;
2023-03-24 22:44:16 +00:00
}
2023-04-01 17:56:44 +00:00
if ( _stepper < 0 ) _stepper + + ;
if ( _stepper > 0 ) _stepper - - ;
if ( _index_stepper < 0 ) _index_stepper + + ;
if ( _index_stepper > 0 ) _index_stepper - - ;
} // if( !restart_recon || _stepper != 0 || _index_stepper != 0 )
2023-03-24 22:44:16 +00:00
else
{
restart_recon = false ;
}
2023-04-01 17:56:44 +00:00
} // if( _freq_lock == 0 || _stepper != 0 || _index_stepper != 0 )
} // if( _recon || _stepper != 0 || _index_stepper != 0 )
} // if( !freq_delete )
2023-03-24 22:44:16 +00:00
chThdSleepMilliseconds ( _lock_duration ) ; //Needed to (eventually) stabilize the receiver into new freq
2023-04-01 17:56:44 +00:00
} //while( !chThdShouldTerminate() && frequency_list_.size() > 0 )
} //if (frequency_list_.size() > 0 )
} //ReconThread::run
2023-03-24 22:44:16 +00:00
bool ReconView : : check_sd_card ( )
{
return ( sd_card : : status ( ) = = sd_card : : Status : : Mounted ) ? true : false ;
}
void ReconView : : set_display_freq ( int64_t freq )
{
2023-04-28 10:57:00 +00:00
big_display . set ( " FREQ: " + to_string_short_freq ( freq ) + " MHz " ) ;
2023-03-24 22:44:16 +00:00
}
void ReconView : : handle_retune ( int64_t freq , uint32_t index ) {
static int64_t last_freq = 0 ;
static uint32_t last_index = 999999 ;
switch ( index ) {
case MSG_RECON_PAUSE :
user_pause ( ) ;
if ( update_ranges & & current_index < frequency_list . size ( ) )
{
button_manual_start . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . min = frequency_list [ current_index ] . frequency_a ;
if ( frequency_list [ current_index ] . frequency_b ! = 0 )
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_b ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_b ;
}
else
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_a ;
}
}
return ;
break ;
case MSG_RECON_SET_MODULATION :
receiver_model . disable ( ) ;
baseband : : shutdown ( ) ;
change_mode ( freq ) ;
2023-05-04 13:20:12 +00:00
if ( ! recon_thread - > is_recon ( ) ) // for some motive, audio output gets stopped.
audio : : output : : start ( ) ; // so if recon was stopped we resume audio
2023-03-24 22:44:16 +00:00
receiver_model . enable ( ) ;
field_mode . set_selected_index ( freq ) ;
return ;
break ;
case MSG_RECON_SET_BANDWIDTH :
switch ( recon_thread - > get_current_modulation ( ) )
{
case AM_MODULATION :
receiver_model . set_am_configuration ( freq ) ;
break ;
case NFM_MODULATION :
receiver_model . set_nbfm_configuration ( freq ) ;
break ;
case WFM_MODULATION :
receiver_model . set_wfm_configuration ( freq ) ;
default :
break ;
}
field_bw . set_selected_index ( freq ) ;
return ;
break ;
case MSG_RECON_SET_STEP :
step_mode . set_selected_index ( freq ) ;
return ;
break ;
}
if ( last_index ! = index )
{
last_index = index ;
current_index = index ;
if ( frequency_list . size ( ) & & index < frequency_list . size ( ) & & frequency_list [ index ] . type = = RANGE )
{
if ( update_ranges )
{
button_manual_start . set_text ( to_string_short_freq ( frequency_list [ index ] . frequency_a ) ) ;
frequency_range . min = frequency_list [ index ] . frequency_a ;
if ( frequency_list [ index ] . frequency_b ! = 0 )
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ index ] . frequency_b ) ) ;
frequency_range . max = frequency_list [ index ] . frequency_b ;
}
else
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ index ] . frequency_a ) ) ;
frequency_range . max = frequency_list [ index ] . frequency_a ;
}
}
}
2023-05-04 13:20:12 +00:00
text_cycle . set_text ( to_string_dec_uint ( index + 1 , 3 ) ) ;
2023-04-24 19:14:34 +00:00
if ( frequency_list [ index ] . description . size ( ) > 0 )
2023-03-24 22:44:16 +00:00
{
2023-04-24 19:14:34 +00:00
switch ( frequency_list [ current_index ] . type )
2023-03-26 13:30:54 +00:00
{
2023-04-24 19:14:34 +00:00
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 ;
2023-03-26 13:30:54 +00:00
}
2023-03-24 22:44:16 +00:00
}
2023-05-04 13:20:12 +00:00
else
{
desc_cycle . set ( " ...no description... " ) ; //Show new description
}
2023-04-24 19:14:34 +00:00
}
uint32_t freq_lock = recon_thread - > is_freq_lock ( ) ;
if ( freq_lock = = 0 ) {
//NO FREQ LOCK, ONGOING STANDARD SCANNING
2023-03-24 22:44:16 +00:00
big_display . set_style ( & style_white ) ;
if ( ! userpause )
button_pause . set_text ( " <PAUSE> " ) ;
else
button_pause . set_text ( " <RESUME> " ) ;
}
else if ( freq_lock = = 1 & & recon_lock_nb_match ! = 1 )
{
//STARTING LOCK FREQ
big_display . set_style ( & style_yellow ) ;
button_pause . set_text ( " <SKPLCK> " ) ;
}
else if ( index < 1000 & & freq_lock > = recon_thread - > get_lock_nb_match ( ) )
{
big_display . set_style ( & style_green ) ;
button_pause . set_text ( " <UNLOCK> " ) ;
//FREQ IS STRONG: GREEN and recon will pause when on_statistics_update()
if ( ( ! scanner_mode ) & & autosave & & last_freq ! = freq ) {
File recon_file ;
std : : string freq_file_path = " /FREQMAN/ " + output_file + " .TXT " ;
std : : string frequency_to_add ;
freqman_entry entry = frequency_list [ current_index ] ;
entry . frequency_a = recon_thread - > get_current_freq ( ) ;
entry . frequency_b = recon_thread - > get_current_freq ( ) ;
entry . modulation = recon_thread - > get_current_modulation ( ) ;
entry . bandwidth = recon_thread - > get_current_bandwidth ( ) ;
entry . type = SINGLE ;
get_freq_string ( entry , frequency_to_add ) ;
auto result = recon_file . open ( freq_file_path ) ; //First recon if freq is already in txt
if ( ! result . is_valid ( ) ) {
char one_char [ 1 ] ; //Read it char by char
std : : string line ; //and put read line in here
bool found = false ;
for ( size_t pointer = 0 ; pointer < recon_file . size ( ) ; pointer + + ) {
recon_file . seek ( pointer ) ;
recon_file . read ( one_char , 1 ) ;
if ( ( int ) one_char [ 0 ] > 31 ) { //ascii space upwards
line + = one_char [ 0 ] ; //Add it to the textline
}
else if ( one_char [ 0 ] = = ' \n ' ) { //New Line
if ( line . compare ( 0 , frequency_to_add . size ( ) , frequency_to_add ) = = 0 ) {
found = true ;
break ;
}
line . clear ( ) ; //Ready for next textline
}
}
if ( ! found ) {
result = recon_file . append ( freq_file_path ) ; //Second: append if it is not there
if ( ! result . is_valid ( ) )
{
recon_file . write_line ( frequency_to_add ) ;
}
}
}
else {
result = recon_file . create ( freq_file_path ) ; //First freq if no output file
if ( ! result . is_valid ( ) )
{
recon_file . write_line ( frequency_to_add ) ;
}
}
}
last_freq = freq ;
}
set_display_freq ( freq ) ; //UPDATE the big Freq after 0, 1 or recon_lock_nb_match (at least, for color synching)
}
void ReconView : : focus ( ) {
button_pause . focus ( ) ;
}
ReconView : : ~ ReconView ( ) {
ReconSetupSaveStrings ( " RECON/RECON.CFG " , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume . value ( ) ) ;
// save app settings
settings . save ( " recon " , & app_settings ) ;
audio : : output : : stop ( ) ;
receiver_model . disable ( ) ;
baseband : : shutdown ( ) ;
}
void ReconView : : show_max ( bool refresh_display ) { //show total number of freqs to recon
static int32_t last_db = 999999 ;
static int32_t last_nb_match = 999999 ;
static int32_t last_timer = - 1 ;
2023-05-04 13:20:12 +00:00
if ( recon_thread )
2023-03-24 22:44:16 +00:00
{
int32_t nb_match = recon_thread - > is_freq_lock ( ) ;
if ( last_db ! = db )
{
last_db = db ;
refresh_display = true ;
}
if ( last_nb_match ! = nb_match )
{
last_nb_match = nb_match ;
refresh_display = true ;
}
if ( last_timer ! = timer )
{
last_timer = timer ;
refresh_display = true ;
}
if ( refresh_display )
{
text_max . set ( " / " + to_string_dec_uint ( frequency_list . size ( ) ) + " " + to_string_dec_int ( db ) + " db " + to_string_dec_uint ( nb_match ) + " / " + to_string_dec_uint ( recon_thread - > get_lock_nb_match ( ) ) ) ;
freq_stats . set ( " RSSI: " + to_string_dec_int ( rssi . get_min ( ) ) + " / " + to_string_dec_int ( rssi . get_avg ( ) ) + " / " + to_string_dec_int ( rssi . get_max ( ) ) + " db " ) ;
text_timer . set ( " TIMER: " + to_string_dec_int ( timer ) ) ;
}
}
}
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
2023-05-04 13:20:12 +00:00
if ( check_sd_card ( ) ) { // Check to see if SD Card is mounted
2023-03-24 22:44:16 +00:00
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 ) ;
frequency_range . min = stored_freq - 1000000 ;
button_manual_start . set_text ( to_string_short_freq ( frequency_range . min ) ) ;
frequency_range . max = stored_freq + 1000000 ;
button_manual_end . set_text ( to_string_short_freq ( frequency_range . max ) ) ;
// Loading settings
autostart = persistent_memory : : recon_autostart_recon ( ) ;
autosave = persistent_memory : : recon_autosave_freqs ( ) ;
continuous = persistent_memory : : recon_continuous ( ) ;
filedelete = persistent_memory : : recon_clear_output ( ) ;
load_freqs = persistent_memory : : recon_load_freqs ( ) ;
load_ranges = persistent_memory : : recon_load_ranges ( ) ;
load_hamradios = persistent_memory : : recon_load_hamradios ( ) ;
update_ranges = persistent_memory : : recon_update_ranges_when_recon ( ) ;
field_volume . set_value ( volume ) ;
if ( sd_card_mounted )
{
2023-05-04 13:20:12 +00:00
//Loading input and output file from settings
ReconSetupLoadStrings ( " RECON/RECON.CFG " , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , volume ) ;
// load auto common app settings
2023-03-24 22:44:16 +00:00
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 ) ;
}
}
2023-05-04 13:20:12 +00:00
2023-03-24 22:44:16 +00:00
button_manual_start . on_select = [ this , & nav ] ( ButtonWithEncoder & button ) {
auto new_view = nav_ . push < FrequencyKeypadView > ( 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 < FrequencyKeypadView > ( 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 < FrequencyKeypadView > ( current_index ) ;
new_view - > on_changed = [ this , & button ] ( rf : : Frequency f ) {
f = f / 1000000 ;
if ( f > = 1 & & f < = frequency_list . size ( ) )
{
2023-03-26 13:11:27 +00:00
recon_thread - > set_index_stepper ( f - 1 - current_index ) ;
2023-03-24 22:44:16 +00:00
recon_thread - > set_freq_lock ( 0 ) ;
}
} ;
}
} ;
button_manual_start . on_change = [ this ] ( ) {
frequency_range . min = frequency_range . min + button_manual_start . get_encoder_delta ( ) * freqman_entry_get_step_value ( def_step ) ;
if ( frequency_range . min < 1 )
{
frequency_range . min = 1 ;
}
if ( frequency_range . min > ( MAX_UFREQ - freqman_entry_get_step_value ( def_step ) ) )
{
frequency_range . min = MAX_UFREQ - freqman_entry_get_step_value ( def_step ) ;
}
if ( frequency_range . min > ( frequency_range . max - freqman_entry_get_step_value ( def_step ) ) )
{
frequency_range . max = frequency_range . min + freqman_entry_get_step_value ( def_step ) ;
if ( frequency_range . max > MAX_UFREQ )
{
frequency_range . min = MAX_UFREQ - freqman_entry_get_step_value ( def_step ) ;
frequency_range . max = MAX_UFREQ ;
}
}
button_manual_start . set_text ( to_string_short_freq ( frequency_range . min ) ) ;
button_manual_end . set_text ( to_string_short_freq ( frequency_range . max ) ) ;
button_manual_start . set_encoder_delta ( 0 ) ;
} ;
button_manual_end . on_change = [ this ] ( ) {
frequency_range . max = frequency_range . max + button_manual_end . get_encoder_delta ( ) * freqman_entry_get_step_value ( def_step ) ;
if ( frequency_range . max < ( freqman_entry_get_step_value ( def_step ) + 1 ) )
{
frequency_range . max = ( freqman_entry_get_step_value ( def_step ) + 1 ) ;
}
if ( frequency_range . max > MAX_UFREQ )
{
frequency_range . max = MAX_UFREQ ;
}
if ( frequency_range . max < ( frequency_range . min + freqman_entry_get_step_value ( def_step ) ) )
{
frequency_range . min = frequency_range . max - freqman_entry_get_step_value ( def_step ) ;
if ( frequency_range . max < ( freqman_entry_get_step_value ( def_step ) + 1 ) )
{
frequency_range . min = 1 ;
frequency_range . max = ( freqman_entry_get_step_value ( def_step ) + 1 ) ;
}
}
button_manual_start . set_text ( to_string_short_freq ( frequency_range . min ) ) ;
button_manual_end . set_text ( to_string_short_freq ( frequency_range . max ) ) ;
button_manual_end . set_encoder_delta ( 0 ) ;
} ;
text_cycle . on_change = [ this ] ( ) {
if ( frequency_list . size ( ) > 0 )
{
2023-03-26 13:11:27 +00:00
if ( text_cycle . get_encoder_delta ( ) > 0 )
{
fwd = true ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " FW> " ) ;
recon_thread - > set_index_stepper ( 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
else if ( text_cycle . get_encoder_delta ( ) < 0 )
{
fwd = false ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " <RW " ) ;
recon_thread - > set_index_stepper ( - 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
}
2023-03-24 22:44:16 +00:00
text_cycle . set_encoder_delta ( 0 ) ;
} ;
button_pause . on_select = [ this ] ( ButtonWithEncoder & ) {
if ( recon_thread & & frequency_list . size ( ) > 0 )
{
if ( continuous_lock )
{
if ( fwd )
{
recon_thread - > set_stepper ( 1 ) ;
}
else
{
recon_thread - > set_stepper ( - 1 ) ;
}
timer = 0 ;
recon_resume ( ) ;
button_pause . set_text ( " <PAUSE> " ) ; //Show button for non continuous stop
continuous_lock = false ;
}
else
{
if ( userpause )
{
user_resume ( ) ;
}
else
{
user_pause ( ) ;
if ( update_ranges )
{
button_manual_start . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . min = frequency_list [ current_index ] . frequency_a ;
if ( frequency_list [ current_index ] . frequency_b ! = 0 )
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_b ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_b ;
}
else
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_a ;
}
}
}
}
}
} ;
button_pause . on_change = [ this ] ( ) {
if ( recon_thread & & frequency_list . size ( ) > 0 )
{
if ( button_pause . get_encoder_delta ( ) > 0 )
{
fwd = true ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " FW> " ) ;
recon_thread - > set_stepper ( 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
else if ( button_pause . get_encoder_delta ( ) < 0 )
{
fwd = false ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " <RW " ) ;
recon_thread - > set_stepper ( - 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
}
button_pause . set_encoder_delta ( 0 ) ;
} ;
button_audio_app . on_select = [ this ] ( Button & ) {
recon_thread - > stop ( ) ;
nav_ . pop ( ) ;
nav_ . push < AnalogAudioView > ( ) ;
} ;
button_looking_glass . on_select = [ this ] ( Button & ) {
recon_thread - > stop ( ) ;
nav_ . pop ( ) ;
nav_ . push < GlassView > ( ) ;
} ;
rssi . set_focusable ( true ) ;
rssi . set_peak ( true , 500 ) ;
rssi . on_select = [ this ] ( RSSI & ) {
recon_thread - > stop ( ) ;
nav_ . pop ( ) ;
nav_ . push < LevelView > ( ) ;
} ;
button_mic_app . on_select = [ this ] ( Button & ) {
recon_thread - > stop ( ) ;
nav_ . pop ( ) ;
nav_ . push < MicTXView > ( ) ;
} ;
button_remove . on_select = [ this ] ( ButtonWithEncoder & ) {
bool previous_is_recon = false ;
bool previous_userpause = userpause ;
if ( recon_thread )
{
previous_is_recon = recon_thread - > is_recon ( ) ;
recon_thread - > set_recon ( false ) ;
recon_thread - > set_freq_delete ( true ) ;
chThdSleepMilliseconds ( recon_lock_duration ) ; // give some time to Thread::Run to pause
}
if ( scanner_mode )
{
if ( frequency_list . size ( ) > 0 )
{
if ( current_index > = frequency_list . size ( ) )
{
current_index = frequency_list . size ( ) - 1 ;
}
frequency_list . erase ( frequency_list . begin ( ) + current_index ) ;
if ( current_index > = frequency_list . size ( ) )
{
current_index = frequency_list . size ( ) - 1 ;
}
if ( frequency_list . size ( ) > 0 )
{
recon_thread - > set_freq_index ( current_index ) ;
if ( frequency_list [ current_index ] . description . size ( ) > 0 )
{
2023-03-26 13:30:54 +00:00
switch ( frequency_list [ current_index ] . type )
{
case RANGE :
2023-05-04 13:20:12 +00:00
desc_cycle . set ( " R: " + frequency_list [ current_index ] . description ) ;
2023-03-26 13:30:54 +00:00
break ;
case HAMRADIO :
2023-05-04 13:20:12 +00:00
desc_cycle . set ( " H: " + frequency_list [ current_index ] . description ) ;
2023-03-26 13:30:54 +00:00
break ;
default :
case SINGLE :
2023-05-04 13:20:12 +00:00
desc_cycle . set ( " S: " + frequency_list [ current_index ] . description ) ;
2023-03-26 13:30:54 +00:00
break ;
}
2023-03-24 22:44:16 +00:00
}
else
{
2023-05-04 13:20:12 +00:00
desc_cycle . set ( " ...no description... " ) ;
show_max ( true ) ;
2023-03-24 22:44:16 +00:00
}
text_cycle . set_text ( to_string_dec_uint ( current_index + 1 , 3 ) ) ;
File freqman_file ;
std : : string freq_file_path = " FREQMAN/ " + output_file + " .TXT " ;
delete_file ( freq_file_path ) ;
auto result = freqman_file . create ( freq_file_path ) ;
if ( ! result . is_valid ( ) )
{
for ( size_t n = 0 ; n < frequency_list . size ( ) ; n + + )
{
std : : string line ;
get_freq_string ( frequency_list [ n ] , line ) ;
freqman_file . write_line ( line ) ;
}
}
}
}
}
else // RECON MODE / MANUAL, only remove matching from output
{
File recon_file ;
File tmp_recon_file ;
std : : string freq_file_path = " /FREQMAN/ " + output_file + " .TXT " ;
std : : string tmp_freq_file_path = " /FREQMAN/ " + output_file + " TMP.TXT " ;
std : : string frequency_to_add ;
freqman_entry entry = frequency_list [ current_index ] ;
entry . frequency_a = recon_thread - > get_current_freq ( ) ;
entry . frequency_b = recon_thread - > get_current_freq ( ) ;
entry . modulation = recon_thread - > get_current_modulation ( ) ;
entry . bandwidth = recon_thread - > get_current_bandwidth ( ) ;
entry . type = SINGLE ;
get_freq_string ( entry , frequency_to_add ) ;
delete_file ( tmp_freq_file_path ) ;
auto result = tmp_recon_file . create ( tmp_freq_file_path ) ; //First recon if freq is already in txt
//
if ( ! result . is_valid ( ) ) {
bool found = false ;
result = recon_file . open ( freq_file_path ) ; //First recon if freq is already in txt
if ( ! result . is_valid ( ) ) {
char one_char [ 1 ] ; //Read it char by char
std : : string line ; //and put read line in here
for ( size_t pointer = 0 ; pointer < recon_file . size ( ) ; pointer + + ) {
recon_file . seek ( pointer ) ;
recon_file . read ( one_char , 1 ) ;
if ( ( int ) one_char [ 0 ] > 31 ) { //ascii space upwards
line + = one_char [ 0 ] ; //Add it to the textline
}
else if ( one_char [ 0 ] = = ' \n ' ) { //New Line
if ( line . compare ( 0 , frequency_to_add . size ( ) , frequency_to_add ) = = 0 ) {
found = true ;
}
else
{
tmp_recon_file . write_line ( frequency_to_add ) ;
}
line . clear ( ) ; //Ready for next textline
}
}
if ( found )
{
delete_file ( freq_file_path ) ;
rename_file ( tmp_freq_file_path , freq_file_path ) ;
}
else
{
delete_file ( tmp_freq_file_path ) ;
}
}
}
}
if ( frequency_list . size ( ) = = 0 )
{
text_cycle . set_text ( " " ) ;
desc_cycle . set ( " no entries in list " ) ; //Show new description
show_max ( true ) ; //UPDATE new list size on screen
delete_file ( " FREQMAN/ " + output_file + " .TXT " ) ;
}
if ( recon_thread )
{
timer = 0 ;
recon_thread - > set_freq_index ( current_index ) ;
RetuneMessage message { } ;
receiver_model . set_tuning_frequency ( frequency_list [ current_index ] . frequency_a ) ; // Retune
message . freq = frequency_list [ current_index ] . frequency_a ;
message . range = current_index ;
EventDispatcher : : send_message ( message ) ;
chThdSleepMilliseconds ( recon_lock_duration ) ; // give some time to Thread::Run to pause
if ( previous_userpause )
{
user_pause ( ) ;
}
else
{
user_resume ( ) ;
}
recon_thread - > set_freq_delete ( false ) ;
recon_thread - > set_recon ( previous_is_recon ) ;
}
} ;
button_remove . on_change = [ this ] ( ) {
if ( recon_thread & & frequency_list . size ( ) > 0 )
{
timer = 0 ;
if ( button_remove . get_encoder_delta ( ) > 0 )
{
fwd = true ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " FW> " ) ;
recon_thread - > set_stepper ( 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
else if ( button_remove . get_encoder_delta ( ) < 0 )
{
fwd = false ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " <RW " ) ;
recon_thread - > set_stepper ( - 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
}
button_remove . set_encoder_delta ( 0 ) ;
} ;
button_manual_recon . on_select = [ this ] ( Button & ) {
scanner_mode = false ;
manual_mode = true ;
if ( ! frequency_range . min | | ! frequency_range . max ) {
nav_ . display_modal ( " Error " , " Both START and END freqs \n need a value " ) ;
} else if ( frequency_range . min > frequency_range . max ) {
nav_ . display_modal ( " Error " , " END freq \n is lower than START " ) ;
} else {
audio : : output : : stop ( ) ;
recon_thread - > stop ( ) ; //STOP SCANNER THREAD
frequency_list . clear ( ) ;
freqman_entry manual_freq_entry ;
def_step = step_mode . selected_index ( ) ; // max range val
manual_freq_entry . type = RANGE ;
manual_freq_entry . description =
" R " + to_string_short_freq ( frequency_range . min ) + " > "
+ to_string_short_freq ( frequency_range . max ) + " S " // current Manual range
+ to_string_short_freq ( freqman_entry_get_step_value ( def_step ) ) . erase ( 0 , 1 ) ; //euquiq: lame kludge to reduce spacing in step freq
manual_freq_entry . frequency_a = frequency_range . min ; // min range val
manual_freq_entry . frequency_b = frequency_range . max ; // max range val
manual_freq_entry . modulation = - 1 ;
manual_freq_entry . bandwidth = - 1 ;
manual_freq_entry . step = def_step ;
frequency_list . push_back ( manual_freq_entry ) ;
big_display . set_style ( & style_white ) ; //Back to white color
set_display_freq ( frequency_range . min ) ;
freq_stats . set_style ( & style_white ) ;
freq_stats . set ( " 0/0/0 " ) ;
show_max ( ) ; /* display step information */
2023-05-04 13:20:12 +00:00
text_cycle . set_text ( " 1 " ) ;
text_max . set ( " /1 " ) ;
2023-03-24 22:44:16 +00:00
button_scanner_mode . set_style ( & style_white ) ;
button_scanner_mode . set_text ( " MSEARCH " ) ;
file_name . set_style ( & style_white ) ;
2023-05-04 13:20:12 +00:00
file_name . set ( " MANUAL RANGE RECON " ) ;
desc_cycle . set_style ( & style_white ) ;
desc_cycle . set ( " MANUAL RANGE RECON " ) ;
2023-03-24 22:44:16 +00:00
start_recon_thread ( ) ;
user_resume ( ) ;
}
} ;
field_mode . on_change = [ this ] ( size_t , OptionsField : : value_t v ) {
if ( v ! = - 1 )
{
receiver_model . disable ( ) ;
baseband : : shutdown ( ) ;
change_mode ( v ) ;
if ( ! recon_thread - > is_recon ( ) ) //for some motive, audio output gets stopped.
audio : : output : : start ( ) ; //So if recon was stopped we resume audio
receiver_model . enable ( ) ;
}
} ;
button_dir . on_select = [ this ] ( Button & ) {
recon_thread - > change_recon_direction ( ) ;
fwd = recon_thread - > get_recon_direction ( ) ;
if ( fwd )
{
button_dir . set_text ( " FW> " ) ;
}
else
{
button_dir . set_text ( " <RW " ) ;
}
timer = 0 ;
if ( userpause ) //If user-paused, resume
user_resume ( ) ;
} ;
button_restart . on_select = [ this ] ( Button & ) {
if ( frequency_list . size ( ) > 0 )
{
2023-05-04 13:20:12 +00:00
def_step = step_mode . selected_index ( ) ; //Use def_step from manual selector
2023-03-24 22:44:16 +00:00
frequency_file_load ( true ) ;
if ( recon_thread )
{
recon_thread - > set_lock_duration ( recon_lock_duration ) ;
recon_thread - > set_lock_nb_match ( recon_lock_nb_match ) ;
}
if ( fwd )
{
button_dir . set_text ( " FW> " ) ;
}
else
{
button_dir . set_text ( " <RW " ) ;
}
timer = 0 ; //Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
show_max ( ) ;
user_resume ( ) ;
}
2023-05-04 13:20:12 +00:00
if ( scanner_mode )
{
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 " ) ;
}
2023-03-24 22:44:16 +00:00
} ;
button_add . on_select = [ this ] ( ButtonWithEncoder & ) { //frequency_list[current_index]
if ( ! scanner_mode )
{
if ( recon_thread & & frequency_list . size ( ) & & frequency_list . size ( ) & & current_index < frequency_list . size ( ) )
{
int64_t freq = recon_thread - > get_current_freq ( ) ;
bool found = false ;
File recon_file ;
std : : string freq_file_path = " /FREQMAN/ " + output_file + " .TXT " ;
std : : string frequency_to_add ;
freqman_entry entry = frequency_list [ current_index ] ;
entry . frequency_a = recon_thread - > get_current_freq ( ) ;
entry . frequency_b = recon_thread - > get_current_freq ( ) ;
entry . modulation = recon_thread - > get_current_modulation ( ) ;
entry . bandwidth = recon_thread - > get_current_bandwidth ( ) ;
entry . type = SINGLE ;
get_freq_string ( entry , frequency_to_add ) ;
auto result = recon_file . open ( freq_file_path ) ; //First recon if freq is already in txt
if ( ! result . is_valid ( ) ) {
char one_char [ 1 ] ; //Read it char by char
std : : string line ; //and put read line in here
for ( size_t pointer = 0 ; pointer < recon_file . size ( ) ; pointer + + ) {
recon_file . seek ( pointer ) ;
recon_file . read ( one_char , 1 ) ;
if ( ( int ) one_char [ 0 ] > 31 ) { //ascii space upwards
line + = one_char [ 0 ] ; //Add it to the textline
}
else if ( one_char [ 0 ] = = ' \n ' ) { //New Line
if ( line . compare ( 0 , frequency_to_add . size ( ) , frequency_to_add ) = = 0 ) {
found = true ;
break ;
}
line . clear ( ) ; //Ready for next textline
}
}
if ( ! found ) {
result = recon_file . append ( freq_file_path ) ; //Second: append if it is not there
if ( ! result . is_valid ( ) )
{
recon_file . write_line ( frequency_to_add ) ;
}
}
if ( found ) {
nav_ . display_modal ( " Error " , " Frequency already exists " ) ;
set_display_freq ( freq ) ;
}
}
else
{
auto result = recon_file . create ( freq_file_path ) ; //third: create if it is not there
if ( ! result . is_valid ( ) )
{
recon_file . write_line ( frequency_to_add ) ;
}
}
}
}
} ;
button_add . on_change = [ this ] ( ) {
if ( recon_thread & & frequency_list . size ( ) > 0 )
{
timer = 0 ;
if ( button_add . get_encoder_delta ( ) > 0 )
{
fwd = true ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " FW> " ) ;
recon_thread - > set_stepper ( 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
else if ( button_add . get_encoder_delta ( ) < 0 )
{
fwd = false ;
recon_thread - > set_recon_direction ( fwd ) ;
button_dir . set_text ( " <RW " ) ;
recon_thread - > set_stepper ( - 1 ) ;
recon_thread - > set_freq_lock ( 0 ) ;
}
}
button_add . set_encoder_delta ( 0 ) ;
} ;
button_scanner_mode . on_select = [ this , & nav ] ( Button & ) {
manual_mode = false ;
if ( scanner_mode )
{
scanner_mode = false ;
button_scanner_mode . set_style ( & style_blue ) ;
button_scanner_mode . set_text ( " RECON " ) ;
}
else
{
scanner_mode = true ;
button_scanner_mode . set_style ( & style_red ) ;
button_scanner_mode . set_text ( " SCANNER " ) ;
}
frequency_file_load ( true ) ;
if ( recon_thread )
{
recon_thread - > set_lock_duration ( recon_lock_duration ) ;
recon_thread - > set_lock_nb_match ( recon_lock_nb_match ) ;
recon_thread - > set_continuous ( continuous ) ;
recon_thread - > set_recon_direction ( fwd ) ;
}
if ( autostart )
{
user_resume ( ) ;
}
else
{
user_pause ( ) ;
}
} ;
button_recon_setup . on_select = [ this , & nav ] ( Button & ) {
ReconSetupSaveStrings ( " RECON/RECON.CFG " , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume . value ( ) ) ;
2023-05-04 13:20:12 +00:00
if ( frequency_list . size ( ) ! = 0 )
user_pause ( ) ;
2023-03-24 22:44:16 +00:00
auto open_view = nav . push < ReconSetupView > ( input_file , output_file , recon_lock_duration , recon_lock_nb_match , recon_match_mode ) ;
open_view - > on_changed = [ this ] ( std : : vector < std : : string > 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 ) ;
2023-05-04 13:20:12 +00:00
recon_match_mode = strtol ( result [ 4 ] . c_str ( ) , nullptr , 10 ) ;
2023-03-24 22:44:16 +00:00
ReconSetupSaveStrings ( " RECON/RECON.CFG " , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume . value ( ) ) ;
autosave = persistent_memory : : recon_autosave_freqs ( ) ;
autostart = persistent_memory : : recon_autostart_recon ( ) ;
continuous = persistent_memory : : recon_continuous ( ) ;
filedelete = persistent_memory : : recon_clear_output ( ) ;
load_freqs = persistent_memory : : recon_load_freqs ( ) ;
load_ranges = persistent_memory : : recon_load_ranges ( ) ;
load_hamradios = persistent_memory : : recon_load_hamradios ( ) ;
update_ranges = persistent_memory : : recon_update_ranges_when_recon ( ) ;
frequency_file_load ( true ) ;
if ( recon_thread )
{
recon_thread - > set_lock_duration ( recon_lock_duration ) ;
recon_thread - > set_lock_nb_match ( recon_lock_nb_match ) ;
recon_thread - > set_continuous ( continuous ) ;
recon_thread - > set_recon_direction ( fwd ) ;
}
if ( autostart )
{
user_resume ( ) ;
}
else
{
user_pause ( ) ;
}
if ( update_ranges )
{
button_manual_start . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . min = frequency_list [ current_index ] . frequency_a ;
if ( frequency_list [ current_index ] . frequency_b ! = 0 )
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_b ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_b ;
}
else
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_a ;
}
}
lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ;
lock_wait = lock_wait * 100 ; // poor man's rounding
if ( lock_wait < 400 )
lock_wait = 400 ;
field_lock_wait . set_value ( lock_wait ) ;
show_max ( ) ;
return ;
} ;
if ( userpause ! = true )
{
timer = 0 ;
user_resume ( ) ;
}
else
{
RetuneMessage message { } ;
message . freq = recon_thread - > get_current_freq ( ) ;
message . range = current_index ;
EventDispatcher : : send_message ( message ) ;
}
} ;
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 ) ;
}
} ;
2023-05-04 13:20:12 +00:00
2023-03-24 22:44:16 +00:00
field_lock_wait . on_change = [ this ] ( int32_t v )
{
lock_wait = v ;
uint32_t lock_white = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ;
lock_white = lock_white * 100 ;
if ( lock_white < 400 )
lock_white = 400 ;
uint32_t lock_yellow = 300 ;
if ( lock_wait > = lock_white )
{
field_lock_wait . set_style ( & style_white ) ;
}
else if ( lock_wait > = lock_yellow )
{
field_lock_wait . set_style ( & style_yellow ) ;
}
else
{
field_lock_wait . set_style ( & style_red ) ;
}
} ;
2023-05-04 13:20:12 +00:00
field_squelch . on_change = [ this ] ( int32_t v ) {
squelch = v ;
} ;
field_volume . on_change = [ this ] ( int32_t v ) {
this - > on_headphone_volume_changed ( v ) ;
} ;
//PRE-CONFIGURATION:
change_mode ( AM_MODULATION ) ; //Start on AM
field_mode . set_by_value ( AM_MODULATION ) ; //Reflect the mode into the manual selector
button_scanner_mode . set_style ( & style_blue ) ;
button_scanner_mode . set_text ( " RECON " ) ;
file_name . set ( " => " ) ;
field_squelch . set_value ( squelch ) ;
2023-03-24 22:44:16 +00:00
field_wait . set_value ( wait ) ;
lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) ;
lock_wait = lock_wait / 100 ; lock_wait = lock_wait * 100 ; // poor man's rounding
if ( lock_wait < 400 )
lock_wait = 400 ;
field_lock_wait . set_value ( lock_wait ) ;
field_volume . set_value ( ( receiver_model . headphone_volume ( ) - audio : : headphone : : volume_range ( ) . max ) . decibel ( ) + 99 ) ;
//FILL STEP OPTIONS
freqman_set_modulation_option ( field_mode ) ;
freqman_set_step_option ( step_mode ) ;
2023-05-04 13:20:12 +00:00
receiver_model . set_tuning_frequency ( portapack : : persistent_memory : : tuned_frequency ( ) ) ; // Retune
2023-03-24 22:44:16 +00:00
if ( filedelete )
{
delete_file ( " FREQMAN/ " + output_file + " .TXT " ) ;
}
frequency_file_load ( false ) ; /* do not stop all at start */
2023-05-04 13:20:12 +00:00
if ( recon_thread )
2023-03-24 22:44:16 +00:00
{
recon_thread - > set_lock_duration ( recon_lock_duration ) ;
recon_thread - > set_lock_nb_match ( recon_lock_nb_match ) ;
2023-05-04 13:20:12 +00:00
if ( update_ranges & & frequency_list . size ( ) > 0 )
2023-03-24 22:44:16 +00:00
{
button_manual_start . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . min = frequency_list [ current_index ] . frequency_a ;
if ( frequency_list [ current_index ] . frequency_b ! = 0 )
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_b ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_b ;
}
else
{
button_manual_end . set_text ( to_string_short_freq ( frequency_list [ current_index ] . frequency_a ) ) ;
frequency_range . max = frequency_list [ current_index ] . frequency_a ;
}
}
if ( autostart )
{
timer = 0 ; //Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
user_resume ( ) ;
}
else
{
user_pause ( ) ;
}
show_max ( ) ;
}
}
void ReconView : : frequency_file_load ( bool stop_all_before ) {
// stop everything running now if required
if ( stop_all_before ) {
recon_thread - > stop ( ) ;
}
audio : : output : : stop ( ) ;
2023-05-04 13:20:12 +00:00
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 )
2023-03-24 22:44:16 +00:00
{
2023-05-04 13:20:12 +00:00
file_input = output_file ;
file_name . set_style ( & style_red ) ;
button_scanner_mode . set_style ( & style_red ) ;
button_scanner_mode . set_text ( " SCANNER " ) ;
2023-03-24 22:44:16 +00:00
}
else
{
2023-05-04 13:20:12 +00:00
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 )
2023-03-24 22:44:16 +00:00
{
2023-05-04 13:20:12 +00:00
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 ) ;
2023-03-24 22:44:16 +00:00
}
else
{
2023-05-04 13:20:12 +00:00
if ( frequency_list . size ( ) > FREQMAN_MAX_PER_FILE )
{
file_name . set_style ( & style_yellow ) ;
desc_cycle . set_style ( & style_yellow ) ;
}
2023-03-24 22:44:16 +00:00
}
}
2023-05-04 13:20:12 +00:00
step_mode . set_selected_index ( def_step ) ; //Impose the default step into the manual step selector
2023-03-24 22:44:16 +00:00
start_recon_thread ( ) ;
2023-05-04 13:20:12 +00:00
std : : string description = " ...no description... " ;
if ( frequency_list . size ( ) ! = 0 )
{
current_index = 0 ;
recon_thread - > set_freq_index ( 0 ) ;
switch ( frequency_list [ current_index ] . type )
{
case RANGE :
description = " R: " + frequency_list [ current_index ] . description ;
break ;
case HAMRADIO :
description = " H: " + frequency_list [ current_index ] . description ;
break ;
default :
case SINGLE :
description = " S: " + frequency_list [ current_index ] . description ;
break ;
}
text_cycle . set_text ( to_string_dec_uint ( current_index + 1 , 3 ) ) ;
}
else
{
text_cycle . set_text ( " " ) ;
}
desc_cycle . set ( description ) ;
2023-03-24 22:44:16 +00:00
}
void ReconView : : on_statistics_update ( const ChannelStatistics & statistics ) {
int32_t actual_db = statistics . max_db ;
bool update_stats = false ;
int32_t freq_lock = recon_thread - > is_freq_lock ( ) ;
int32_t max_lock = recon_thread - > get_lock_nb_match ( ) ;
// 0 recon , 1 locking , 2 locked
static int32_t status = - 1 ;
if ( actual_db ! = 0 & & db ! = actual_db )
{
db = actual_db ;
update_stats = true ;
}
if ( ! userpause )
2022-11-20 18:46:01 +00:00
{
2023-03-24 22:44:16 +00:00
if ( ! timer )
{
if ( status ! = 0 )
{
status = 0 ;
update_stats = true ;
continuous_lock = false ;
recon_thread - > set_freq_lock ( 0 ) ; //in lock period, still analyzing the signal
recon_resume ( ) ; // RESUME!
big_display . set_style ( & style_white ) ;
}
}
if ( freq_lock > = max_lock ) // LOCKED
{
if ( status ! = 2 )
{
status = 2 ;
update_stats = true ;
if ( wait ! = 0 )
{
audio : : output : : start ( ) ;
this - > on_headphone_volume_changed ( ( receiver_model . headphone_volume ( ) - audio : : headphone : : volume_range ( ) . max ) . decibel ( ) + 99 ) ;
}
if ( wait > = 0 )
{
timer = wait ;
}
//Inform freq (for coloring purposes also!)
RetuneMessage message { } ;
message . freq = recon_thread - > get_current_freq ( ) ;
message . range = current_index ;
EventDispatcher : : send_message ( message ) ;
}
if ( wait < 0 )
{
if ( actual_db > squelch ) //MATCHING LEVEL IN STAY X AFTER LAST ACTIVITY
{
timer = abs ( wait ) ;
}
}
}
else // freq_lock < max_lock , LOCKING
{
if ( actual_db > squelch ) //MATCHING LEVEL
{
if ( status ! = 1 )
{
status = 1 ;
continuous_lock = true ;
if ( wait ! = 0 )
{
audio : : output : : stop ( ) ;
}
timer = lock_wait ;
//Inform freq (for coloring purposes also!)
RetuneMessage message { } ;
message . freq = recon_thread - > get_current_freq ( ) ;
current_index = recon_thread - > get_freq_index ( ) ;
message . range = current_index ;
EventDispatcher : : send_message ( message ) ;
}
freq_lock + + ;
recon_thread - > set_freq_lock ( freq_lock ) ; //in lock period, still analyzing the signal
update_stats = true ;
}
else
{
// continuous, direct cut it if not consecutive match
if ( recon_match_mode = = 0 )
{
timer = 0 ;
update_stats = true ;
}
}
}
2022-11-20 18:46:01 +00:00
}
else
{
2023-03-24 22:44:16 +00:00
if ( actual_db > squelch )
{
if ( status ! = 2 ) //MATCHING LEVEL
{
status = 2 ;
big_display . set_style ( & style_yellow ) ;
set_display_freq ( recon_thread - > get_current_freq ( ) ) ;
}
}
else
{
if ( status ! = 0 )
{
status = 0 ;
big_display . set_style ( & style_white ) ;
set_display_freq ( recon_thread - > get_current_freq ( ) ) ;
}
}
}
if ( update_stats )
{
show_max ( ) ;
2022-11-20 18:46:01 +00:00
}
2023-03-24 22:44:16 +00:00
if ( timer > 0 )
show_max ( ) ;
2022-09-11 14:07:08 +00:00
2023-03-24 22:44:16 +00:00
timer - = 50 ;
if ( timer < 0 )
{
timer = 0 ;
show_max ( ) ;
}
} /* on_statistic_updates */
void ReconView : : recon_pause ( ) {
if ( recon_thread - > is_recon ( ) )
{
audio : : output : : start ( ) ;
this - > on_headphone_volume_changed ( ( receiver_model . headphone_volume ( ) - audio : : headphone : : volume_range ( ) . max ) . decibel ( ) + 99 ) ;
recon_thread - > set_freq_lock ( 0 ) ; //in lock period, still analyzing the signal
recon_thread - > set_recon ( false ) ; // WE STOP SCANNING
}
}
void ReconView : : recon_resume ( ) {
audio : : output : : stop ( ) ;
if ( ! recon_thread - > is_recon ( ) )
2023-05-04 13:20:12 +00:00
recon_thread - > set_recon ( true ) ; // RESUME!
2023-03-24 22:44:16 +00:00
big_display . set_style ( & style_white ) ; //Back to grey color
}
void ReconView : : user_pause ( ) {
2023-05-04 13:20:12 +00:00
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
2023-03-24 22:44:16 +00:00
//button_pause.set_text("<RESUME>"); //PAUSED, show resume
userpause = true ;
continuous_lock = false ;
recon_pause ( ) ;
}
void ReconView : : user_resume ( ) {
2023-05-04 13:20:12 +00:00
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
2023-03-24 22:44:16 +00:00
//button_pause.set_text("<PAUSE>"); //Show button for pause
2023-05-04 13:20:12 +00:00
userpause = false ; // Resume recon
2023-03-24 22:44:16 +00:00
continuous_lock = false ;
recon_resume ( ) ;
}
void ReconView : : on_headphone_volume_changed ( int32_t v ) {
const auto new_volume = volume_t : : decibel ( v - 99 ) + audio : : headphone : : volume_range ( ) . max ;
receiver_model . set_headphone_volume ( new_volume ) ;
}
size_t ReconView : : change_mode ( freqman_index_t new_mod ) { //Before this, do a recon_thread->stop(); After this do a start_recon_thread()
field_bw . on_change = [ this ] ( size_t n , OptionsField : : value_t ) { ( void ) n ; } ;
switch ( new_mod ) {
case AM_MODULATION :
freqman_set_bandwidth_option ( new_mod , field_bw ) ;
//bw DSB (0) default
field_bw . set_selected_index ( 0 ) ;
baseband : : run_image ( portapack : : spi_flash : : image_tag_am_audio ) ;
receiver_model . set_modulation ( ReceiverModel : : Mode : : AMAudio ) ;
receiver_model . set_am_configuration ( field_bw . selected_index ( ) ) ;
field_bw . on_change = [ this ] ( size_t n , OptionsField : : value_t ) { receiver_model . set_am_configuration ( n ) ; } ;
receiver_model . set_sampling_rate ( 3072000 ) ; receiver_model . set_baseband_bandwidth ( 1750000 ) ;
2023-05-04 13:26:33 +00:00
text_ctcss . set ( " " ) ;
2023-03-24 22:44:16 +00:00
break ;
case NFM_MODULATION :
freqman_set_bandwidth_option ( new_mod , field_bw ) ;
//bw 16k (2) default
field_bw . set_selected_index ( 2 ) ;
baseband : : run_image ( portapack : : spi_flash : : image_tag_nfm_audio ) ;
receiver_model . set_modulation ( ReceiverModel : : Mode : : NarrowbandFMAudio ) ;
receiver_model . set_nbfm_configuration ( field_bw . selected_index ( ) ) ;
field_bw . on_change = [ this ] ( size_t n , OptionsField : : value_t ) { receiver_model . set_nbfm_configuration ( n ) ; } ;
receiver_model . set_sampling_rate ( 3072000 ) ; receiver_model . set_baseband_bandwidth ( 1750000 ) ;
break ;
case WFM_MODULATION :
freqman_set_bandwidth_option ( new_mod , field_bw ) ;
//bw 200k (0) only/default
field_bw . set_selected_index ( 0 ) ;
baseband : : run_image ( portapack : : spi_flash : : image_tag_wfm_audio ) ;
receiver_model . set_modulation ( ReceiverModel : : Mode : : WidebandFMAudio ) ;
receiver_model . set_wfm_configuration ( field_bw . selected_index ( ) ) ;
field_bw . on_change = [ this ] ( size_t n , OptionsField : : value_t ) { receiver_model . set_wfm_configuration ( n ) ; } ;
receiver_model . set_sampling_rate ( 3072000 ) ; receiver_model . set_baseband_bandwidth ( 1750000 ) ;
2023-05-04 13:26:33 +00:00
text_ctcss . set ( " " ) ;
2023-03-24 22:44:16 +00:00
break ;
default :
break ;
}
return freqman_entry_get_step_value ( def_step ) ;
}
void ReconView : : start_recon_thread ( ) {
receiver_model . enable ( ) ;
receiver_model . set_squelch_level ( 0 ) ;
recon_thread = std : : make_unique < ReconThread > ( & frequency_list ) ;
recon_thread - > set_continuous ( continuous ) ;
recon_thread - > set_lock_duration ( recon_lock_duration ) ;
recon_thread - > set_lock_nb_match ( recon_lock_nb_match ) ;
recon_thread - > set_recon_direction ( fwd ) ;
}
void ReconView : : handle_coded_squelch ( const uint32_t value ) {
static int32_t last_idx = - 1 ;
float diff , min_diff = value ;
size_t min_idx { 0 } ;
size_t c ;
if ( field_mode . selected_index ( ) ! = NFM_MODULATION )
{
2023-05-04 13:26:33 +00:00
text_ctcss . set ( " " ) ;
2023-03-24 22:44:16 +00:00
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
2023-05-04 13:26:33 +00:00
text_ctcss . set ( " " ) ;
2023-03-24 22:44:16 +00:00
}
}
2022-09-11 14:07:08 +00:00
} /* namespace ui */