mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-02-03 10:00:12 -05:00
parent
6bb2a3559d
commit
d059248802
@ -27,15 +27,15 @@ using namespace portapack;
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
ScannerThread::ScannerThread(
|
ScannerThread::ScannerThread(std::vector<rf::Frequency> frequency_list) : frequency_list_ { std::move(frequency_list) }
|
||||||
std::vector<rf::Frequency> frequency_list
|
|
||||||
) : frequency_list_ { std::move(frequency_list) }
|
|
||||||
{
|
{
|
||||||
|
_manual_search = false;
|
||||||
create_thread();
|
create_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step) : frequency_range_(frequency_range), def_step_(def_step)
|
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz) : frequency_range_(frequency_range), def_step_hz_(def_step_hz)
|
||||||
{
|
{
|
||||||
|
_manual_search = true;
|
||||||
create_thread();
|
create_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +56,7 @@ void ScannerThread::stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Set by "userpause"
|
||||||
void ScannerThread::set_scanning(const bool v) {
|
void ScannerThread::set_scanning(const bool v) {
|
||||||
_scanning = v;
|
_scanning = v;
|
||||||
}
|
}
|
||||||
@ -72,14 +73,26 @@ uint32_t ScannerThread::is_freq_lock() {
|
|||||||
return _freq_lock;
|
return _freq_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerThread::set_freq_del(const uint32_t v) {
|
//Delete an entry from frequency list
|
||||||
|
//Caller must pause scan_thread AND can't delete a second one until this field is cleared
|
||||||
|
void ScannerThread::set_freq_del(const rf::Frequency v) {
|
||||||
_freq_del = v;
|
_freq_del = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerThread::change_scanning_direction() {
|
//Force a one-time forward or reverse frequency index change; OK to do this without pausing scan thread
|
||||||
_fwd = !_fwd;
|
//(used when rotary encoder is turned)
|
||||||
chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction
|
void ScannerThread::set_index_stepper(const int32_t v) {
|
||||||
|
_index_stepper = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set scanning direction; OK to do this without pausing scan_thread
|
||||||
|
void ScannerThread::set_scanning_direction(bool fwd) {
|
||||||
|
int32_t new_stepper = fwd? 1 : -1;
|
||||||
|
|
||||||
|
if (_stepper != new_stepper) {
|
||||||
|
_stepper = new_stepper;
|
||||||
|
chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_t ScannerThread::static_fn(void* arg) {
|
msg_t ScannerThread::static_fn(void* arg) {
|
||||||
@ -89,117 +102,136 @@ msg_t ScannerThread::static_fn(void* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScannerThread::run() {
|
void ScannerThread::run() {
|
||||||
if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ...
|
RetuneMessage message { };
|
||||||
RetuneMessage message { };
|
|
||||||
uint32_t frequency_index = frequency_list_.size();
|
|
||||||
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
|
|
||||||
while( !chThdShouldTerminate() ) {
|
|
||||||
if (_scanning) { //Scanning
|
|
||||||
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
|
|
||||||
if (!restart_scan) { //looping at full speed
|
|
||||||
if (_fwd) { //forward
|
|
||||||
frequency_index++;
|
|
||||||
if (frequency_index >= frequency_list_.size())
|
|
||||||
frequency_index = 0;
|
|
||||||
|
|
||||||
} else { //reverse
|
if (!_manual_search && frequency_list_.size()) { //IF NOT MANUAL MODE AND THERE IS A FREQUENCY LIST ...
|
||||||
if (frequency_index < 1)
|
int32_t size = frequency_list_.size();
|
||||||
frequency_index = frequency_list_.size();
|
int32_t frequency_index = (_stepper > 0)? size : 0; //Forcing wraparound to starting frequency on 1st pass
|
||||||
frequency_index--;
|
|
||||||
}
|
while( !chThdShouldTerminate() ) {
|
||||||
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune
|
bool force_one_step = (_index_stepper != 0);
|
||||||
}
|
int32_t step = force_one_step? _index_stepper : _stepper; //_index_stepper direction takes priority
|
||||||
else
|
|
||||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
if (_scanning || force_one_step) { //Scanning, or paused and using rotary encoder
|
||||||
|
if ((_freq_lock == 0) || force_one_step) { //normal scanning (not performing freq_lock)
|
||||||
|
frequency_index += step;
|
||||||
|
if (frequency_index >= size) //Wrap
|
||||||
|
frequency_index = 0;
|
||||||
|
else if (frequency_index < 0)
|
||||||
|
frequency_index = size-1;
|
||||||
|
|
||||||
|
if (force_one_step)
|
||||||
|
_index_stepper = 0;
|
||||||
|
|
||||||
|
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune
|
||||||
}
|
}
|
||||||
message.freq = frequency_list_[frequency_index];
|
message.freq = frequency_list_[frequency_index];
|
||||||
message.range = frequency_index; //Inform freq (for coloring purposes also!)
|
message.range = frequency_index; //Inform freq (for coloring purposes also!)
|
||||||
EventDispatcher::send_message(message);
|
EventDispatcher::send_message(message);
|
||||||
}
|
}
|
||||||
else { //NOT scanning
|
else if (_freq_del != 0) { //There is a frequency to delete
|
||||||
if (_freq_del != 0) { //There is a frequency to delete
|
for (int32_t i = 0; i < size; i++) { //Search for the freq to delete
|
||||||
for (uint32_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
|
if (frequency_list_[i] == _freq_del)
|
||||||
if (frequency_list_[i] == _freq_del)
|
{ //found: Erase it
|
||||||
{ //found: Erase it
|
frequency_list_.erase(frequency_list_.begin() + i);
|
||||||
frequency_list_.erase(frequency_list_.begin() + i);
|
size = frequency_list_.size();
|
||||||
if (i==0) //set scan index one place back to compensate
|
break;
|
||||||
i=frequency_list_.size();
|
}
|
||||||
else
|
}
|
||||||
i--;
|
_freq_del = 0; //deleted.
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_freq_del = 0; //deleted.
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
|
||||||
}
|
|
||||||
}else if(def_step_ > 0) // manual mode
|
|
||||||
{
|
|
||||||
RetuneMessage message { };
|
|
||||||
int64_t frequency_index = 0;
|
|
||||||
int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_;
|
|
||||||
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
|
|
||||||
while( !chThdShouldTerminate() ) {
|
|
||||||
if (_scanning) { //Scanning
|
|
||||||
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
|
|
||||||
if (!restart_scan) { //looping at full speed
|
|
||||||
if (_fwd) { //forward
|
|
||||||
frequency_index++;
|
|
||||||
if (frequency_index >= size)
|
|
||||||
frequency_index = 0;
|
|
||||||
|
|
||||||
} else { //reverse
|
chThdSleepMilliseconds(SCANNER_SLEEP_MS); //Needed to (eventually) stabilize the receiver into new freq
|
||||||
if (frequency_index < 1)
|
}
|
||||||
frequency_index = size;
|
}else if(_manual_search && (def_step_hz_ > 0)) // manual search range mode
|
||||||
frequency_index--;
|
{
|
||||||
}
|
int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_hz_;
|
||||||
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_); // Retune
|
int64_t frequency_index = (_stepper > 0)? size : 0; //Forcing wraparound to starting frequency on 1st pass
|
||||||
}
|
|
||||||
else
|
while( !chThdShouldTerminate() ) {
|
||||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
bool force_one_step = (_index_stepper != 0);
|
||||||
|
int32_t step = force_one_step? _index_stepper : _stepper; //_index_stepper direction takes priority
|
||||||
|
|
||||||
|
if (_scanning || force_one_step) { //Scanning, or paused and using rotary encoder
|
||||||
|
if ((_freq_lock == 0) || force_one_step) { //normal scanning (not performing freq_lock)
|
||||||
|
frequency_index += step;
|
||||||
|
if (frequency_index >= size) //Wrap
|
||||||
|
frequency_index = 0;
|
||||||
|
else if (frequency_index < 0)
|
||||||
|
frequency_index = size-1;
|
||||||
|
|
||||||
|
if (force_one_step)
|
||||||
|
_index_stepper = 0;
|
||||||
|
|
||||||
|
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_hz_); // Retune
|
||||||
}
|
}
|
||||||
message.freq = frequency_range_.min + frequency_index * def_step_;
|
message.freq = frequency_range_.min + frequency_index * def_step_hz_;
|
||||||
message.range = 0; //Inform freq (for coloring purposes also!)
|
message.range = 0; //Inform freq (for coloring purposes also!)
|
||||||
EventDispatcher::send_message(message);
|
EventDispatcher::send_message(message);
|
||||||
}
|
}
|
||||||
else { //NOT scanning
|
|
||||||
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
|
chThdSleepMilliseconds(SCANNER_SLEEP_MS); //Needed to (eventually) stabilize the receiver into new freq
|
||||||
}
|
}
|
||||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScannerView::bigdisplay_update(int32_t v) {
|
||||||
|
if (v != bigdisplay_current_color) {
|
||||||
|
if (v!=-1)
|
||||||
|
bigdisplay_current_color = v; // -1 means refresh display but keep current color
|
||||||
|
|
||||||
|
switch (bigdisplay_current_color) {
|
||||||
|
case BDC_GREY: big_display.set_style(&style_grey); break;
|
||||||
|
case BDC_YELLOW:big_display.set_style(&style_yellow); break;
|
||||||
|
case BDC_GREEN: big_display.set_style(&style_green); break;
|
||||||
|
case BDC_RED: big_display.set_style(&style_red); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update frequency display
|
||||||
|
bigdisplay_current_frequency = current_frequency;
|
||||||
|
big_display.set(bigdisplay_current_frequency);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// no style change, but update frequency display if it's changed
|
||||||
|
if (current_frequency != bigdisplay_current_frequency) {
|
||||||
|
bigdisplay_current_frequency = current_frequency;
|
||||||
|
big_display.set(bigdisplay_current_frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
||||||
|
|
||||||
current_index = freq_idx; //since it is an ongoing scan, this is a new index
|
current_index = freq_idx; //since it is an ongoing scan, this is a new index
|
||||||
current_frequency = freq;
|
current_frequency = freq;
|
||||||
|
|
||||||
switch (scan_thread->is_freq_lock())
|
if (scan_thread) {
|
||||||
{
|
switch (scan_thread->is_freq_lock())
|
||||||
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
|
{
|
||||||
|
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
|
||||||
|
bigdisplay_update(BDC_GREY);
|
||||||
|
break;
|
||||||
|
case 1: //STARTING LOCK FREQ
|
||||||
|
bigdisplay_update(BDC_YELLOW);
|
||||||
|
break;
|
||||||
|
case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update()
|
||||||
|
bigdisplay_update(BDC_GREEN);
|
||||||
|
break;
|
||||||
|
default: //freq lock is checking the signal, do not update display
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manual_search) {
|
||||||
if (frequency_list.size() > 0) {
|
if (frequency_list.size() > 0) {
|
||||||
text_cycle.set( to_string_dec_uint(freq_idx + 1,3) );
|
text_current_index.set( to_string_dec_uint(freq_idx + 1, 3) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freq_idx < description_list.size() && description_list[freq_idx].size() > 0) {
|
if (freq_idx < description_list.size() && description_list[freq_idx].size() > 1)
|
||||||
desc_cycle.set( description_list[freq_idx] ); //Show new description
|
desc_current_index.set( description_list[freq_idx] ); //Show description from file
|
||||||
}
|
else
|
||||||
break;
|
desc_current_index.set(desc_freq_list_scan); //Show Scan file name (no description in file)
|
||||||
case 1: //STARTING LOCK FREQ
|
|
||||||
big_display.set_style(&style_yellow);
|
|
||||||
break;
|
|
||||||
case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update()
|
|
||||||
big_display.set_style(&style_green);
|
|
||||||
break;
|
|
||||||
default: //freq lock is checking the signal, do not update display
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
big_display.set(freq); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::focus() {
|
void ScannerView::focus() {
|
||||||
@ -214,14 +246,16 @@ ScannerView::~ScannerView() {
|
|||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::show_max() { //show total number of freqs to scan
|
void ScannerView::show_max_index() { //show total number of freqs to scan
|
||||||
|
text_current_index.set("---");
|
||||||
|
|
||||||
if (frequency_list.size() == FREQMAN_MAX_PER_FILE ) {
|
if (frequency_list.size() == FREQMAN_MAX_PER_FILE ) {
|
||||||
text_max.set_style(&style_red);
|
text_max_index.set_style(&style_red);
|
||||||
text_max.set( "/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE ) + " (DB MAX!)");
|
text_max_index.set( "/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE ) + " (DB MAX!)");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
text_max.set_style(&style_grey);
|
text_max_index.set_style(&style_grey);
|
||||||
text_max.set( "/ " + to_string_dec_uint(frequency_list.size()));
|
text_max_index.set( "/ " + to_string_dec_uint(frequency_list.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,18 +271,20 @@ ScannerView::ScannerView(
|
|||||||
&field_volume,
|
&field_volume,
|
||||||
&field_bw,
|
&field_bw,
|
||||||
&field_squelch,
|
&field_squelch,
|
||||||
&field_wait,
|
&field_browse_wait,
|
||||||
|
&field_lock_wait,
|
||||||
&button_load,
|
&button_load,
|
||||||
|
&button_clear,
|
||||||
&rssi,
|
&rssi,
|
||||||
&text_cycle,
|
&text_current_index,
|
||||||
&text_max,
|
&text_max_index,
|
||||||
&desc_cycle,
|
&desc_current_index,
|
||||||
&big_display,
|
&big_display,
|
||||||
&button_manual_start,
|
&button_manual_start,
|
||||||
&button_manual_end,
|
&button_manual_end,
|
||||||
&field_mode,
|
&field_mode,
|
||||||
&step_mode,
|
&field_step,
|
||||||
&button_manual_scan,
|
&button_manual_search,
|
||||||
&button_pause,
|
&button_pause,
|
||||||
&button_dir,
|
&button_dir,
|
||||||
&button_audio_app,
|
&button_audio_app,
|
||||||
@ -258,9 +294,16 @@ ScannerView::ScannerView(
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
def_step = change_mode(AM); //Start on AM
|
//Populate option text for these fields
|
||||||
field_mode.set_by_value(AM); //Reflect the mode into the manual selector
|
freqman_set_modulation_option( field_mode );
|
||||||
|
freqman_set_step_option( field_step );
|
||||||
|
|
||||||
|
// Default starting modulation (these may be overridden in SCANNER.TXT)
|
||||||
|
change_mode(AM_MODULATION); //Default modulation
|
||||||
|
field_mode.set_by_value(AM_MODULATION); //Reflect the mode into the manual selector
|
||||||
|
field_step.set_by_value(9000); //Default step interval (Hz)
|
||||||
|
|
||||||
|
//FUTURE: perhaps additional settings should be stored in persistent memory vs using defaults
|
||||||
//HELPER: Pre-setting a manual range, based on stored frequency
|
//HELPER: Pre-setting a manual range, based on stored frequency
|
||||||
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
|
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
|
||||||
frequency_range.min = stored_freq - 1000000;
|
frequency_range.min = stored_freq - 1000000;
|
||||||
@ -268,8 +311,8 @@ ScannerView::ScannerView(
|
|||||||
frequency_range.max = stored_freq + 1000000;
|
frequency_range.max = stored_freq + 1000000;
|
||||||
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
||||||
|
|
||||||
|
// Button to load txt files from the FREQMAN folder
|
||||||
button_load.on_select = [this, &nav](Button&) {
|
button_load.on_select = [this, &nav](Button&) {
|
||||||
// load txt files from the FREQMAN folder
|
|
||||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||||
|
|
||||||
@ -287,6 +330,23 @@ ScannerView::ScannerView(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Button to clear in-memory frequency list
|
||||||
|
button_clear.on_select = [this, &nav](Button&) {
|
||||||
|
if (scan_thread && frequency_list.size()) {
|
||||||
|
scan_thread->stop(); //STOP SCANNER THREAD
|
||||||
|
frequency_list.clear();
|
||||||
|
description_list.clear();
|
||||||
|
|
||||||
|
show_max_index(); //UPDATE new list size on screen
|
||||||
|
text_current_index.set("");
|
||||||
|
desc_current_index.set(desc_freq_list_scan);
|
||||||
|
scan_thread->set_freq_lock(0); //Reset the scanner lock
|
||||||
|
|
||||||
|
//FUTURE: Consider switching to manual search mode automatically after clear (but would need to validate freq range)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Button to configure starting frequency for a manual range search
|
||||||
button_manual_start.on_select = [this, &nav](Button& button) {
|
button_manual_start.on_select = [this, &nav](Button& button) {
|
||||||
auto new_view = nav_.push<FrequencyKeypadView>(frequency_range.min);
|
auto new_view = nav_.push<FrequencyKeypadView>(frequency_range.min);
|
||||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||||
@ -295,6 +355,7 @@ ScannerView::ScannerView(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Button to configure ending frequency for a manual range search
|
||||||
button_manual_end.on_select = [this, &nav](Button& button) {
|
button_manual_end.on_select = [this, &nav](Button& button) {
|
||||||
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
|
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
|
||||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||||
@ -303,86 +364,128 @@ ScannerView::ScannerView(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
button_pause.on_select = [this](Button&) {
|
// Button to pause/resume scan (note that some other buttons will trigger resume also)
|
||||||
|
button_pause.on_select = [this](ButtonWithEncoder&) {
|
||||||
if ( userpause )
|
if ( userpause )
|
||||||
user_resume();
|
user_resume();
|
||||||
else {
|
else {
|
||||||
scan_pause();
|
scan_pause();
|
||||||
button_pause.set_text("RESUME"); //PAUSED, show resume
|
button_pause.set_text("<RESUME>"); //PAUSED, show resume
|
||||||
userpause=true;
|
userpause=true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Encoder dial causes frequency change when focus is on pause button
|
||||||
|
button_pause.on_change = [this]() {
|
||||||
|
int32_t encoder_delta { (button_pause.get_encoder_delta()>0)? 1 : -1 };
|
||||||
|
|
||||||
|
if (scan_thread)
|
||||||
|
scan_thread->set_index_stepper(encoder_delta);
|
||||||
|
|
||||||
|
//Restart browse timer when frequency changes
|
||||||
|
if (browse_timer != 0)
|
||||||
|
browse_timer = 1;
|
||||||
|
|
||||||
|
button_pause.set_encoder_delta( 0 );
|
||||||
|
};
|
||||||
|
|
||||||
|
// Button to switch to Audio app
|
||||||
button_audio_app.on_select = [this](Button&) {
|
button_audio_app.on_select = [this](Button&) {
|
||||||
scan_thread->stop();
|
if (scan_thread)
|
||||||
|
scan_thread->stop();
|
||||||
nav_.pop();
|
nav_.pop();
|
||||||
nav_.push<AnalogAudioView>();
|
nav_.push<AnalogAudioView>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Button to switch to Mic app
|
||||||
button_mic_app.on_select = [this](Button&) {
|
button_mic_app.on_select = [this](Button&) {
|
||||||
scan_thread->stop();
|
if (scan_thread)
|
||||||
|
scan_thread->stop();
|
||||||
nav_.pop();
|
nav_.pop();
|
||||||
nav_.push<MicTXView>();
|
nav_.push<MicTXView>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Button to delete current frequency from scan Freq List
|
||||||
button_remove.on_select = [this](Button&) {
|
button_remove.on_select = [this](Button&) {
|
||||||
if (frequency_list.size() > current_index) {
|
if (scan_thread && (frequency_list.size() > current_index)) {
|
||||||
if (scan_thread->is_scanning()) //STOP Scanning if necessary
|
scan_thread->set_scanning(false); //PAUSE Scanning if necessary
|
||||||
scan_thread->set_scanning(false);
|
|
||||||
|
// Remove frequency from the Freq List in memory (it is not removed from the file)
|
||||||
scan_thread->set_freq_del(frequency_list[current_index]);
|
scan_thread->set_freq_del(frequency_list[current_index]);
|
||||||
description_list.erase(description_list.begin() + current_index);
|
description_list.erase(description_list.begin() + current_index);
|
||||||
frequency_list.erase(frequency_list.begin() + current_index);
|
frequency_list.erase(frequency_list.begin() + current_index);
|
||||||
show_max(); //UPDATE new list size on screen
|
|
||||||
desc_cycle.set(" "); //Clean up description (cosmetic detail)
|
show_max_index(); //UPDATE new list size on screen
|
||||||
|
desc_current_index.set(""); //Clean up description (cosmetic detail)
|
||||||
scan_thread->set_freq_lock(0); //Reset the scanner lock
|
scan_thread->set_freq_lock(0); //Reset the scanner lock
|
||||||
if ( userpause ) //If user-paused, resume
|
|
||||||
user_resume();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
button_manual_scan.on_select = [this](Button&) {
|
// Button to toggle between Manual Search and Freq List Scan modes
|
||||||
if (!frequency_range.min || !frequency_range.max) {
|
button_manual_search.on_select = [this](Button&) {
|
||||||
nav_.display_modal("Error", "Both START and END freqs\nneed a value");
|
if (!manual_search) {
|
||||||
} else if (frequency_range.min > frequency_range.max) {
|
if (!frequency_range.min || !frequency_range.max) {
|
||||||
nav_.display_modal("Error", "END freq\nis lower than START");
|
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 {
|
||||||
|
manual_search = true; //Switch to Manual Search mode
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
audio::output::stop();
|
manual_search = false; // Switch to List Scan mode
|
||||||
scan_thread->stop(); //STOP SCANNER THREAD
|
|
||||||
frequency_list.clear();
|
|
||||||
description_list.clear();
|
|
||||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
|
||||||
|
|
||||||
show_max();
|
|
||||||
desc_cycle.set("");
|
|
||||||
text_max.set("");
|
|
||||||
text_cycle.set("");
|
|
||||||
|
|
||||||
if ( userpause ) //If user-paused, resume
|
|
||||||
user_resume();
|
|
||||||
big_display.set_style(&style_grey); //Back to grey color
|
|
||||||
start_scan_thread(frequency_range, def_step); //RESTART SCANNER THREAD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audio::output::stop();
|
||||||
|
|
||||||
|
if (scan_thread)
|
||||||
|
scan_thread->stop(); //STOP SCANNER THREAD
|
||||||
|
|
||||||
|
if ( userpause ) //If user-paused, resume
|
||||||
|
user_resume();
|
||||||
|
|
||||||
|
start_scan_thread(); //RESTART SCANNER THREAD in selected mode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Mode field was changed (AM/NFM/WFM)
|
||||||
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||||
receiver_model.disable();
|
receiver_model.disable();
|
||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
change_mode(v);
|
change_mode((freqman_index_t)v);
|
||||||
if ( !scan_thread->is_scanning() ) //for some motive, audio output gets stopped.
|
if ( scan_thread && !scan_thread->is_scanning() ) //for some motive, audio output gets stopped.
|
||||||
audio::output::start(); //So if scan was stopped we resume audio
|
audio::output::start(); //So if scan was stopped we resume audio
|
||||||
receiver_model.enable();
|
receiver_model.enable();
|
||||||
};
|
};
|
||||||
|
|
||||||
button_dir.on_select = [this](Button&) {
|
// Step field was changed (Hz) -- only affects manual Search mode
|
||||||
scan_thread->change_scanning_direction();
|
field_step.on_change = [this](size_t, OptionsField::value_t v) {
|
||||||
if ( userpause ) //If user-paused, resume
|
(void)v; // prevent compiler Unused warning
|
||||||
user_resume();
|
|
||||||
big_display.set_style(&style_grey); //Back to grey color
|
if ( manual_search && scan_thread ) {
|
||||||
|
//Restart scan thread with new step value
|
||||||
|
scan_thread->stop(); //STOP SCANNER THREAD
|
||||||
|
|
||||||
|
//Resuming pause automatically
|
||||||
|
//FUTURE: Consider whether we should stay in Pause mode...
|
||||||
|
if ( userpause ) //If user-paused, resume
|
||||||
|
user_resume();
|
||||||
|
|
||||||
|
start_scan_thread(); //RESTART SCANNER THREAD in Manual Search mode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Button to toggle Forward/Reverse
|
||||||
|
button_dir.on_select = [this](Button&) {
|
||||||
|
fwd = !fwd;
|
||||||
|
if (scan_thread)
|
||||||
|
scan_thread->set_scanning_direction(fwd);
|
||||||
|
if ( userpause ) //If user-paused, resume
|
||||||
|
user_resume();
|
||||||
|
button_dir.set_text(fwd? "REVERSE":"FORWARD");
|
||||||
|
bigdisplay_update(BDC_GREY); //Back to grey color
|
||||||
|
};
|
||||||
|
|
||||||
button_add.on_select = [this](Button&) { // current_frequency
|
// Button to add current frequency (found during Search) to the Scan Frequency List
|
||||||
|
button_add.on_select = [this](Button&) {
|
||||||
File scanner_file;
|
File scanner_file;
|
||||||
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||||
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
|
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
|
||||||
@ -429,22 +532,38 @@ ScannerView::ScannerView(
|
|||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
nav_.display_modal("Error", "Frequency already exists");
|
nav_.display_modal("Error", "Frequency already exists");
|
||||||
big_display.set(current_frequency); //After showing an error
|
bigdisplay_update(-1); //After showing an error
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scanner_file.append(freq_file_path); //Second: append if it is not there
|
scanner_file.append(freq_file_path); //Second: append if it is not there
|
||||||
scanner_file.write_line(frequency_to_add + ",d=ADD FQ");
|
scanner_file.write_line(frequency_to_add);
|
||||||
|
|
||||||
|
// Add to frequency_list in memory too, since we can now switch back from manual mode
|
||||||
|
// Note that we are allowing freqs to be added to file (code above) that exceed the max count we can load into memory
|
||||||
|
if (frequency_list.size() < FREQMAN_MAX_PER_FILE) {
|
||||||
|
frequency_list.push_back(current_frequency);
|
||||||
|
description_list.push_back("");
|
||||||
|
|
||||||
|
show_max_index(); //Display updated frequency list size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
|
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
|
||||||
big_display.set(current_frequency); //After showing an error
|
bigdisplay_update(-1); //After showing an error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//PRE-CONFIGURATION:
|
//PRE-CONFIGURATION:
|
||||||
field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5);
|
field_browse_wait.on_change = [this](int32_t v) { browse_wait = v; };
|
||||||
field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(-10);
|
field_browse_wait.set_value(5);
|
||||||
|
|
||||||
|
field_lock_wait.on_change = [this](int32_t v) { lock_wait = v; };
|
||||||
|
field_lock_wait.set_value(2);
|
||||||
|
|
||||||
|
field_squelch.on_change = [this](int32_t v) { squelch = v; };
|
||||||
|
field_squelch.set_value(-10);
|
||||||
|
|
||||||
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
|
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
|
||||||
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
|
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
|
||||||
|
|
||||||
@ -455,50 +574,69 @@ ScannerView::ScannerView(
|
|||||||
|
|
||||||
void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) {
|
void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) {
|
||||||
|
|
||||||
|
bool found_range { false };
|
||||||
|
bool found_single { false };
|
||||||
|
freqman_index_t def_mod_index { -1 };
|
||||||
|
freqman_index_t def_bw_index { -1 };
|
||||||
|
freqman_index_t def_step_index { -1 };
|
||||||
|
|
||||||
// stop everything running now if required
|
// stop everything running now if required
|
||||||
if (stop_all_before) {
|
if (stop_all_before) {
|
||||||
scan_thread->stop();
|
scan_thread->stop();
|
||||||
frequency_list.clear(); // clear the existing frequency list (expected behavior)
|
frequency_list.clear(); // clear the existing frequency list (expected behavior)
|
||||||
description_list.clear();
|
description_list.clear();
|
||||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( load_freqman_file(file_name, database) ) {
|
if ( load_freqman_file(file_name, database) ) {
|
||||||
loaded_file_name = file_name; // keep loaded filename in memory
|
loaded_file_name = file_name; // keep loaded filename in memory
|
||||||
for(auto& entry : database) { // READ LINE PER LINE
|
for(auto& entry : database) { // READ LINE PER LINE
|
||||||
if (frequency_list.size() < FREQMAN_MAX_PER_FILE ) { //We got space!
|
if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { //We got space!
|
||||||
if (entry.type == RANGE) { //RANGE
|
//
|
||||||
switch (entry.step) {
|
// Get modulation & bw & step from file if specified
|
||||||
case AM_US: def_step = 10000; break ;
|
// Note these values could be different for each line in the file, but we only look at the first one
|
||||||
case AM_EUR:def_step = 9000; break ;
|
//
|
||||||
case NFM_1: def_step = 12500; break ;
|
// Note that freqman requires a very specific string for these parameters,
|
||||||
case NFM_2: def_step = 6250; break ;
|
// so check syntax in frequency file if specified value isn't being loaded
|
||||||
case FM_1: def_step = 100000; break ;
|
//
|
||||||
case FM_2: def_step = 50000; break ;
|
if (def_mod_index == -1)
|
||||||
case N_1: def_step = 25000; break ;
|
def_mod_index = entry.modulation;
|
||||||
case N_2: def_step = 250000; break ;
|
|
||||||
case AIRBAND: def_step= 8330; break ;
|
if (def_bw_index == -1)
|
||||||
case ERROR_STEP:
|
def_bw_index = entry.bandwidth;
|
||||||
case STEP_DEF:
|
|
||||||
default:
|
if (def_step_index == -1)
|
||||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
def_step_index = entry.step;
|
||||||
break ;
|
|
||||||
|
// Get frequency
|
||||||
|
if (entry.type == RANGE) {
|
||||||
|
if (!found_range) {
|
||||||
|
// Set Start & End Search Range instead of populating the small in-memory frequency table
|
||||||
|
// NOTE: There may be multiple single frequencies in file, but only one search range is supported.
|
||||||
|
found_range = true;
|
||||||
|
frequency_range.min = entry.frequency_a;
|
||||||
|
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
|
||||||
|
frequency_range.max = entry.frequency_b;
|
||||||
|
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
||||||
}
|
}
|
||||||
frequency_list.push_back(entry.frequency_a); //Store starting freq and description
|
|
||||||
description_list.push_back("R" + to_string_short_freq(entry.frequency_a)
|
|
||||||
+ ">" + to_string_short_freq(entry.frequency_b)
|
|
||||||
+ " S" + to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
|
|
||||||
);
|
|
||||||
while (frequency_list.size() < FREQMAN_MAX_PER_FILE && entry.frequency_a <= entry.frequency_b) { //add the rest of the range
|
|
||||||
entry.frequency_a+=def_step;
|
|
||||||
frequency_list.push_back(entry.frequency_a);
|
|
||||||
description_list.push_back(""); //Token (keep showing the last description)
|
|
||||||
}
|
|
||||||
} else if ( entry.type == SINGLE) {
|
|
||||||
frequency_list.push_back(entry.frequency_a);
|
|
||||||
description_list.push_back("S: " + entry.description);
|
|
||||||
}
|
}
|
||||||
show_max();
|
else if ( entry.type == SINGLE ) {
|
||||||
|
found_single = true;
|
||||||
|
frequency_list.push_back(entry.frequency_a);
|
||||||
|
description_list.push_back(entry.description);
|
||||||
|
}
|
||||||
|
else if ( entry.type == HAMRADIO ) {
|
||||||
|
// For HAM repeaters, add both receive & transmit frequencies to scan list and modify description
|
||||||
|
// (FUTURE fw versions might handle these differently)
|
||||||
|
found_single = true;
|
||||||
|
frequency_list.push_back(entry.frequency_a);
|
||||||
|
description_list.push_back("R:" + entry.description);
|
||||||
|
|
||||||
|
if ( (entry.frequency_a != entry.frequency_b) &&
|
||||||
|
(frequency_list.size() < FREQMAN_MAX_PER_FILE) ) {
|
||||||
|
frequency_list.push_back(entry.frequency_b);
|
||||||
|
description_list.push_back("T:" + entry.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -509,63 +647,122 @@ void ScannerView::frequency_file_load(std::string file_name, bool stop_all_befor
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
loaded_file_name = "SCANNER"; // back to the default frequency file
|
loaded_file_name = "SCANNER"; // back to the default frequency file
|
||||||
desc_cycle.set(" NO " + file_name + ".TXT FILE ..." );
|
desc_current_index.set(" NO " + file_name + ".TXT FILE ..." );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desc_freq_list_scan = loaded_file_name+".TXT";
|
||||||
|
if (desc_freq_list_scan.length() > 23) { // Truncate description and add ellipses if long file name
|
||||||
|
desc_freq_list_scan.resize(20);
|
||||||
|
desc_freq_list_scan = desc_freq_list_scan + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((def_mod_index != -1) && (def_mod_index != (freqman_index_t)field_mode.selected_index_value()))
|
||||||
|
field_mode.set_by_value( def_mod_index ); //Update mode (also triggers a change callback that disables & reenables RF background)
|
||||||
|
|
||||||
|
if (def_bw_index != -1) //Update BW if specified in file
|
||||||
|
field_bw.set_selected_index( def_bw_index );
|
||||||
|
|
||||||
|
if (def_step_index != -1) //Update step if specified in file
|
||||||
|
field_step.set_selected_index( def_step_index );
|
||||||
|
|
||||||
audio::output::stop();
|
audio::output::stop();
|
||||||
step_mode.set_by_value(def_step); //Impose the default step into the manual step selector
|
|
||||||
|
if ( userpause ) //If user-paused, resume
|
||||||
|
user_resume();
|
||||||
|
|
||||||
|
// Scan list if we found one, otherwise do manual range search
|
||||||
|
manual_search = !found_single;
|
||||||
|
|
||||||
start_scan_thread();
|
start_scan_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScannerView::update_squelch_while_paused(int32_t max_db) {
|
||||||
|
//Update audio & color based on signal level even if paused
|
||||||
|
if (++color_timer > 2) { //Counter to reduce color toggling when weak signal
|
||||||
|
if (max_db > squelch) {
|
||||||
|
audio::output::start(); //Re-enable audio when signal goes above squelch
|
||||||
|
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
|
||||||
|
bigdisplay_update(BDC_GREEN);
|
||||||
|
} else {
|
||||||
|
audio::output::stop(); //Silence audio when signal drops below squelch
|
||||||
|
bigdisplay_update(BDC_GREY); //Back to grey color
|
||||||
|
}
|
||||||
|
|
||||||
|
color_timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
|
void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||||
if ( !userpause ) //Scanning not user-paused
|
|
||||||
|
if (userpause) {
|
||||||
|
update_squelch_while_paused(statistics.max_db);
|
||||||
|
}
|
||||||
|
else if ( scan_thread ) //Scanning not user-paused
|
||||||
{
|
{
|
||||||
if (timer >= (wait * 10) )
|
//Resume regardless of signal strength if browse time reached
|
||||||
{
|
if ((browse_wait != 0) && (browse_timer >= (browse_wait * STATISTICS_UPDATES_PER_SEC)) ) {
|
||||||
timer = 0;
|
browse_timer = 0;
|
||||||
scan_resume();
|
scan_resume(); //Resume scanning
|
||||||
}
|
}
|
||||||
else if (!timer)
|
else
|
||||||
{
|
{
|
||||||
if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch)
|
if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch)
|
||||||
if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached
|
if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //Pause scanning when signal checking time reached
|
||||||
scan_pause();
|
if (!browse_timer) //Don't bother pausing if already paused
|
||||||
timer++;
|
scan_pause();
|
||||||
|
browse_timer++; // browse_timer!=0 is also an indication that we've paused the scan
|
||||||
|
update_squelch_while_paused(statistics.max_db);
|
||||||
} else {
|
} else {
|
||||||
scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal
|
scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal
|
||||||
|
if (browse_timer) //Continue incrementing browse timer while paused
|
||||||
|
browse_timer++;
|
||||||
}
|
}
|
||||||
|
lock_timer = 0; //Keep resetting lock timer while signal remains
|
||||||
} else { //There is NOTHING on the air
|
} else { //There is NOTHING on the air
|
||||||
if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ?
|
if (!browse_timer) {
|
||||||
big_display.set_style(&style_grey); //Back to grey color
|
// Signal lost and scan was never paused
|
||||||
scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no signal
|
if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ?
|
||||||
}
|
bigdisplay_update(BDC_GREY); //Back to grey color
|
||||||
|
scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no sig
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Signal lost and scan is still paused
|
||||||
|
lock_timer++; //Bump paused time
|
||||||
|
if (lock_timer >= (lock_wait * STATISTICS_UPDATES_PER_SEC)) { //Stay on freq until lock_wait time elapses
|
||||||
|
browse_timer = 0;
|
||||||
|
scan_resume();
|
||||||
|
} else {
|
||||||
|
browse_timer++; //Bump browse time too (may hit that limit before lock_timer reached)
|
||||||
|
update_squelch_while_paused(statistics.max_db);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else //Ongoing wait time
|
|
||||||
{
|
|
||||||
timer++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::scan_pause() {
|
void ScannerView::scan_pause() {
|
||||||
if (scan_thread->is_scanning()) {
|
if (scan_thread && scan_thread->is_scanning()) {
|
||||||
scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan
|
scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan
|
||||||
scan_thread->set_scanning(false); // WE STOP SCANNING
|
scan_thread->set_scanning(false); // WE STOP SCANNING
|
||||||
audio::output::start();
|
|
||||||
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
|
|
||||||
}
|
}
|
||||||
|
audio::output::start();
|
||||||
|
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::scan_resume() {
|
void ScannerView::scan_resume() {
|
||||||
audio::output::stop();
|
audio::output::stop();
|
||||||
big_display.set_style(&style_grey); //Back to grey color
|
bigdisplay_update(BDC_GREY); //Back to grey color
|
||||||
if (!scan_thread->is_scanning())
|
|
||||||
scan_thread->set_scanning(true); // RESUME!
|
if (scan_thread) {
|
||||||
|
scan_thread->set_index_stepper(fwd? 1 : -1);
|
||||||
|
scan_thread->set_scanning(true); // RESUME!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::user_resume() {
|
void ScannerView::user_resume() {
|
||||||
timer = wait * 10; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq.
|
browse_timer = browse_wait * STATISTICS_UPDATES_PER_SEC; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq.
|
||||||
button_pause.set_text("PAUSE"); //Show button for pause
|
button_pause.set_text("<PAUSE>"); //Show button for pause, arrows indicate rotary encoder enabled for freq change
|
||||||
userpause=false; //Resume scanning
|
userpause=false; //Resume scanning
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,7 +771,7 @@ void ScannerView::on_headphone_volume_changed(int32_t v) {
|
|||||||
receiver_model.set_headphone_volume(new_volume);
|
receiver_model.set_headphone_volume(new_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread()
|
void ScannerView::change_mode(freqman_index_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread()
|
||||||
using option_t = std::pair<std::string, int32_t>;
|
using option_t = std::pair<std::string, int32_t>;
|
||||||
using options_t = std::vector<option_t>;
|
using options_t = std::vector<option_t>;
|
||||||
options_t bw;
|
options_t bw;
|
||||||
@ -583,39 +780,26 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
|||||||
};
|
};
|
||||||
|
|
||||||
switch (new_mod) {
|
switch (new_mod) {
|
||||||
case NFM: //bw 16k (2) default
|
case AM_MODULATION:
|
||||||
bw.emplace_back("8k5 ", 0);
|
freqman_set_bandwidth_option( new_mod , field_bw );
|
||||||
bw.emplace_back("11k ", 0);
|
|
||||||
bw.emplace_back("16k ", 0);
|
|
||||||
field_bw.set_options(bw);
|
|
||||||
|
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
|
||||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
|
||||||
field_bw.set_selected_index(2);
|
|
||||||
receiver_model.set_nbfm_configuration(field_bw.selected_index());
|
|
||||||
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); };
|
|
||||||
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
|
||||||
break;
|
|
||||||
case AM:
|
|
||||||
bw.emplace_back("DSB 9k" , 0 );
|
|
||||||
bw.emplace_back("DSB 6k" , 1 );
|
|
||||||
bw.emplace_back("USB+3k" , 2 );
|
|
||||||
bw.emplace_back("LSB-3k" , 3 );
|
|
||||||
bw.emplace_back("CW " , 4 );
|
|
||||||
|
|
||||||
field_bw.set_options(bw);
|
|
||||||
|
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||||
field_bw.set_selected_index(0);
|
field_bw.set_selected_index(0);
|
||||||
receiver_model.set_am_configuration(field_bw.selected_index());
|
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); };
|
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);
|
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
||||||
|
break;
|
||||||
|
case NFM_MODULATION: //bw 16k (2) default
|
||||||
|
freqman_set_bandwidth_option( new_mod , field_bw );
|
||||||
|
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||||
|
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||||
|
field_bw.set_selected_index(2);
|
||||||
|
receiver_model.set_nbfm_configuration(field_bw.selected_index());
|
||||||
|
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); };
|
||||||
|
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
||||||
break;
|
break;
|
||||||
case WFM:
|
case WFM_MODULATION:
|
||||||
bw.emplace_back("200k ", 0);
|
freqman_set_bandwidth_option( new_mod , field_bw );
|
||||||
field_bw.set_options(bw);
|
|
||||||
|
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||||
field_bw.set_selected_index(0);
|
field_bw.set_selected_index(0);
|
||||||
@ -623,21 +807,29 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
|||||||
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); };
|
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); };
|
||||||
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000);
|
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod_step[new_mod];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScannerView::start_scan_thread() {
|
void ScannerView::start_scan_thread() {
|
||||||
receiver_model.enable();
|
receiver_model.enable();
|
||||||
receiver_model.set_squelch_level(0);
|
receiver_model.set_squelch_level(0);
|
||||||
scan_thread = std::make_unique<ScannerThread>(frequency_list);
|
show_max_index();
|
||||||
}
|
|
||||||
|
|
||||||
void ScannerView::start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step) {
|
//Start Scanner Thread
|
||||||
receiver_model.enable();
|
//FUTURE: Consider passing additional control flags (fwd,userpause,etc) to scanner thread at start (perhaps in a data structure)
|
||||||
receiver_model.set_squelch_level(0);
|
if (manual_search) {
|
||||||
scan_thread = std::make_unique<ScannerThread>(frequency_range, def_step);
|
button_manual_search.set_text("SCAN"); //Update meaning of Manual Scan button
|
||||||
|
desc_current_index.set(desc_freq_range_search);
|
||||||
|
scan_thread = std::make_unique<ScannerThread>(frequency_range, field_step.selected_index_value() );
|
||||||
|
} else {
|
||||||
|
button_manual_search.set_text("SRCH"); //Update meaning of Manual Scan button
|
||||||
|
desc_current_index.set(desc_freq_list_scan);
|
||||||
|
scan_thread = std::make_unique<ScannerThread>(frequency_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_thread->set_scanning_direction(fwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -33,20 +33,16 @@
|
|||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
#include "file.hpp"
|
#include "file.hpp"
|
||||||
|
|
||||||
|
#define SCANNER_SLEEP_MS 50 //ms that Scanner Thread sleeps per loop
|
||||||
#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
|
#define STATISTICS_UPDATES_PER_SEC 10
|
||||||
|
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
enum modulation_type { AM = 0,WFM,NFM };
|
|
||||||
|
|
||||||
string const mod_name[3] = {"AM", "WFM", "NFM"};
|
|
||||||
size_t const mod_step[3] = {9000, 100000, 12500 };
|
|
||||||
|
|
||||||
class ScannerThread {
|
class ScannerThread {
|
||||||
public:
|
public:
|
||||||
ScannerThread(std::vector<rf::Frequency> frequency_list);
|
ScannerThread(std::vector<rf::Frequency> frequency_list);
|
||||||
ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz);
|
||||||
~ScannerThread();
|
~ScannerThread();
|
||||||
|
|
||||||
void set_scanning(const bool v);
|
void set_scanning(const bool v);
|
||||||
@ -55,9 +51,9 @@ public:
|
|||||||
void set_freq_lock(const uint32_t v);
|
void set_freq_lock(const uint32_t v);
|
||||||
uint32_t is_freq_lock();
|
uint32_t is_freq_lock();
|
||||||
|
|
||||||
void set_freq_del(const uint32_t v);
|
void set_freq_del(const rf::Frequency v);
|
||||||
|
void set_index_stepper(const int32_t v);
|
||||||
void change_scanning_direction();
|
void set_scanning_direction(bool fwd);
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
@ -69,13 +65,16 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::vector<rf::Frequency> frequency_list_ { };
|
std::vector<rf::Frequency> frequency_list_ { };
|
||||||
jammer::jammer_range_t frequency_range_ {false, 0, 0};
|
jammer::jammer_range_t frequency_range_ {false, 0, 0};
|
||||||
size_t def_step_ { 0 };
|
size_t def_step_hz_ { 0 };
|
||||||
Thread* thread { nullptr };
|
Thread* thread { nullptr };
|
||||||
|
|
||||||
bool _scanning { true };
|
bool _scanning { true };
|
||||||
bool _fwd { true };
|
bool _manual_search { false };
|
||||||
uint32_t _freq_lock { 0 };
|
uint32_t _freq_lock { 0 };
|
||||||
uint32_t _freq_del { 0 };
|
rf::Frequency _freq_del { 0 };
|
||||||
|
uint32_t _freq_idx { 0 };
|
||||||
|
int32_t _stepper { 1 };
|
||||||
|
int32_t _index_stepper { 0 };
|
||||||
static msg_t static_fn(void* arg);
|
static msg_t static_fn(void* arg);
|
||||||
void run();
|
void run();
|
||||||
void create_thread();
|
void create_thread();
|
||||||
@ -122,33 +121,50 @@ private:
|
|||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
|
|
||||||
void start_scan_thread();
|
void start_scan_thread();
|
||||||
void start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
void change_mode(freqman_index_t mod_type);
|
||||||
size_t change_mode(uint8_t mod_type);
|
void show_max_index();
|
||||||
void show_max();
|
|
||||||
void scan_pause();
|
void scan_pause();
|
||||||
void scan_resume();
|
void scan_resume();
|
||||||
void user_resume();
|
void user_resume();
|
||||||
void frequency_file_load(std::string file_name, bool stop_all_before = false);
|
void frequency_file_load(std::string file_name, bool stop_all_before = false);
|
||||||
|
void bigdisplay_update(int32_t);
|
||||||
|
void update_squelch_while_paused(int32_t max_db);
|
||||||
void on_statistics_update(const ChannelStatistics& statistics);
|
void on_statistics_update(const ChannelStatistics& statistics);
|
||||||
void on_headphone_volume_changed(int32_t v);
|
void on_headphone_volume_changed(int32_t v);
|
||||||
void handle_retune(int64_t freq, uint32_t freq_idx);
|
void handle_retune(int64_t freq, uint32_t freq_idx);
|
||||||
|
|
||||||
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
|
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
|
||||||
int32_t squelch { 0 };
|
int32_t squelch { 0 };
|
||||||
uint32_t timer { 0 };
|
uint32_t browse_timer { 0 };
|
||||||
uint32_t wait { 0 };
|
uint32_t lock_timer { 0 };
|
||||||
size_t def_step { 0 };
|
uint32_t color_timer { 0 };
|
||||||
|
int32_t bigdisplay_current_color { -2 };
|
||||||
|
rf::Frequency bigdisplay_current_frequency { 0 };
|
||||||
|
uint32_t browse_wait { 0 };
|
||||||
|
uint32_t lock_wait { 0 };
|
||||||
freqman_db database { };
|
freqman_db database { };
|
||||||
std::string loaded_file_name;
|
std::string loaded_file_name;
|
||||||
uint32_t current_index { 0 };
|
uint32_t current_index { 0 };
|
||||||
rf::Frequency current_frequency { 0 };
|
rf::Frequency current_frequency { 0 };
|
||||||
bool userpause { false };
|
bool userpause { false };
|
||||||
|
bool manual_search { false };
|
||||||
|
bool fwd { true }; //to preserve direction setting even if scan_thread restarted
|
||||||
|
|
||||||
|
enum bigdisplay_color_type {
|
||||||
|
BDC_GREY,
|
||||||
|
BDC_YELLOW,
|
||||||
|
BDC_GREEN,
|
||||||
|
BDC_RED
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string desc_freq_range_search = "SEARCHING...";
|
||||||
|
std::string desc_freq_list_scan = "";
|
||||||
|
|
||||||
Labels labels {
|
Labels labels {
|
||||||
{ { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() },
|
{ { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() },
|
||||||
{ { 0 * 8, 1* 16 }, "BW: SQLCH: db WAIT:", Color::light_grey() },
|
{ { 0 * 8, 1 * 16 }, "BW: SQ: Wsa: Wsl:", Color::light_grey() },
|
||||||
{ { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() },
|
{ { 0 * 8, 10 * 16 }, "SRCH START SEARCH END SWITCH", Color::light_grey() },
|
||||||
|
|
||||||
{ { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() },
|
{ { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() },
|
||||||
{ { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() },
|
{ { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() },
|
||||||
};
|
};
|
||||||
@ -175,20 +191,28 @@ private:
|
|||||||
|
|
||||||
OptionsField field_bw {
|
OptionsField field_bw {
|
||||||
{ 3 * 8, 1 * 16 },
|
{ 3 * 8, 1 * 16 },
|
||||||
4,
|
6,
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
NumberField field_squelch {
|
NumberField field_squelch {
|
||||||
{ 18 * 8, 1 * 16 },
|
{ 13 * 8, 1 * 16 },
|
||||||
3,
|
3,
|
||||||
{ -90, 20 },
|
{ -90, 20 },
|
||||||
1,
|
1,
|
||||||
' ',
|
' ',
|
||||||
};
|
};
|
||||||
|
|
||||||
NumberField field_wait {
|
NumberField field_browse_wait { //Signal-Active wait timer - time to wait before moving on even when signal locked
|
||||||
{ 27 * 8, 1 * 16 },
|
{ 21 * 8, 1 * 16 },
|
||||||
|
2,
|
||||||
|
{ 0, 99 },
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_lock_wait { //Signal-Lost wait timer - time to wait before moving on after losing signal lock
|
||||||
|
{ 28 * 8, 1 * 16 },
|
||||||
2,
|
2,
|
||||||
{ 0, 99 },
|
{ 0, 99 },
|
||||||
1,
|
1,
|
||||||
@ -199,16 +223,16 @@ private:
|
|||||||
{ 0 * 16, 2 * 16, 15 * 16, 8 },
|
{ 0 * 16, 2 * 16, 15 * 16, 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
Text text_cycle {
|
Text text_current_index {
|
||||||
{ 0, 3 * 16, 3 * 8, 16 },
|
{ 0, 3 * 16, 3 * 8, 16 },
|
||||||
};
|
};
|
||||||
|
|
||||||
Text text_max {
|
Text text_max_index {
|
||||||
{ 4 * 8, 3 * 16, 18 * 8, 16 },
|
{ 4 * 8, 3 * 16, 18 * 8, 16 },
|
||||||
};
|
};
|
||||||
|
|
||||||
Text desc_cycle {
|
Text desc_current_index {
|
||||||
{0, 4 * 16, 240, 16 },
|
{0, 4 * 16, 240 - 6 * 8, 16 },
|
||||||
};
|
};
|
||||||
|
|
||||||
BigFrequency big_display { //Show frequency in glamour
|
BigFrequency big_display { //Show frequency in glamour
|
||||||
@ -226,46 +250,31 @@ private:
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_manual_scan {
|
Button button_manual_search {
|
||||||
{ 24 * 8, 11 * 16, 6 * 8, 28 },
|
{ 24 * 8, 11 * 16, 6 * 8, 28 },
|
||||||
"SCAN"
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
OptionsField field_mode {
|
OptionsField field_mode {
|
||||||
{ 5 * 8, (26 * 8) + 4 },
|
{ 5 * 8, (26 * 8) + 4 },
|
||||||
6,
|
6,
|
||||||
{
|
{ } //Text strings get filled by freqman_set_modulation_option()
|
||||||
{ " AM ", 0 },
|
|
||||||
{ " WFM ", 1 },
|
|
||||||
{ " NFM ", 2 },
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OptionsField step_mode {
|
OptionsField field_step {
|
||||||
{ 17 * 8, (26 * 8) + 4 },
|
{ 17 * 8, (26 * 8) + 4 },
|
||||||
12,
|
12,
|
||||||
{
|
{ } //Text strings get filled by freqman_set_step_option()
|
||||||
{ "5kHz (SA AM)", 5000 },
|
|
||||||
{ "9kHz (EU AM)", 9000 },
|
|
||||||
{ "10kHz(US AM)", 10000 },
|
|
||||||
{ "50kHz (FM1)", 50000 },
|
|
||||||
{ "100kHz(FM2)", 100000 },
|
|
||||||
{ "6.25kHz(NFM)", 6250 },
|
|
||||||
{ "12.5kHz(NFM)", 12500 },
|
|
||||||
{ "25kHz (N1)", 25000 },
|
|
||||||
{ "250kHz (N2)", 250000 },
|
|
||||||
{ "8.33kHz(AIR)", 8330 }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_pause {
|
ButtonWithEncoder button_pause {
|
||||||
{ 0, (15 * 16) - 4, 72, 28 },
|
{ 0, (15 * 16) - 4, 72, 28 },
|
||||||
"PAUSE"
|
"<PAUSE>"
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_dir {
|
Button button_dir {
|
||||||
{ 0, (35 * 8) - 4, 72, 28 },
|
{ 0, (35 * 8) - 4, 72, 28 },
|
||||||
"FW><RV"
|
"REVERSE"
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_audio_app {
|
Button button_audio_app {
|
||||||
@ -284,8 +293,13 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Button button_load {
|
Button button_load {
|
||||||
{ 24 * 8, 3 * 16 - 8, 6 * 8, 22 },
|
{ 24 * 8, 3 * 16 - 10, 6 * 8, 22 },
|
||||||
"Load"
|
"LOAD"
|
||||||
|
};
|
||||||
|
|
||||||
|
Button button_clear {
|
||||||
|
{ 24 * 8, 4 * 16, 6 * 8, 22 },
|
||||||
|
"MCLR"
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_remove {
|
Button button_remove {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user