Scanner cleanup (#1320)

* FreqmanDB in Scanner

* Format

* Add comment on squelch(0)
This commit is contained in:
Kyle Reed 2023-07-29 15:36:00 -07:00 committed by GitHub
parent 5ca74db2f9
commit 0a3aa706ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 212 additions and 238 deletions

View File

@ -487,7 +487,7 @@ void FrequencyEditView::populate_bandwidth_options() {
if (entry_.modulation < std::size(freqman_bandwidths)) { if (entry_.modulation < std::size(freqman_bandwidths)) {
auto& bandwidths = freqman_bandwidths[entry_.modulation]; auto& bandwidths = freqman_bandwidths[entry_.modulation];
for (auto i = 1u; i < bandwidths.size(); ++i) { for (auto i = 0u; i < bandwidths.size(); ++i) {
auto& item = bandwidths[i]; auto& item = bandwidths[i];
options.push_back({item.first, (OptionsField::value_t)i}); options.push_back({item.first, (OptionsField::value_t)i});
} }
@ -500,7 +500,7 @@ void FrequencyEditView::populate_step_options() {
OptionsField::options_t options; OptionsField::options_t options;
options.push_back({"None", -1}); options.push_back({"None", -1});
for (auto i = 1u; i < freqman_steps.size(); ++i) { for (auto i = 0u; i < freqman_steps.size(); ++i) {
auto& item = freqman_steps[i]; auto& item = freqman_steps[i];
options.push_back({item.first, (OptionsField::value_t)i}); options.push_back({item.first, (OptionsField::value_t)i});
} }
@ -513,7 +513,7 @@ void FrequencyEditView::populate_tone_options() {
OptionsField::options_t options; OptionsField::options_t options;
options.push_back({"None", -1}); options.push_back({"None", -1});
for (auto i = 1u; i < tone_keys.size(); ++i) { for (auto i = 0u; i < tone_keys.size(); ++i) {
auto& item = tone_keys[i]; auto& item = tone_keys[i];
options.push_back({fx100_string(item.second), (OptionsField::value_t)i}); options.push_back({fx100_string(item.second), (OptionsField::value_t)i});
} }

View File

@ -219,12 +219,15 @@ class FrequencyEditView : public View {
{{0 * 8, 10 * 16}, "Description:", Color::light_grey()}, {{0 * 8, 10 * 16}, "Description:", Color::light_grey()},
}; };
OptionsField field_type{{13 * 8, 3 * 16}, 8, { OptionsField field_type{
{"Single", 0}, {13 * 8, 3 * 16},
{"Range", 1}, 8,
{"HamRadio", 2}, {
{"Raw", 3}, {"Single", 0},
}}; {"Range", 1},
{"HamRadio", 2},
{"Raw", 3},
}};
FrequencyField field_freq_a{{13 * 8, 4 * 16}}; FrequencyField field_freq_a{{13 * 8, 4 * 16}};

View File

@ -21,20 +21,25 @@
*/ */
#include "ui_scanner.hpp" #include "ui_scanner.hpp"
#include "optional.hpp"
#include "ui_fileman.hpp" #include "ui_fileman.hpp"
#include "ui_freqman.hpp" #include "ui_freqman.hpp"
using namespace portapack; using namespace portapack;
namespace fs = std::filesystem;
namespace ui { namespace ui {
static const fs::path default_scan_file{u"FREQMAN/SCANNER.TXT"};
ScannerThread::ScannerThread(std::vector<rf::Frequency> frequency_list) ScannerThread::ScannerThread(std::vector<rf::Frequency> frequency_list)
: frequency_list_{std::move(frequency_list)} { : frequency_list_{std::move(frequency_list)} {
_manual_search = false; _manual_search = false;
create_thread(); create_thread();
} }
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step_hz) ScannerThread::ScannerThread(const scanner_range_t& frequency_range, size_t def_step_hz)
: frequency_range_(frequency_range), def_step_hz_(def_step_hz) { : frequency_range_(frequency_range), def_step_hz_(def_step_hz) {
_manual_search = true; _manual_search = true;
create_thread(); create_thread();
@ -80,7 +85,7 @@ void ScannerThread::set_freq_del(const rf::Frequency v) {
} }
// Force a one-time forward or reverse frequency index change; OK to do this without pausing scan thread // Force a one-time forward or reverse frequency index change; OK to do this without pausing scan thread
//(used when rotary encoder is turned) // (used when rotary encoder is turned)
void ScannerThread::set_index_stepper(const int32_t v) { void ScannerThread::set_index_stepper(const int32_t v) {
_index_stepper = v; _index_stepper = v;
} }
@ -228,17 +233,26 @@ void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
} }
if (!manual_search) { if (!manual_search) {
if (frequency_list.size() > 0) { if (entries.size() > 0)
text_current_index.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() > 1) if (freq_idx < entries.size() && entries[freq_idx].description.size() > 1)
desc_current_index.set(description_list[freq_idx]); // Show description from file text_current_desc.set(entries[freq_idx].description); // Show description from file
else else
desc_current_index.set(desc_freq_list_scan); // Show Scan file name (no description in file) text_current_desc.set(loaded_filename()); // Show Scan file name (no description in file)
} }
} }
std::string ScannerView::loaded_filename() const {
auto filename = loaded_path.filename().string();
if (filename.length() > 23) { // Truncate and add ellipses if long file name
filename.resize(22);
filename = filename + "+";
}
return filename;
}
void ScannerView::focus() { void ScannerView::focus() {
button_load.focus(); button_load.focus();
} }
@ -254,46 +268,46 @@ ScannerView::~ScannerView() {
void ScannerView::show_max_index() { // show total number of freqs to scan void ScannerView::show_max_index() { // show total number of freqs to scan
text_current_index.set("---"); text_current_index.set("---");
if (frequency_list.size() == FREQMAN_MAX_PER_FILE) { if (entries.size() == FREQMAN_MAX_PER_FILE) {
text_max_index.set_style(&Styles::red); text_max_index.set_style(&Styles::red);
text_max_index.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_index.set_style(&Styles::grey); text_max_index.set_style(&Styles::grey);
text_max_index.set("/ " + to_string_dec_uint(frequency_list.size())); text_max_index.set("/ " + to_string_dec_uint(entries.size()));
} }
} }
ScannerView::ScannerView( ScannerView::ScannerView(
NavigationView& nav) NavigationView& nav)
: nav_{nav}, loaded_file_name{"SCANNER"} { : nav_{nav} {
add_children({&labels, add_children({
&field_lna, &labels,
&field_vga, &field_lna,
&field_rf_amp, &field_vga,
&field_volume, &field_rf_amp,
&field_bw, &field_volume,
&field_squelch, &field_bw,
&field_browse_wait, &field_squelch,
&field_lock_wait, &field_browse_wait,
&button_load, &field_lock_wait,
&button_clear, &button_load,
&rssi, &button_clear,
&text_current_index, &rssi,
&text_max_index, &text_current_index,
&desc_current_index, &text_max_index,
&big_display, &text_current_desc,
&button_manual_start, &big_display,
&button_manual_end, &button_manual_start,
&field_mode, &button_manual_end,
&field_step, &field_mode,
&button_manual_search, &field_step,
&button_pause, &button_manual_search,
&button_dir, &button_pause,
&button_audio_app, &button_dir,
&button_mic_app, &button_audio_app,
&button_add, &button_mic_app,
&button_remove &button_add,
&button_remove,
}); });
// Populate option text for these fields // Populate option text for these fields
@ -312,14 +326,14 @@ 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 to load a Freqman file.
button_load.on_select = [this, &nav](Button&) { button_load.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT"); auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir); open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) { open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) { if (new_file_path.native().find(freqman_dir.native()) == 0) {
scan_pause(); scan_pause();
frequency_file_load(new_file_path.stem().string(), true); frequency_file_load(new_file_path);
} else { } else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired."); nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
} }
@ -328,14 +342,13 @@ ScannerView::ScannerView(
// Button to clear in-memory frequency list // Button to clear in-memory frequency list
button_clear.on_select = [this, &nav](Button&) { button_clear.on_select = [this, &nav](Button&) {
if (scan_thread && frequency_list.size()) { if (scan_thread && entries.size()) {
scan_thread->stop(); // STOP SCANNER THREAD scan_thread->stop(); // STOP SCANNER THREAD
frequency_list.clear(); entries.clear();
description_list.clear();
show_max_index(); // UPDATE new list size on screen show_max_index(); // UPDATE new list size on screen
text_current_index.set(""); text_current_index.set("");
desc_current_index.set(desc_freq_list_scan); text_current_desc.set(loaded_filename());
scan_thread->set_freq_lock(0); // Reset the scanner lock 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) // FUTURE: Consider switching to manual search mode automatically after clear (but would need to validate freq range)
@ -404,16 +417,15 @@ ScannerView::ScannerView(
// Button to delete current frequency from scan Freq List // Button to delete current frequency from scan Freq List
button_remove.on_select = [this](Button&) { button_remove.on_select = [this](Button&) {
if (scan_thread && (frequency_list.size() > current_index)) { if (scan_thread && (entries.size() > current_index)) {
scan_thread->set_scanning(false); // PAUSE Scanning if necessary scan_thread->set_scanning(false); // PAUSE Scanning if necessary
// Remove frequency from the Freq List in memory (it is not removed from the file) // 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(entries[current_index].freq);
description_list.erase(description_list.begin() + current_index); entries.erase(entries.begin() + current_index);
frequency_list.erase(frequency_list.begin() + current_index);
show_max_index(); // UPDATE new list size on screen show_max_index(); // UPDATE new list size on screen
desc_current_index.set(""); // Clean up description (cosmetic detail) text_current_desc.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
} }
}; };
@ -432,15 +444,7 @@ ScannerView::ScannerView(
manual_search = false; // Switch to List Scan mode manual_search = false; // Switch to List Scan mode
} }
audio::output::stop(); restart_scan();
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) // Mode field was changed (AM/NFM/WFM)
@ -467,17 +471,8 @@ ScannerView::ScannerView(
field_step.on_change = [this](size_t, OptionsField::value_t v) { field_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v); receiver_model.set_frequency_step(v);
if (manual_search && scan_thread) { if (manual_search && scan_thread)
// Restart scan thread with new step value restart_scan();
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 to toggle Forward/Reverse
@ -491,68 +486,37 @@ ScannerView::ScannerView(
bigdisplay_update(BDC_GREY); // Back to grey color bigdisplay_update(BDC_GREY); // Back to grey color
}; };
// TODO: remove this parsing?
// Button to add current frequency (found during Search) to the Scan Frequency List // Button to add current frequency (found during Search) to the Scan Frequency List
button_add.on_select = [this](Button&) { button_add.on_select = [this](Button&) {
File scanner_file; FreqmanDB db;
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT"; if (db.open(loaded_path, /*create*/ true)) {
auto result = scanner_file.open(freq_file_path); // First search if freq is already in txt freqman_entry entry{
.frequency_a = current_frequency,
.type = freqman_type::Single,
};
if (!result.is_valid()) { // Look for existing entry with same frequency.
const std::string frequency_to_add = "f=" + to_string_dec_uint(current_frequency / 1000) + to_string_dec_uint(current_frequency % 1000UL, 3, '0'); auto it = db.find_entry([&entry](const auto& e) {
return e.frequency_a == entry.frequency_a;
bool found = false; });
constexpr size_t buffer_size = 1024; auto found = (it != db.end());
char buffer[buffer_size];
for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) {
size_t adjusted_buffer_size;
if (pointer + buffer_size >= scanner_file.size()) {
memset(buffer, 0, sizeof(buffer));
adjusted_buffer_size = scanner_file.size() - pointer;
} else {
adjusted_buffer_size = buffer_size;
}
scanner_file.seek(pointer);
scanner_file.read(buffer, adjusted_buffer_size);
for (size_t i = 0; i < adjusted_buffer_size; ++i) {
if (buffer[i] == frequency_to_add.data()[freq_str_idx]) {
++freq_str_idx;
if (freq_str_idx >= frequency_to_add.size()) {
found = true;
break;
}
} else {
freq_str_idx = 0;
}
}
if (found) {
break;
}
}
if (found) { if (found) {
nav_.display_modal("Error", "Frequency already exists"); nav_.display_modal("Error", "Frequency already exists");
bigdisplay_update(-1); // After showing an error bigdisplay_update(-1); // Need to poke this control after displaying modal?
} else { } else {
scanner_file.append(freq_file_path); // Second: append if it is not there db.append_entry(entry);
scanner_file.write_line(frequency_to_add);
// Add to frequency_list in memory too, since we can now switch back from manual mode // 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 // Note that we are allowing freqs to be added to file (code above) that exceed the
if (frequency_list.size() < FREQMAN_MAX_PER_FILE) { // max count we can load into memory.
frequency_list.push_back(current_frequency); if (entries.size() < FREQMAN_MAX_PER_FILE) {
description_list.push_back(""); entries.push_back({current_frequency, ""});
show_max_index(); // Display updated frequency list size 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_path.filename().string() + "\nfor appending freq.");
bigdisplay_update(-1); // After showing an error bigdisplay_update(-1); // Need to poke this control after displaying modal?
} }
}; };
@ -566,106 +530,76 @@ ScannerView::ScannerView(
field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.on_change = [this](int32_t v) { squelch = v; };
field_squelch.set_value(-30); field_squelch.set_value(-30);
// LEARN FREQUENCIES // LOAD FREQUENCIES
std::string scanner_txt = "SCANNER"; frequency_file_load(default_scan_file);
frequency_file_load(scanner_txt);
} }
void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) { void ScannerView::frequency_file_load(const fs::path& path) {
bool found_range{false};
bool found_single{false};
freqman_index_t def_mod_index{freqman_invalid_index}; freqman_index_t def_mod_index{freqman_invalid_index};
freqman_index_t def_bw_index{freqman_invalid_index}; freqman_index_t def_bw_index{freqman_invalid_index};
freqman_index_t def_step_index{freqman_invalid_index}; freqman_index_t def_step_index{freqman_invalid_index};
// stop everything running now if required FreqmanDB db;
if (stop_all_before) { if (!db.open(path)) {
scan_thread->stop(); text_current_desc.set("NO " + path.filename().string());
frequency_list.clear(); // clear the existing frequency list (expected behavior) loaded_path = default_scan_file;
description_list.clear(); return;
} }
if (load_freqman_file(file_name, database, {})) { entries.clear();
loaded_file_name = file_name; loaded_path = path;
for (auto& entry_ptr : database) { Optional<scanner_range_t> range;
if (frequency_list.size() >= FREQMAN_MAX_PER_FILE)
for (auto entry : db) {
if (is_invalid(def_mod_index))
def_mod_index = entry.modulation;
if (is_invalid(def_bw_index))
def_bw_index = entry.bandwidth;
if (is_invalid(def_step_index))
def_step_index = entry.step;
switch (entry.type) {
case freqman_type::Single:
entries.push_back({entry.frequency_a, entry.description});
break;
case freqman_type::HamRadio:
entries.push_back({entry.frequency_a, "R: " + entry.description});
entries.push_back({entry.frequency_b, "T: " + entry.description});
break;
case freqman_type::Range:
// NB: Only the first range will be loaded.
if (!range)
range = {entry.frequency_a, entry.frequency_b};
break;
default:
break; break;
auto& entry = *entry_ptr;
// Get modulation & bw & step from file if specified
// Note these values could be different for each line in the file, but we only look at the first one
//
// Note that freqman requires a very specific string for these parameters,
// so check syntax in frequency file if specified value isn't being loaded
//
if (is_invalid(def_mod_index))
def_mod_index = entry.modulation;
if (is_invalid(def_bw_index))
def_bw_index = entry.bandwidth;
if (is_invalid(def_step_index))
def_step_index = entry.step;
// Get frequency
if (entry.type == freqman_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));
}
} else if (entry.type == freqman_type::Single) {
found_single = true;
frequency_list.push_back(entry.frequency_a);
description_list.push_back(entry.description);
} else if (entry.type == freqman_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 {
loaded_file_name = "SCANNER"; // back to the default frequency file
desc_current_index.set(" NO " + file_name + ".TXT FILE ...");
}
desc_freq_list_scan = loaded_file_name + ".TXT"; if (entries.size() >= FREQMAN_MAX_PER_FILE)
if (desc_freq_list_scan.length() > 23) { // Truncate description and add ellipses if long file name break;
desc_freq_list_scan.resize(20);
desc_freq_list_scan = desc_freq_list_scan + "...";
} }
if (is_valid(def_mod_index) && def_mod_index != (freqman_index_t)field_mode.selected_index_value()) if (is_valid(def_mod_index) && 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) field_mode.set_by_value(def_mod_index);
if (is_valid(def_bw_index)) // Update BW if specified in file if (is_valid(def_bw_index))
field_bw.set_selected_index(def_bw_index); field_bw.set_selected_index(def_bw_index);
if (is_valid(def_step_index)) // Update step if specified in file if (is_valid(def_step_index))
field_step.set_selected_index(def_step_index); field_step.set_selected_index(def_step_index);
audio::output::stop(); // Found range, set it and update UI.
if (range) {
frequency_range = *range;
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
}
if (userpause) // If user-paused, resume // Scan entries if any, otherwise do manual range search.
user_resume(); manual_search = entries.empty();
restart_scan();
// Scan list if we found one, otherwise do manual range search
manual_search = !found_single;
start_scan_thread();
} }
void ScannerView::update_squelch_while_paused(int32_t max_db) { void ScannerView::update_squelch_while_paused(int32_t max_db) {
@ -754,7 +688,8 @@ void ScannerView::user_resume() {
userpause = false; // Resume scanning userpause = false; // Resume scanning
} }
void ScannerView::change_mode(freqman_index_t new_mod) { // Before this, do a scan_thread->stop(); After this do a start_scan_thread() // Before this, do a scan_thread->stop(); After this do a start_scan_thread()
void ScannerView::change_mode(freqman_index_t new_mod) {
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;
@ -791,22 +726,41 @@ void ScannerView::change_mode(freqman_index_t new_mod) { // Before this, do a s
void ScannerView::start_scan_thread() { void ScannerView::start_scan_thread() {
receiver_model.enable(); receiver_model.enable();
// Disable squelch on the model because RSSI handler is where the
// actual squelching is applied for this app.
receiver_model.set_squelch_level(0); receiver_model.set_squelch_level(0);
show_max_index(); show_max_index();
// Start Scanner Thread // Start Scanner Thread
// FUTURE: Consider passing additional control flags (fwd,userpause,etc) to scanner thread at start (perhaps in a data structure)
if (manual_search) { if (manual_search) {
button_manual_search.set_text("SCAN"); // Update meaning of Manual Scan button button_manual_search.set_text("SCAN"); // Update meaning of Manual Scan button
desc_current_index.set(desc_freq_range_search); text_current_desc.set("SEARCHING...");
scan_thread = std::make_unique<ScannerThread>(frequency_range, field_step.selected_index_value()); scan_thread = std::make_unique<ScannerThread>(frequency_range, field_step.selected_index_value());
} else { } else {
button_manual_search.set_text("SRCH"); // Update meaning of Manual Scan button button_manual_search.set_text("SRCH"); // Update meaning of Manual Scan button
desc_current_index.set(desc_freq_list_scan); text_current_desc.set(loaded_filename());
scan_thread = std::make_unique<ScannerThread>(frequency_list);
// TODO: just pass ref to the thread?
std::vector<rf::Frequency> frequency_list;
frequency_list.reserve(entries.size());
for (const auto& entry : entries)
frequency_list.push_back(entry.freq);
scan_thread = std::make_unique<ScannerThread>(std::move(frequency_list));
} }
scan_thread->set_scanning_direction(fwd); scan_thread->set_scanning_direction(fwd);
} }
void ScannerView::restart_scan() {
audio::output::stop();
if (scan_thread) // STOP SCANNER THREAD
scan_thread->stop();
if (userpause) // If user-paused, resume
user_resume();
start_scan_thread(); // RESTART SCANNER THREAD in selected mode
}
} /* namespace ui */ } /* namespace ui */

View File

@ -20,19 +20,20 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include "ui.hpp" #include "audio.hpp"
#include "receiver_model.hpp" #include "analog_audio_app.hpp"
#include "baseband_api.hpp"
#include "file.hpp"
#include "freqman.hpp"
#include "freqman_db.hpp"
#include "portapack_persistent_memory.hpp"
#include "radio_state.hpp" #include "radio_state.hpp"
#include "receiver_model.hpp"
#include "string_format.hpp"
#include "ui.hpp"
#include "ui_mictx.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_styles.hpp" #include "ui_styles.hpp"
#include "freqman.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 SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop #define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
#define STATISTICS_UPDATES_PER_SEC 10 #define STATISTICS_UPDATES_PER_SEC 10
@ -40,10 +41,28 @@
namespace ui { namespace ui {
// TODO: There is too much duplicated data in these classes.
// ScannerThread should just use more from the View.
// Or perhaps ScannerThread should just be in the View.
// TODO: Too many functions mix work and UI update.
// Consolidate UI fixup to a single function.
// TODO: Just use freqman_entry.
struct scanner_entry_t {
rf::Frequency freq;
std::string description;
};
struct scanner_range_t {
int64_t min;
int64_t max;
};
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_hz); ScannerThread(const scanner_range_t& frequency_range, size_t def_step_hz);
~ScannerThread(); ~ScannerThread();
void set_scanning(const bool v); void set_scanning(const bool v);
@ -65,7 +84,7 @@ class ScannerThread {
private: private:
std::vector<rf::Frequency> frequency_list_{}; std::vector<rf::Frequency> frequency_list_{};
jammer::jammer_range_t frequency_range_{false, 0, 0}; scanner_range_t frequency_range_{0, 0};
size_t def_step_hz_{0}; size_t def_step_hz_{0};
Thread* thread{nullptr}; Thread* thread{nullptr};
@ -89,10 +108,6 @@ class ScannerView : public View {
void focus() override; void focus() override;
std::string title() const override { return "Scanner"; }; std::string title() const override { return "Scanner"; };
std::vector<rf::Frequency> frequency_list{};
std::vector<std::string> description_list{};
// void set_parent_rect(const Rect new_parent_rect) override;
private: private:
app_settings::SettingsManager settings_{ app_settings::SettingsManager settings_{
@ -102,18 +117,21 @@ class ScannerView : public View {
RxRadioState radio_state_{}; RxRadioState radio_state_{};
void start_scan_thread(); void start_scan_thread();
void restart_scan();
void change_mode(freqman_index_t mod_type); void change_mode(freqman_index_t mod_type);
void show_max_index(); void show_max_index();
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(const std::filesystem::path& path);
void bigdisplay_update(int32_t); void bigdisplay_update(int32_t);
void update_squelch_while_paused(int32_t max_db); void update_squelch_while_paused(int32_t max_db);
void on_statistics_update(const ChannelStatistics& statistics); void on_statistics_update(const ChannelStatistics& statistics);
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... std::string loaded_filename() const;
scanner_range_t frequency_range{0, 0};
int32_t squelch{0}; int32_t squelch{0};
uint32_t browse_timer{0}; uint32_t browse_timer{0};
uint32_t lock_timer{0}; uint32_t lock_timer{0};
@ -122,10 +140,12 @@ class ScannerView : public View {
rf::Frequency bigdisplay_current_frequency{0}; rf::Frequency bigdisplay_current_frequency{0};
uint32_t browse_wait{0}; uint32_t browse_wait{0};
uint32_t lock_wait{0}; uint32_t lock_wait{0};
freqman_db database{};
std::string loaded_file_name; std::filesystem::path loaded_path{};
std::vector<scanner_entry_t> entries{};
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 manual_search{false};
bool fwd{true}; // to preserve direction setting even if scan_thread restarted bool fwd{true}; // to preserve direction setting even if scan_thread restarted
@ -137,9 +157,6 @@ class ScannerView : public View {
BDC_RED 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: SQ: Wsa: Wsl:", Color::light_grey()}, {{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Color::light_grey()},
@ -204,7 +221,7 @@ class ScannerView : public View {
{4 * 8, 3 * 16, 18 * 8, 16}, {4 * 8, 3 * 16, 18 * 8, 16},
}; };
Text desc_current_index{ Text text_current_desc{
{0, 4 * 16, 240 - 6 * 8, 16}, {0, 4 * 16, 240 - 6 * 8, 16},
}; };