Merge pull request #105 from euquiq/new-ui-scanner-version

Update ui_scanner.hpp
This commit is contained in:
Erwin Ried 2020-08-03 00:36:28 +02:00 committed by GitHub
commit 00a8d22c5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 262 additions and 75 deletions

View File

@ -22,11 +22,6 @@
#include "ui_scanner.hpp" #include "ui_scanner.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "audio.hpp"
using namespace portapack; using namespace portapack;
namespace ui { namespace ui {
@ -58,12 +53,22 @@ bool ScannerThread::is_scanning() {
return _scanning; return _scanning;
} }
void ScannerThread::set_userpause(const bool v) { void ScannerThread::set_freq_lock(const uint32_t v) {
_userpause = v; _freq_lock = v;
} }
bool ScannerThread::is_userpause() { uint32_t ScannerThread::is_freq_lock() {
return _userpause; return _freq_lock;
}
void ScannerThread::set_freq_del(const uint32_t v) {
_freq_del = v;
}
void ScannerThread::change_scanning_direction() {
_fwd = !_fwd;
chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction
} }
msg_t ScannerThread::static_fn(void* arg) { msg_t ScannerThread::static_fn(void* arg) {
@ -73,29 +78,74 @@ msg_t ScannerThread::static_fn(void* arg) {
} }
void ScannerThread::run() { void ScannerThread::run() {
if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ...
RetuneMessage message { }; RetuneMessage message { };
uint32_t frequency_index = 0; uint32_t frequency_index = frequency_list_.size();
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
while( !chThdShouldTerminate() ) { while( !chThdShouldTerminate() ) {
if (_scanning) { if (_scanning) { //Scanning
// Retune if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); if (!restart_scan) { //looping at full speed
message.range = frequency_index; if (_fwd) { //forward
EventDispatcher::send_message(message); frequency_index++;
if (frequency_index >= frequency_list_.size())
frequency_index = 0;
frequency_index++; } else { //reverse
if (frequency_index >= frequency_list_.size()) if (frequency_index < 1)
frequency_index = 0; frequency_index = frequency_list_.size();
frequency_index--;
}
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune
}
else
restart_scan=false; //Effectively skipping first retuning, giving system time
}
message.range = frequency_index; //Inform freq (for coloring purposes also!)
EventDispatcher::send_message(message);
} }
chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? else { //NOT scanning
if (_freq_del != 0) { //There is a frequency to delete
for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
if (frequency_list_[i] == _freq_del)
{ //found: Erase it
frequency_list_.erase(frequency_list_.begin() + i);
if (i==0) //set scan index one place back to compensate
i=frequency_list_.size();
else
i--;
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
} }
} }
} }
void ScannerView::handle_retune(uint32_t i) { void ScannerView::handle_retune(uint32_t i) {
big_display.set(frequency_list[i]); //Show the big Freq switch (scan_thread->is_freq_lock())
text_cycle.set( to_string_dec_uint(i + 1,3) ); {
if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
text_cycle.set( to_string_dec_uint(i + 1,3) );
current_index = i; //since it is an ongoing scan, this is a new index
if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description
break;
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(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
} }
void ScannerView::focus() { void ScannerView::focus() {
@ -109,10 +159,14 @@ ScannerView::~ScannerView() {
} }
void ScannerView::show_max() { //show total number of freqs to scan void ScannerView::show_max() { //show total number of freqs to scan
if (frequency_list.size() == MAX_DB_ENTRY) if (frequency_list.size() == MAX_DB_ENTRY) {
text_max.set_style(&style_red);
text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)");
else }
else {
text_max.set_style(&style_grey);
text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); text_max.set( "/ " + to_string_dec_uint(frequency_list.size()));
}
} }
ScannerView::ScannerView( ScannerView::ScannerView(
@ -139,13 +193,23 @@ ScannerView::ScannerView(
&step_mode, &step_mode,
&button_manual_scan, &button_manual_scan,
&button_pause, &button_pause,
&button_audio_app &button_dir,
&button_audio_app,
&button_mic_app,
&button_add,
&button_remove
}); });
def_step = change_mode(AM); //Start on AM def_step = change_mode(AM); //Start on AM
field_mode.set_by_value(AM); //Reflect the mode into the manual selector field_mode.set_by_value(AM); //Reflect the mode into the manual selector
big_display.set_style(&style_green); //Start with green color //HELPER: Pre-setting a manual range, based on stored frequency
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
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));
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);
@ -164,39 +228,56 @@ ScannerView::ScannerView(
}; };
button_pause.on_select = [this](Button&) { button_pause.on_select = [this](Button&) {
if (scan_thread->is_userpause()) { if ( userpause )
timer = wait * 10; //Unlock timer pause on_statistics_update user_resume();
button_pause.set_text("PAUSE"); //resume scanning (show button for pause) else {
scan_thread->set_userpause(false);
//scan_resume();
} else {
scan_pause(); scan_pause();
scan_thread->set_userpause(true); button_pause.set_text("RESUME"); //PAUSED, show resume
button_pause.set_text("RESUME"); //PAUSED, show resume userpause=true;
} }
}; };
button_audio_app.on_select = [this](Button&) { button_audio_app.on_select = [this](Button&) {
if (scan_thread->is_scanning())
scan_thread->set_scanning(false);
scan_thread->stop(); scan_thread->stop();
nav_.pop(); nav_.pop();
nav_.push<AnalogAudioView>(); nav_.push<AnalogAudioView>();
}; };
button_mic_app.on_select = [this](Button&) {
scan_thread->stop();
nav_.pop();
nav_.push<MicTXView>();
};
button_remove.on_select = [this](Button&) {
if (frequency_list.size() > current_index) {
if (scan_thread->is_scanning()) //STOP Scanning if necessary
scan_thread->set_scanning(false);
scan_thread->set_freq_del(frequency_list[current_index]);
description_list.erase(description_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)
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_manual_scan.on_select = [this](Button&) {
if (!frequency_range.min || !frequency_range.max) { if (!frequency_range.min || !frequency_range.max) {
nav_.display_modal("Error", "Both START and END freqs\nneed a value"); nav_.display_modal("Error", "Both START and END freqs\nneed a value");
} else if (frequency_range.min > frequency_range.max) { } else if (frequency_range.min > frequency_range.max) {
nav_.display_modal("Error", "END freq\nis lower than START"); nav_.display_modal("Error", "END freq\nis lower than START");
} else { } else {
audio::output::stop();
scan_thread->stop(); //STOP SCANNER THREAD scan_thread->stop(); //STOP SCANNER THREAD
frequency_list.clear(); frequency_list.clear();
description_list.clear(); description_list.clear();
def_step = step_mode.selected_index_value(); //Use def_step from manual selector def_step = step_mode.selected_index_value(); //Use def_step from manual selector
description_list.push_back( description_list.push_back(
"M:" + to_string_short_freq(frequency_range.min) + ">" "M:" + to_string_short_freq(frequency_range.min) + " >"
+ to_string_short_freq(frequency_range.max) + " S:" + to_string_short_freq(frequency_range.max) + " S:"
+ to_string_short_freq(def_step) + to_string_short_freq(def_step)
); );
@ -207,27 +288,73 @@ ScannerView::ScannerView(
description_list.push_back(""); //If empty, will keep showing the last description description_list.push_back(""); //If empty, will keep showing the last description
frequency+=def_step; frequency+=def_step;
} }
show_max(); show_max();
if ( userpause ) //If user-paused, resume
user_resume();
big_display.set_style(&style_grey); //Back to grey color
start_scan_thread(); //RESTART SCANNER THREAD start_scan_thread(); //RESTART SCANNER THREAD
} }
}; };
field_mode.on_change = [this](size_t, OptionsField::value_t v) { field_mode.on_change = [this](size_t, OptionsField::value_t v) {
if (scan_thread->is_scanning())
scan_thread->set_scanning(false); // WE STOP SCANNING
audio::output::stop();
scan_thread->stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
chThdSleepMilliseconds(50);
change_mode(v); change_mode(v);
start_scan_thread(); if ( !scan_thread->is_scanning() ) //for some motive, audio output gets stopped.
audio::output::start(); //So if scan was stopped we resume audio
receiver_model.enable();
};
button_dir.on_select = [this](Button&) {
scan_thread->change_scanning_direction();
if ( userpause ) //If user-paused, resume
user_resume();
big_display.set_style(&style_grey); //Back to grey color
};
button_add.on_select = [this](Button&) { //frequency_list[current_index]
File scanner_file;
auto result = scanner_file.open("FREQMAN/SCANNER.TXT"); //First search if freq is already in txt
if (!result.is_valid()) {
std::string frequency_to_add = "f="
+ to_string_dec_uint(frequency_list[current_index] / 1000)
+ to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0');
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 < scanner_file.size();pointer++) {
scanner_file.seek(pointer);
scanner_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) {
nav_.display_modal("Error", "Frequency already exists");
big_display.set(frequency_list[current_index]); //After showing an error
}
else {
auto result = scanner_file.append("FREQMAN/SCANNER.TXT"); //Second: append if it is not there
scanner_file.write_line(frequency_to_add + ",d=ADD FQ");
}
} else
{
nav_.display_modal("Error", "Cannot open SCANNER.TXT\nfor appending freq.");
big_display.set(frequency_list[current_index]); //After showing an error
}
}; };
//PRE-CONFIGURATION: //PRE-CONFIGURATION:
field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5);
field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); 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); };
// LEARN FREQUENCIES // LEARN FREQUENCIES
@ -249,7 +376,7 @@ ScannerView::ScannerView(
} }
frequency_list.push_back(entry.frequency_a); //Store starting freq and description frequency_list.push_back(entry.frequency_a); //Store starting freq and description
description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) description_list.push_back("R:" + to_string_short_freq(entry.frequency_a)
+ ">" + to_string_short_freq(entry.frequency_b) + " >" + to_string_short_freq(entry.frequency_b)
+ " S:" + to_string_short_freq(def_step)); + " S:" + to_string_short_freq(def_step));
while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range
entry.frequency_a+=def_step; entry.frequency_a+=def_step;
@ -278,21 +405,30 @@ ScannerView::ScannerView(
} }
void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
if (!scan_thread->is_userpause()) if ( !userpause ) //Scanning not user-paused
{ {
if (timer >= (wait * 10) ) if (timer >= (wait * 10) )
{ {
timer=0; timer = 0;
scan_resume(); scan_resume();
} }
else if (!timer) else if (!timer)
{ {
if (statistics.max_db > -squelch) { //There is something on the air... if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch)
scan_pause(); if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached
timer++; scan_pause();
timer++;
} else {
scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal
}
} else { //There is NOTHING on the air
if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ?
big_display.set_style(&style_grey); //Back to grey color
scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no signal
}
} }
} }
else else //Ongoing wait time
{ {
timer++; timer++;
} }
@ -301,16 +437,23 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
void ScannerView::scan_pause() { void ScannerView::scan_pause() {
if (scan_thread->is_scanning()) { if (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_scanning(false); // WE STOP SCANNING scan_thread->set_scanning(false); // WE STOP SCANNING
audio::output::start(); audio::output::start();
} }
} }
void ScannerView::scan_resume() { void ScannerView::scan_resume() {
if (!scan_thread->is_scanning()) { audio::output::stop();
audio::output::stop(); big_display.set_style(&style_grey); //Back to grey color
scan_thread->set_scanning(true); // WE RESCAN if (!scan_thread->is_scanning())
} scan_thread->set_scanning(true); // RESUME!
}
void ScannerView::user_resume() {
timer = wait * 10; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq.
button_pause.set_text("PAUSE"); //Show button for pause
userpause=false; //Resume scanning
} }
void ScannerView::on_headphone_volume_changed(int32_t v) { void ScannerView::on_headphone_volume_changed(int32_t v) {
@ -365,7 +508,6 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
} }
return mod_step[new_mod]; return mod_step[new_mod];
} }
void ScannerView::start_scan_thread() { void ScannerView::start_scan_thread() {

View File

@ -22,16 +22,20 @@
#include "ui.hpp" #include "ui.hpp"
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_font_fixed_8x16.hpp" #include "ui_font_fixed_8x16.hpp"
#include "ui_spectrum.hpp"
#include "freqman.hpp" #include "freqman.hpp"
#include "log_file.hpp"
#include "analog_audio_app.hpp" #include "analog_audio_app.hpp"
#include "audio.hpp"
#include "ui_mictx.hpp"
#include "portapack_persistent_memory.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "file.hpp"
#define MAX_DB_ENTRY 500 #define MAX_DB_ENTRY 500
#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
namespace ui { namespace ui {
@ -48,8 +52,12 @@ public:
void set_scanning(const bool v); void set_scanning(const bool v);
bool is_scanning(); bool is_scanning();
void set_userpause(const bool v); void set_freq_lock(const uint32_t v);
bool is_userpause(); uint32_t is_freq_lock();
void set_freq_del(const uint32_t v);
void change_scanning_direction();
void stop(); void stop();
@ -63,7 +71,9 @@ private:
Thread* thread { nullptr }; Thread* thread { nullptr };
bool _scanning { true }; bool _scanning { true };
bool _userpause { false }; bool _fwd { true };
uint32_t _freq_lock { 0 };
uint32_t _freq_del { 0 };
static msg_t static_fn(void* arg); static msg_t static_fn(void* arg);
void run(); void run();
}; };
@ -83,12 +93,24 @@ public:
.foreground = Color::grey(), .foreground = Color::grey(),
}; };
const Style style_yellow { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::dark_yellow(),
};
const Style style_green { //Found signal const Style style_green { //Found signal
.font = font::fixed_8x16, .font = font::fixed_8x16,
.background = Color::black(), .background = Color::black(),
.foreground = Color::green(), .foreground = Color::green(),
}; };
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
std::string title() const override { return "SCANNER"; }; std::string title() const override { return "SCANNER"; };
std::vector<rf::Frequency> frequency_list{ }; std::vector<rf::Frequency> frequency_list{ };
std::vector<string> description_list { }; std::vector<string> description_list { };
@ -103,6 +125,7 @@ private:
void show_max(); void show_max();
void scan_pause(); void scan_pause();
void scan_resume(); void scan_resume();
void user_resume();
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);
@ -114,13 +137,15 @@ private:
uint32_t wait { 0 }; uint32_t wait { 0 };
size_t def_step { 0 }; size_t def_step { 0 };
freqman_db database { }; freqman_db database { };
uint32_t current_index { 0 };
bool userpause { false };
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: SQUELCH: /99 WAIT:", Color::light_grey() }, { { 0 * 8, 1* 16 }, "BW: SQUELCH: db WAIT:", Color::light_grey() },
{ { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() },
{ { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() },
{ { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() },
}; };
LNAGainField field_lna { LNAGainField field_lna {
@ -151,8 +176,8 @@ private:
NumberField field_squelch { NumberField field_squelch {
{ 15 * 8, 1 * 16 }, { 15 * 8, 1 * 16 },
2, 3,
{ 0, 99 }, { -90, 20 },
1, 1,
' ', ' ',
}; };
@ -202,7 +227,7 @@ private:
}; };
OptionsField field_mode { OptionsField field_mode {
{ 5 * 8, 14 * 16 }, { 5 * 8, (26 * 8) + 4 },
6, 6,
{ {
{ " AM ", 0 }, { " AM ", 0 },
@ -212,7 +237,7 @@ private:
}; };
OptionsField step_mode { OptionsField step_mode {
{ 17 * 8, 14 * 16 }, { 17 * 8, (26 * 8) + 4 },
12, 12,
{ {
{ "5Khz (SA AM)", 5000 }, { "5Khz (SA AM)", 5000 },
@ -229,13 +254,33 @@ private:
}; };
Button button_pause { Button button_pause {
{ 12, 17 * 16, 96, 24 }, { 0, (15 * 16) - 4, 72, 28 },
"PAUSE" "PAUSE"
}; };
Button button_dir {
{ 0, (35 * 8) - 4, 72, 28 },
"FW><RV"
};
Button button_audio_app { Button button_audio_app {
{ 124, 17 * 16, 96, 24 }, { 84, (15 * 16) - 4, 72, 28 },
"AUDIO APP" "AUDIO"
};
Button button_mic_app {
{ 84, (35 * 8) - 4, 72, 28 },
"MIC TX"
};
Button button_add {
{ 168, (15 * 16) - 4, 72, 28 },
"ADD FQ"
};
Button button_remove {
{ 168, (35 * 8) - 4, 72, 28 },
"DEL FQ"
}; };
std::unique_ptr<ScannerThread> scan_thread { }; std::unique_ptr<ScannerThread> scan_thread { };