Compare commits

...

20 Commits

Author SHA1 Message Date
Kyle Reed a8cdde7bea Logging Updates (#978)
* log_file ensures directory exists
* Fix audio timestamp path
* Fix auto inc, add subfolder for recordings
* Fix auto inc and support subfolder
2023-05-12 20:08:07 +02:00
ittercho 8b395239d2 Add example signal and correct FM radio stations (#980)
-fixed RADIO_FM_NO.txt and RADIO_FM_PL.txt
-added aurora rgb led command samples
2023-05-12 18:53:08 +02:00
Mark Thompson 730e7ad72b Two minor patches - freqman file processing & NumberField "can_loop" option (#981)
-Enhanced frequency file reading:
Correctly read freq files that contain a mix of SINGLE and RANGE or HAM_RADIO types (strstr in file processing was ignoring EOL and was therefore finding the f= on the next line).  Also changed to simply ignore blank or unrecognized lines versus adding  them as SIMPLE entries to freq table.
This allow comments and white line in freqman files.
-Fixed "can_loop" option in NumberField:
When NumberField range.first was non-zero, and can_loop was true, turning the encoder dial in the downward direction did not result in numbers looping back to range.second as was expected.  This fix allows looping in downward direction in the case where range.first is non-zero.
2023-05-12 18:48:32 +02:00
gullradriel 1e4ea753ba save load settings / thread timer fix (#979)
* fixing bad parameter in save and load settings, as well as default settings. Tuned thread timers a bit
* uniformisation of default values
* indentation
* fix resume recon after lock
2023-05-12 12:06:01 +02:00
Kyle Reed a86993af91 Minor fileman bug fixes (#977)
-sets max file count to 100, fixes partner rename and delete in subfolders.
2023-05-12 07:54:42 +02:00
Kyle Reed a832bd433d Fileman icon toolbar (#975)
* Add cut/copy/paste menu with icons
* Add icons, file limit to fileman
2023-05-11 22:46:38 +02:00
Brumi-2021 f9fdeb3419 Adding the 3 x WFM BW filters to Mic App (#976)
adding the 3 x WFM BW filters to Mic App and other minor msg correction and adding comments to code
2023-05-11 22:45:32 +02:00
gullradriel 3c2fc74f82 Recon skip fix (#974)
* definitely fixed, tested, documented bad consecutive match skip problem
* stability fix for config when no list
* fixed boundaries
* replaced 1000000 by #define OneMHz 1000000
2023-05-11 22:42:46 +02:00
gullradriel 69927e3c2d All logs to LOGS directory (#973)
* Added #define LOG_ROOT_DIR
* using LOG_ROOT_DIR as root path of log
2023-05-11 21:56:24 +02:00
jLynx 1edd3716a1 Revert "specific directory for screens and logs (#971)" (#972)
This reverts commit bc1ad5598c.
2023-05-11 11:44:19 +12:00
gullradriel bc1ad5598c specific directory for screens and logs (#971)
* Changed to LOGS directory, creating if not existing. Adjusted to upper case.
* Changed to SCREENSHOTS directory, creating if not existing
2023-05-10 21:11:06 +02:00
Kyle Reed 8cae998146 Fileman copy/paste support (#970)
* Add copy/paste UI instead of file save
2023-05-10 18:51:09 +02:00
gullradriel 9a22a760ad Record button remove (#968)
* removed unused debug and record buttons
* added log checkbox
* changed ignore and log to false at app start for pocsag
* fixed warning: suggest parentheses around arithmetic in operand of '^' proc signal
* ui comsetic fix
2023-05-10 14:12:49 +02:00
gullradriel 909b00bdae Fixing units KHz => kHz (#967)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-05-10 13:26:21 +02:00
gullradriel 58e731c56e Wfm freqm options (#966)
* added WFM 180K and 40K options in freqman
* Make audio app use freqman funcs to load modulations bandwidths
2023-05-10 13:16:23 +02:00
wifwucite 04e77e0ee8 Update PRESETS.TXT (#962)
LoRa bands added
2023-05-08 22:03:45 +02:00
Mark Thompson 55b93a7044 Update Looking Glass PRESETS.TXT to add yet more bands (#964)
Added FM RADIO, AVIATION, AMATEUR RADIO (HAM), and ITU-defined FREQ BANDS.  (we do realize that 902-928MHz is also used for ISM, and that FM list is having two nearly identical bands.)
2023-05-08 21:58:15 +02:00
Kyle Reed f22046eccc Add "Auto" mode to FrequencyField (#959)
* Add "Auto" mode to FrequencyField
* Use exponential function instead of hyperbolic
* Fix tabs
* Back to hyperbolic, better feel
2023-05-07 22:01:43 +02:00
Brumi-2021 dfadd38e32 Adding 2 WFM filters more to current Audio App ( bw 180Khz and 40 khz to support rx for weather NOAA APT) (#961)
* First part new filter 40k for NOAA in Audio App

* Adding a third WFM filter 180Khz general pursose
2023-05-07 16:08:45 +02:00
jLynx 3cb927c840 Delete sdcard/FIRMWARE directory 2023-05-06 08:19:26 +12:00
102 changed files with 1298 additions and 632 deletions
+4
View File
@@ -13,6 +13,7 @@
/sdcard/FREQMAN/BHT*
/sdcard/FREQMAN/R.TXT
/sdcard/FREQMAN/XXX.TXT
/armbin
# Compiled Object files
*.slo
@@ -59,6 +60,9 @@ CMakeFiles/
*.sublime-workspace
.vscode
# VSCodium extensions
.history
# Host OS leftovers
.DS_Store
/firmware/CMakeCache.txt
+1 -1
View File
@@ -100,7 +100,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav) {
logger = std::make_unique<ACARSLogger>();
if (logger)
logger->append("acars.txt");
logger->append(LOG_ROOT_DIR "/ACARS.TXT");
}
ACARSAppView::~ACARSAppView() {
+1 -1
View File
@@ -399,7 +399,7 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } {
logger = std::make_unique<AISLogger>();
if( logger ) {
logger->append(u"ais.txt");
logger->append( LOG_ROOT_DIR "/AIS.TXT" );
}
}
@@ -36,6 +36,8 @@ using namespace tonekey;
#include "string_format.hpp"
#include "freqman.hpp"
namespace ui {
/* AMOptionsView *********************************************************/
@@ -51,6 +53,7 @@ AMOptionsView::AMOptionsView(
&options_config,
});
freqman_set_bandwidth_option( AM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config
options_config.set_selected_index(receiver_model.am_configuration());
options_config.on_change = [this](size_t n, OptionsField::value_t) {
receiver_model.set_am_configuration(n);
@@ -72,6 +75,7 @@ NBFMOptionsView::NBFMOptionsView(
&field_squelch
});
freqman_set_bandwidth_option( NFM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config
options_config.set_selected_index(receiver_model.nbfm_configuration());
options_config.on_change = [this](size_t n, OptionsField::value_t) {
receiver_model.set_nbfm_configuration(n);
@@ -83,6 +87,27 @@ NBFMOptionsView::NBFMOptionsView(
};
}
/* WFMOptionsView *******************************************************/
WFMOptionsView::WFMOptionsView(
const Rect parent_rect, const Style* const style
) : View { parent_rect }
{
set_style(style);
add_children({
&label_config,
&options_config,
});
freqman_set_bandwidth_option( WFM_MODULATION , options_config ); // adding the common message from freqman.cpp to the options_config
options_config.set_selected_index(receiver_model.wfm_configuration());
options_config.on_change = [this](size_t n, OptionsField::value_t) {
receiver_model.set_wfm_configuration(n);
};
}
/* SPECOptionsView *******************************************************/
SPECOptionsView::SPECOptionsView(
@@ -346,6 +371,7 @@ void AnalogAudioView::on_show_options_modulation() {
break;
case ReceiverModel::Mode::WidebandFMAudio:
widget = std::make_unique<WFMOptionsView>(options_view_rect, &style_options_group);
waterfall.show_audio_spectrum_view(true);
text_ctcss.hidden(true);
break;
+21 -11
View File
@@ -54,12 +54,7 @@ private:
OptionsField options_config {
{ 3 * 8, 0 * 16 },
5,
{
{ "DSB 9k ", 0 },
{ "DSB 6k ", 0 },
{ "USB+3k ", 0 },
{ "LSB-3k ", 0 },
{ "CW ", 0 },
{ // using common messages from freqman.cpp
}
};
};
@@ -76,10 +71,7 @@ private:
OptionsField options_config {
{ 3 * 8, 0 * 16 },
4,
{
{ " 8k5", 0 },
{ "11k ", 0 },
{ "16k ", 0 },
{ // using common messages from freqman.cpp
}
};
@@ -96,6 +88,24 @@ private:
};
};
class WFMOptionsView : public View {
public:
WFMOptionsView(const Rect parent_rect, const Style* const style);
private:
Text label_config {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
"BW",
};
OptionsField options_config {
{ 3 * 8, 0 * 16 },
3,
{ // using common messages from freqman.cpp
}
};
};
class AnalogAudioView;
class SPECOptionsView : public View {
@@ -222,7 +232,7 @@ private:
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
u"AUD",
u"AUD", u"AUDIO",
RecordView::FileType::WAV,
4096,
4
+1 -1
View File
@@ -108,7 +108,7 @@ private:
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
u"BBD_????", RecordView::FileType::RawS16, 16384, 3
u"BBD_????.*", u"CAPTURES", RecordView::FileType::RawS16, 16384, 3
};
spectrum::WaterfallWidget waterfall { };
+1 -1
View File
@@ -132,7 +132,7 @@ ERTAppView::ERTAppView(NavigationView&) {
logger = std::make_unique<ERTLogger>();
if( logger ) {
logger->append(u"ert.txt");
logger->append( LOG_ROOT_DIR "/ERT.TXT" );
}
}
+1 -1
View File
@@ -131,7 +131,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logger = std::make_unique<POCSAGLogger>();
if (logger)
logger->append("pocsag.txt");
logger->append(LOG_ROOT_DIR "/POCSAG.TXT");
audio::output::start();
audio::output::unmute();
+14 -14
View File
@@ -64,8 +64,8 @@ private:
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
bool logging { true };
bool ignore { true };
bool logging { false };
bool ignore { false };
uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state { };
@@ -91,13 +91,7 @@ private:
FrequencyField field_frequency {
{ 0 * 8, 0 * 8 },
};
Checkbox check_log {
{ 24 * 8, 21 },
3,
"LOG",
true
};
NumberField field_volume{
NumberField field_volume{
{ 28 * 8, 0 * 16 },
2,
{ 0, 99 },
@@ -106,16 +100,22 @@ private:
};
Checkbox check_ignore {
{ 1 * 8, 21 },
12,
"Ignore addr:",
true
{ 0 * 8, 21 },
8,
"Ign addr",
false
};
SymField sym_ignore {
{ 16 * 8, 21 },
{ 13 * 8, 25 },
7,
SymField::SYMFIELD_DEC
};
Checkbox check_log {
{ 240 - 8 * 8, 21 },
3,
"LOG",
false
};
Console console {
{ 0, 3 * 16, 240, 256 }
+1 -1
View File
@@ -196,7 +196,7 @@ TPMSAppView::TPMSAppView(NavigationView&) {
logger = std::make_unique<TPMSLogger>();
if( logger ) {
logger->append(u"tpms.txt");
logger->append( LOG_ROOT_DIR "/TPMS.TXT" );
}
}
+3 -2
View File
@@ -351,7 +351,7 @@ void ADSBRxView::sort_entries_by_state()
}
void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
logger = std::make_unique<ADSBLogger>();
logger = std::make_unique<ADSBLogger>();
rtc::RTC datetime;
std::string callsign;
std::string str_info;
@@ -422,6 +422,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
} else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){
entry.set_frame_velo(frame);
if (logger) {
logger->append( LOG_ROOT_DIR "/ADSB.TXT" );
logentry += "Type:" + to_string_dec_uint(msg_sub) +
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
" Spd: "+ to_string_dec_int(entry.velo.speed);
@@ -432,7 +433,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
} // frame.get_DF() == DF_ADSB
if (logger) {
logger->append(u"adsb.txt");
logger->append( LOG_ROOT_DIR "/ADSB.TXT" );
// will log each frame in format:
// 20171103100227 8DADBEEFDEADBEEFDEADBEEFDEADBEEF ICAO:nnnnnn callsign Alt:nnnnnn Latnnn.nn Lonnnn.nn
logger->log_str(logentry);
+12 -13
View File
@@ -60,9 +60,9 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
&field_lna,
&field_vga,
&field_frequency,
&check_log,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
@@ -74,13 +74,6 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
field_rf_amp.set_value(app_settings.rx_amp);
}
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(467225500); // 462713300
auto def_bell202 = &modem_defs[0];
@@ -105,13 +98,18 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
};
logger = std::make_unique<AFSKLogger>();
if (logger)
logger->append("AFSK_LOG.TXT");
logger->append(LOG_ROOT_DIR "/AFSK.TXT");
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false);
@@ -153,22 +151,23 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
console.write(str_console);
if (logger) str_log += str_byte;
if (logger && logging) str_log += str_byte;
if ((value != 0x7F) && (prev_value == 0x7F)) {
// Message split
console.writeln("");
console_color++;
if (logger) {
if (logger && logging) {
logger->log_raw_data(str_log);
str_log = "";
}
}
prev_value = value;
} else {
}
else {
// Baudrate estimation
text_debug.set("~" + to_string_dec_uint(value));
text_debug.set("Baudrate estimation: ~" + to_string_dec_uint(value));
}
}
+11 -10
View File
@@ -64,6 +64,7 @@ private:
uint8_t console_color { 0 };
uint32_t prev_value { 0 };
std::string str_log { "" };
bool logging { false };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
@@ -85,23 +86,23 @@ private:
{ 0 * 8, 0 * 16 },
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
Checkbox check_log {
{ 0 * 8, 1 * 16 },
3,
"LOG",
false
};
Text text_debug {
{ 0 * 8, 12 + 2 * 16, 240, 16 },
"DEBUG"
};
Button button_modem_setup {
{ 12 * 8, 1 * 16, 96, 24 },
{ 240 - 12 * 8, 1 * 16, 96, 24 },
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
+1 -1
View File
@@ -143,7 +143,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) : View(parent_rect
logger = std::make_unique<APRSLogger>();
if (logger)
logger->append("APRS_RX_LOG.TXT");
logger->append(LOG_ROOT_DIR "/APRS.TXT");
baseband::set_aprs(1200);
+1 -1
View File
@@ -235,7 +235,7 @@ private:
// DEBUG
RecordView record_view {
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
u"AFS_????.WAV", u"APRS", RecordView::FileType::WAV, 4096, 4
};
Console console {
-10
View File
@@ -54,9 +54,7 @@ BTLERxView::BTLERxView(NavigationView& nav) {
&field_lna,
&field_vga,
&field_frequency,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
@@ -68,13 +66,6 @@ BTLERxView::BTLERxView(NavigationView& nav) {
field_rf_amp.set_value(app_settings.rx_amp);
}
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2426000000);
auto def_bell202 = &modem_defs[0];
@@ -103,7 +94,6 @@ BTLERxView::BTLERxView(NavigationView& nav) {
nav.push<ModemSetupView>();
};
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false);
+1 -14
View File
@@ -53,7 +53,6 @@ private:
uint8_t console_color { 0 };
uint32_t prev_value { 0 };
std::string str_log { "" };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
@@ -75,23 +74,11 @@ private:
{ 0 * 8, 0 * 16 },
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
"DEBUG"
};
Button button_modem_setup {
{ 12 * 8, 1 * 16, 96, 24 },
{ 240 - 12 * 8, 1 * 16, 96, 24 },
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
+201 -83
View File
@@ -22,7 +22,6 @@
/* TODO:
* - Paging menu items
* - Copy/Move
*/
#include <algorithm>
@@ -43,8 +42,7 @@ bool is_hidden_file(const fs::path& path) {
// Gets a truncated name from a path for display.
std::string truncate(const fs::path& path, size_t max_length) {
auto name = path.string();
return name.length() <= max_length ? name : name.substr(0, max_length);
return ::truncate(path.string(), max_length);
}
// Gets a human readable file size string.
@@ -146,6 +144,22 @@ bool partner_file_prompt(
return true;
}
fs::path get_unique_filename(const fs::path& path, const fs::path& file) {
auto stem = file.stem();
auto ext = file.extension();
auto serial = 1;
fs::path new_path = file;
while (fs::file_exists(path / new_path)) {
new_path = stem;
new_path += fs::path{ u"_" };
new_path += to_string_dec_int(serial++);
new_path += ext;
}
return new_path;
}
}
namespace ui {
@@ -192,12 +206,13 @@ const fileman_entry& FileManBaseView::get_selected_entry() const {
FileManBaseView::FileManBaseView(
NavigationView& nav,
std::string filter
) : nav_ (nav),
extension_filter { filter }
) : nav_{ nav },
extension_filter{ filter }
{
add_children({
&labels,
&text_current,
&text_info,
&button_exit
});
@@ -206,7 +221,7 @@ FileManBaseView::FileManBaseView(
};
if (!sdcIsCardInserted(&SDCD1)) {
empty_root = true;
empty_ = EmptyReason::NoSDC;
text_current.set("NO SD CARD!");
return;
}
@@ -214,7 +229,7 @@ FileManBaseView::FileManBaseView(
load_directory_contents(current_path);
if (!entry_list.size()) {
empty_root = true;
empty_ = EmptyReason::NoFiles;
text_current.set("EMPTY SD CARD!");
} else {
menu_view.on_left = [this]() {
@@ -224,7 +239,7 @@ FileManBaseView::FileManBaseView(
}
void FileManBaseView::focus() {
if (empty_root) {
if (empty_ != EmptyReason::NotEmpty) {
button_exit.focus();
} else {
menu_view.focus();
@@ -287,8 +302,13 @@ void FileManBaseView::refresh_list() {
}
});
}
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
}
text_info.set(menu_view.item_count() >= max_items_shown ? "Too many files!" : "");
menu_view.set_highlighted(prev_highlight);
}
@@ -310,34 +330,8 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
return file_types[index];
}
/*void FileSaveView::on_save_name() {
text_prompt(nav_, &filename_buffer, 8, [this](std::string * buffer) {
nav_.pop();
});
}
FileSaveView::FileSaveView(
NavigationView& nav
) : FileManBaseView(nav)
{
name_buffer.clear();
add_children({
&text_save,
&button_save_name,
&live_timestamp
});
button_save_name.on_select = [this, &nav](Button&) {
on_save_name();
};
}*/
/* FileLoadView **************************************************************/
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
FileLoadView::FileLoadView(
NavigationView& nav,
std::string filter
@@ -367,6 +361,68 @@ FileLoadView::FileLoadView(
};
}
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
/* FileSaveView **************************************************************/
/*
FileSaveView::FileSaveView(
NavigationView& nav,
const fs::path& path,
const fs::path& file
) : nav_{ nav },
path_{ path },
file_{ file }
{
add_children({
&labels,
&text_path,
&button_edit_path,
&text_name,
&button_edit_name,
&button_save,
&button_cancel,
});
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
});
};
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
});
};
button_save.on_select = [this](Button&) {
if (on_save)
on_save(path_ / file_);
else
nav_.pop();
};
button_cancel.on_select = [this](Button&) {
nav_.pop();
};
refresh_widgets();
}
void FileSaveView::refresh_widgets() {
text_path.set(truncate(path_, 30));
text_name.set(truncate(file_, 30));
set_dirty();
}
*/
/* FileManagerView ***********************************************************/
void FileManagerView::on_rename() {
@@ -387,7 +443,7 @@ void FileManagerView::on_rename() {
[this, renamed_path](const fs::path& partner, bool should_rename) mutable {
if (should_rename) {
auto new_name = renamed_path.replace_extension(partner.extension());
rename_file(current_path / partner, current_path / new_name);
rename_file(partner, current_path / new_name);
}
reload_current();
}
@@ -409,7 +465,7 @@ void FileManagerView::on_delete() {
nav_, get_selected_full_path(), "Delete",
[this](const fs::path& partner, bool should_delete) {
if (should_delete)
delete_file(current_path / partner);
delete_file(partner);
reload_current();
}
);
@@ -429,6 +485,34 @@ void FileManagerView::on_new_dir() {
});
}
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current();
});
}
void FileManagerView::on_paste() {
// TODO: handle partner file. Need to fix nav stack first.
auto new_name = get_unique_filename(current_path, clipboard_path.filename());
fs::filesystem_error result;
if (clipboard_mode == ClipboardMode::Cut)
result = rename_file(clipboard_path, current_path / new_name);
else if (clipboard_mode == ClipboardMode::Copy)
result = copy_file(clipboard_path, current_path / new_name);
if (result.code() != FR_OK)
nav_.display_modal("Paste Failed", result.what());
clipboard_path = fs::path{ };
clipboard_mode = ClipboardMode::None;
menu_view.focus();
reload_current();
}
bool FileManagerView::selected_is_valid() const {
return !entry_list.empty() &&
get_selected_entry().path != parent_dir_path;
@@ -437,63 +521,97 @@ bool FileManagerView::selected_is_valid() const {
void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v);
button_delete.hidden(v);
button_cut.hidden(v);
button_copy.hidden(v);
button_paste.hidden(v);
button_new_dir.hidden(v);
set_dirty();
}
button_new_file.hidden(v);
FileManagerView::~FileManagerView() {
set_dirty();
}
FileManagerView::FileManagerView(
NavigationView& nav
) : FileManBaseView(nav, "")
{
if (!empty_root) {
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_delete,
&button_new_dir,
});
menu_view.on_highlight = [this]() {
// TODO: enable/disable buttons.
if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
};
refresh_list();
// Don't bother with the UI in the case of no SDC.
if (empty_ == EmptyReason::NoSDC)
return;
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else {
button_rename.focus();
}
};
button_rename.on_select = [this](Button&) {
if (selected_is_valid())
on_rename();
};
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_delete,
&button_cut,
&button_copy,
&button_paste,
&button_new_dir,
&button_new_file
});
menu_view.on_highlight = [this]() {
if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
};
refresh_list();
button_delete.on_select = [this](Button&) {
if (selected_is_valid())
on_delete();
};
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else {
button_rename.focus();
}
};
button_rename.on_select = [this]() {
if (selected_is_valid())
on_rename();
};
button_new_dir.on_select = [this](Button&) {
on_new_dir();
};
}
button_delete.on_select = [this]() {
if (selected_is_valid())
on_delete();
};
button_cut.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Cut;
} else
nav_.display_modal("Cut", "Can't cut that.");
};
button_copy.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Copy;
} else
nav_.display_modal("Copy", "Can't copy that.");
};
button_paste.on_select = [this]() {
if (clipboard_mode != ClipboardMode::None)
on_paste();
else
nav_.display_modal("Paste", "Cut or copy a file first.");
};
button_new_dir.on_select = [this]() {
on_new_dir();
};
button_new_file.on_select = [this]() {
on_new_file();
};
}
}
+132 -37
View File
@@ -36,6 +36,18 @@ struct fileman_entry {
bool is_directory { };
};
enum class EmptyReason : uint8_t {
NotEmpty,
NoFiles,
NoSDC
};
enum class ClipboardMode : uint8_t {
None,
Cut,
Copy
};
class FileManBaseView : public View {
public:
FileManBaseView(
@@ -43,11 +55,14 @@ public:
std::string filter
);
virtual ~FileManBaseView() { }
void focus() override;
std::string title() const override { return "Fileman"; };
protected:
static constexpr size_t max_filename_length = 50;
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 100;
struct file_assoc_t {
std::filesystem::path extension;
@@ -65,7 +80,6 @@ protected:
{ u"", &bitmap_icon_file, ui::Color::light_grey() } // NB: Must be last.
};
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
@@ -78,7 +92,7 @@ protected:
NavigationView& nav_;
bool empty_root { false };
EmptyReason empty_ { EmptyReason::NotEmpty };
std::function<void(KeyEvent)> on_select_entry { nullptr };
std::function<void(bool)> on_refresh_widgets { nullptr };
@@ -102,59 +116,106 @@ protected:
{ 0, 2 * 8, 240, 26 * 8 },
true
};
// HACK: for item count limit.
Text text_info {
{ 1 * 8, 35 * 8, 15 * 8, 16 },
""
};
Button button_exit {
{ 16 * 8, 34 * 8, 14 * 8, 32 },
{ 21 * 8, 34 * 8, 9 * 8, 32 },
"Exit"
};
};
/*class FileSaveView : public FileManBaseView {
public:
FileSaveView(NavigationView& nav);
~FileSaveView();
private:
std::string name_buffer { };
void on_save_name();
Text text_save {
{ 4 * 8, 15 * 8, 8 * 8, 16 },
"Save as:",
};
Button button_save_name {
{ 4 * 8, 18 * 8, 12 * 8, 32 },
"Name (set)"
};
LiveDateTime live_timestamp {
{ 17 * 8, 24 * 8, 11 * 8, 16 }
};
};*/
class FileLoadView : public FileManBaseView {
public:
std::function<void(std::filesystem::path)> on_changed { };
FileLoadView(NavigationView& nav, std::string filter);
virtual ~FileLoadView() { }
private:
void refresh_widgets(const bool v);
};
/*
// It would be nice to be able to launch FileLoadView
// but it will OOM if launched from within FileManager.
class FileSaveView : public View {
public:
FileSaveView(
NavigationView& nav,
const std::filesystem::path& path,
const std::filesystem::path& file);
std::function<void(std::filesystem::path)> on_save { };
private:
static constexpr size_t max_filename_length = 64;
void refresh_widgets();
NavigationView& nav_;
std::filesystem::path path_;
std::filesystem::path file_;
std::string buffer_ { };
Labels labels {
{ { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() },
{ { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() },
};
Text text_path {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
"",
};
Button button_edit_path {
{ 18 * 8, 3 * 16, 11 * 8, 32 },
"Edit Path"
};
Text text_name {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
"",
};
Button button_edit_name {
{ 18 * 8, 8 * 16, 11 * 8, 32 },
"Edit Name"
};
Button button_save {
{ 10 * 8, 16 * 16, 9 * 8, 32 },
"Save"
};
Button button_cancel {
{ 20 * 8, 16 * 16, 9 * 8, 32 },
"Cancel"
};
};
*/
class FileManagerView : public FileManBaseView {
public:
FileManagerView(NavigationView& nav);
~FileManagerView();
virtual ~FileManagerView() { }
private:
// Passed by ref to other views needing lifetime extension.
std::string name_buffer { };
std::filesystem::path clipboard_path { };
ClipboardMode clipboard_mode { ClipboardMode::None };
void refresh_widgets(const bool v);
void on_rename();
void on_delete();
void on_paste();
void on_new_dir();
void on_new_file();
// True if the selected entry is a real file item.
bool selected_is_valid() const;
@@ -168,19 +229,53 @@ private:
""
};
Button button_rename {
{ 0 * 8, 29 * 8, 14 * 8, 32 },
"Rename"
NewButton button_rename {
{ 0 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_rename,
Color::dark_blue()
};
Button button_delete {
{ 16 * 8, 29 * 8, 14 * 8, 32 },
"Delete"
NewButton button_delete {
{ 4 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_trash,
Color::red()
};
Button button_new_dir {
{ 0 * 8, 34 * 8, 14 * 8, 32 },
"New Dir"
NewButton button_cut {
{ 9 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_cut,
Color::dark_grey()
};
NewButton button_copy {
{ 13 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_copy,
Color::dark_grey()
};
NewButton button_paste {
{ 17 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_paste,
Color::dark_grey()
};
NewButton button_new_dir {
{ 22 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_new_dir,
Color::green()
};
NewButton button_new_file {
{ 26 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_new_file,
Color::green()
};
};
+14 -10
View File
@@ -167,14 +167,14 @@ void MicTXView::rxaudio(bool is_on) {
}
else { // We are in NFM/FM or WFM (NFM BW:8k5 or 11k / FM BW 16k / WFM BW:200k)
if (enable_wfm) { // WFM , BW 200Khz aprox ,
if (enable_wfm) { // WFM , BW 200Khz aprox , or the two new addional BW filters (180k, 40k)
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(0); // there are only 1 x config filters 200k WFM . (not like 8k5/11k/16k)
// receiver_model.set_wfm_configuration(n); // it is called above , depending user's selection (200k, 180k,40k).
} else { // NFM BW:8k5 or 11k / FM BW 16k
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); //
// receiver_model.set_nbfm_configuration(n); is called above , depending 8k5, 11k, 16k
// receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (8k5, 11k, 16k).
}
}
receiver_model.set_sampling_rate(3072000);
@@ -423,10 +423,12 @@ MicTXView::MicTXView(
rxaudio(rx_enabled); //Update now if we have RX audio on
options_tone_key.hidden(0); // we are in WFM mode , we should have active the Key-tones & CTCSS option.
rxbw.emplace_back(" 200k-WFM ", 0); // locked a fixed option , to display it .
rxbw.emplace_back(" 200k-WFM ", 0); // We allow the user selection of the 3 x WFM BW filters, (0) WFM-200K, (1) WFM-180K , (2) WFM-40K .
rxbw.emplace_back(" 180k-WFM ", 1);
rxbw.emplace_back(" 40k-WFM ", 2);
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
field_rxbw.hidden(0); // we are in WFM mode, we need to show to the user the selected BW WFM filer .
field_rxbw.hidden(0); // we are in WFM mode, we need to show to the user the selected BW WFM filter .
field_bw.hidden(0); // we are in WFM mode, we need to allow WFM deviation parameter , in non FM mode.
break;
case 2: //{ "AM", 2 }
@@ -436,7 +438,7 @@ MicTXView::MicTXView(
set_dirty(); // Refresh display
options_tone_key.hidden(1); // we hide that Key-tones & CTCSS input selecction, (no meaning in AM/DSB/SSB).
rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k .
rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k .
rxbw.emplace_back(" DSB2-6k ", 1);
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
@@ -450,7 +452,7 @@ MicTXView::MicTXView(
check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now.
check_rogerbeep.hidden(1); // hide that roger beep selection.
rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it .
rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it .
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
set_dirty(); // Refresh display
@@ -461,7 +463,7 @@ MicTXView::MicTXView(
check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now.
check_rogerbeep.hidden(1); // hide that roger beep selection.
rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it .
rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it .
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
set_dirty(); // Refresh display
@@ -565,12 +567,14 @@ MicTXView::MicTXView(
}
else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-SC)
if (enable_am) { // we are in AM TX mode , we will allow both independent RX audio BW : AM 9K (9K00AE3 / AM 6K (6K00AE3). (In AM option v can be 0 (9k) , 1 (6k)
receiver_model.set_am_configuration(v ); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1),
receiver_model.set_am_configuration(v); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1),
}
if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band). in that submenu, v is 0 (SSB1 USB) or 1 (SSB2 LSB)
receiver_model.set_am_configuration(v +2 ); // we are in DSB-SC TX mode , we need to select proper SSB filter. 0+2 =>usb(2), 1+2=3 =>lsb(3),
}
if (enable_wfm) {
receiver_model.set_wfm_configuration(v); // we are in WFM case, we need to select proper WFB RX BW filter , WFM BW 200K(0), WFM BW 180K(1) , WFM BW 40K(2)
}
}
};
-8
View File
@@ -54,9 +54,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
&field_lna,
&field_vga,
&field_frequency,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
@@ -68,12 +66,6 @@ NRFRxView::NRFRxView(NavigationView& nav) {
field_rf_amp.set_value(app_settings.rx_amp);
}
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2480000000);
auto def_bell202 = &modem_defs[0];
+1 -14
View File
@@ -52,7 +52,6 @@ private:
uint8_t console_color { 0 };
uint32_t prev_value { 0 };
std::string str_log { "" };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
@@ -74,23 +73,11 @@ private:
{ 0 * 8, 0 * 16 },
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
"DEBUG"
};
Button button_modem_setup {
{ 12 * 8, 1 * 16, 96, 24 },
{ 240 - 12 * 8, 1 * 16, 96, 24 },
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
+94 -80
View File
@@ -91,10 +91,10 @@ namespace ui {
return _recon;
}
void ReconThread::set_freq_lock(const uint32_t v) {
void ReconThread::set_freq_lock(const int32_t v) {
_freq_lock = v;
}
uint32_t ReconThread::is_freq_lock() {
int32_t ReconThread::is_freq_lock() {
return _freq_lock;
}
int64_t ReconThread::get_current_freq() {
@@ -103,7 +103,6 @@ namespace ui {
void ReconThread::change_recon_direction() {
_fwd = !_fwd;
//chThdSleepMilliseconds(300); //Give some pause after reversing recon direction
}
bool ReconThread::get_recon_direction() {
@@ -204,6 +203,7 @@ namespace ui {
while( !chThdShouldTerminate() && frequency_list_.size() > 0 )
{
uint32_t remaining_sleep = _lock_duration ;
if( !_freq_delete )
{
has_looped = false ;
@@ -408,7 +408,6 @@ namespace ui {
default:
break;
}
restart_recon = true ;
}
// send a pause message with the right freq
if( has_looped && !_continuous )
@@ -429,9 +428,24 @@ namespace ui {
restart_recon = false ;
}
} // if( _freq_lock == 0 || _stepper != 0 || _index_stepper != 0 )
// this while loop is needed to stabilize consecutive match without skipping
// the little the rest, the more reactive and more CPU hog
// the bigger the rest, less CPU and more delay when switching. I choose 5 msecs.
while( _freq_lock != -1 && _freq_lock < (int32_t)_lock_nb_match && !chThdShouldTerminate() )
{
chThdSleepMilliseconds( 5 );
if( remaining_sleep >= 5 )
remaining_sleep -= 5 ;
else
remaining_sleep = 0 ; // leave some process time
}
if( _freq_lock == -1 )
_freq_lock = 0 ;
} // if( _recon || _stepper != 0 || _index_stepper != 0 )
} // if( !freq_delete )
chThdSleepMilliseconds( _lock_duration ); //Needed to (eventually) stabilize the receiver into new freq
if( remaining_sleep )
chThdSleepMilliseconds( remaining_sleep ); //Needed to (eventually) stabilize the receiver into new freq
} //while( !chThdShouldTerminate() && frequency_list_.size() > 0 )
}//if (frequency_list_.size() > 0 )
} //ReconThread::run
@@ -631,7 +645,7 @@ namespace ui {
ReconView::~ReconView() {
ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume.value() );
ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , field_volume.value() );
// save app settings
settings.save("recon", &app_settings);
@@ -648,6 +662,8 @@ namespace ui {
if( recon_thread )
{
int32_t nb_match = recon_thread->is_freq_lock();
if( nb_match == -1 )
nb_match = 0 ;
if( last_db != db )
{
last_db = db ;
@@ -719,9 +735,15 @@ namespace ui {
//HELPER: Pre-setting a manual range, based on stored frequency
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
receiver_model.set_tuning_frequency( stored_freq );
frequency_range.min = stored_freq - 1000000;
if( stored_freq - OneMHz > 0 )
frequency_range.min = stored_freq - OneMHz ;
else
frequency_range.min = 0 ;
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
frequency_range.max = stored_freq + 1000000;
if( stored_freq + OneMHz < MAX_UFREQ )
frequency_range.max = stored_freq + OneMHz ;
else
frequency_range.max = MAX_UFREQ ;
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
// Loading settings
autostart = persistent_memory::recon_autostart_recon();
@@ -736,8 +758,6 @@ namespace ui {
field_volume.set_value( volume );
if( sd_card_mounted )
{
//Loading input and output file from settings
ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , volume );
// load auto common app settings
auto rc = settings.load("recon", &app_settings);
if(rc == SETTINGS_OK) {
@@ -769,11 +789,11 @@ namespace ui {
{
auto new_view = nav_.push<FrequencyKeypadView>(current_index);
new_view->on_changed = [this, &button](rf::Frequency f) {
f = f / 1000000 ;
f = f / OneMHz ;
if( f >= 1 && f <= frequency_list.size() )
{
recon_thread-> set_index_stepper( f - 1 - current_index );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
};
}
@@ -781,9 +801,9 @@ namespace ui {
button_manual_start.on_change = [this]() {
frequency_range.min = frequency_range.min + button_manual_start.get_encoder_delta() * freqman_entry_get_step_value( def_step );
if( frequency_range.min < 1 )
if( frequency_range.min < 0 )
{
frequency_range.min = 1 ;
frequency_range.min = 0 ;
}
if( frequency_range.min > ( MAX_UFREQ - freqman_entry_get_step_value( def_step ) ) )
{
@@ -836,7 +856,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "FW>" );
recon_thread-> set_index_stepper( 1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
else if( text_cycle.get_encoder_delta() < 0 )
{
@@ -844,7 +864,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "<RW" );
recon_thread-> set_index_stepper( -1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
}
text_cycle.set_encoder_delta( 0 );
@@ -907,7 +927,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "FW>" );
recon_thread-> set_stepper( 1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
else if( button_pause.get_encoder_delta() < 0 )
{
@@ -915,7 +935,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "<RW" );
recon_thread-> set_stepper( -1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
}
button_pause.set_encoder_delta( 0 );
@@ -1117,7 +1137,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "FW>" );
recon_thread-> set_stepper( 1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
else if( button_remove.get_encoder_delta() < 0 )
{
@@ -1125,7 +1145,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "<RW" );
recon_thread-> set_stepper( -1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
}
button_remove.set_encoder_delta( 0 );
@@ -1317,7 +1337,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "FW>" );
recon_thread-> set_stepper( 1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
else if( button_add.get_encoder_delta() < 0 )
{
@@ -1325,7 +1345,7 @@ namespace ui {
recon_thread -> set_recon_direction( fwd );
button_dir.set_text( "<RW" );
recon_thread-> set_stepper( -1 );
recon_thread->set_freq_lock( 0 );
recon_thread->set_freq_lock( -1 );
}
}
button_add.set_encoder_delta( 0 );
@@ -1366,10 +1386,9 @@ namespace ui {
button_recon_setup.on_select = [this,&nav](Button&) {
ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume.value() );
if( frequency_list.size() != 0 )
user_pause();
audio::output::stop();
recon_thread->stop(); //STOP SCANNER THREAD
frequency_list.clear();
auto open_view = nav.push<ReconSetupView>(input_file,output_file,recon_lock_duration,recon_lock_nb_match,recon_match_mode);
open_view -> on_changed = [this](std::vector<std::string> result) {
@@ -1379,7 +1398,7 @@ namespace ui {
recon_lock_nb_match = strtol( result[3].c_str() , nullptr , 10 );
recon_match_mode = strtol( result[4].c_str() , nullptr , 10 );
ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , recon_lock_duration , field_volume.value() );
ReconSetupSaveStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , field_volume.value() );
autosave = persistent_memory::recon_autosave_freqs();
autostart = persistent_memory::recon_autostart_recon();
@@ -1390,61 +1409,56 @@ namespace ui {
load_hamradios = persistent_memory::recon_load_hamradios();
update_ranges = persistent_memory::recon_update_ranges_when_recon();
frequency_file_load( true );
frequency_file_load( false );
if( recon_thread )
{
recon_thread->set_lock_duration( recon_lock_duration );
recon_thread->set_lock_nb_match( recon_lock_nb_match );
recon_thread->set_continuous( continuous );
recon_thread->set_recon_direction( fwd );
if( autostart )
{
user_resume();
}
else
{
user_pause();
}
if( update_ranges && frequency_list.size() != 0 )
{
current_index = 0 ;
button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) );
frequency_range.min = frequency_list[ current_index ] . frequency_a ;
if( frequency_list[ current_index ] . frequency_b != 0 )
{
button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) );
frequency_range.max = frequency_list[ current_index ] . frequency_b ;
}
else
{
button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) );
frequency_range.max = frequency_list[ current_index ] . frequency_a ;
}
}
}
if( autostart )
field_lock_wait.set_value( lock_wait );
show_max();
if( userpause != true )
{
timer = 0 ;
user_resume();
}
else
{
user_pause();
if( recon_thread && frequency_list.size() != 0 )
{
RetuneMessage message { };
message.freq = recon_thread->get_current_freq();
message.range = current_index ;
EventDispatcher::send_message(message);
}
}
if( update_ranges )
{
button_manual_start.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) );
frequency_range.min = frequency_list[ current_index ] . frequency_a ;
if( frequency_list[ current_index ] . frequency_b != 0 )
{
button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_b ) );
frequency_range.max = frequency_list[ current_index ] . frequency_b ;
}
else
{
button_manual_end.set_text( to_string_short_freq( frequency_list[ current_index ] . frequency_a ) );
frequency_range.max = frequency_list[ current_index ] . frequency_a ;
}
}
lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) ) / 100 ;
lock_wait = lock_wait * 100 ; // poor man's rounding
if( lock_wait < 400 )
lock_wait = 400 ;
field_lock_wait.set_value( lock_wait );
show_max();
return ;
};
if( userpause != true )
{
timer = 0 ;
user_resume();
}
else
{
RetuneMessage message { };
message.freq = recon_thread->get_current_freq();
message.range = current_index ;
EventDispatcher::send_message(message);
}
};
field_wait.on_change = [this](int32_t v)
@@ -1507,13 +1521,12 @@ namespace ui {
button_scanner_mode.set_style( &style_blue );
button_scanner_mode.set_text( "RECON" );
file_name.set( "=>" );
field_squelch.set_value( squelch );
//Loading input and output file from settings
ReconSetupLoadStrings( "RECON/RECON.CFG" , input_file , output_file , recon_lock_duration , recon_lock_nb_match , squelch , recon_match_mode , wait , lock_wait , volume );
field_squelch.set_value( squelch );
field_wait.set_value(wait);
lock_wait = ( 4 * ( recon_lock_duration * recon_lock_nb_match ) );
lock_wait = lock_wait / 100 ; lock_wait = lock_wait * 100 ; // poor man's rounding
if( lock_wait < 400 )
lock_wait = 400 ;
field_lock_wait.set_value(lock_wait);
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
@@ -1668,7 +1681,7 @@ namespace ui {
status = 0 ;
update_stats = true ;
continuous_lock = false ;
recon_thread->set_freq_lock( 0 ); //in lock period, still analyzing the signal
recon_thread->set_freq_lock( -1 ); //in lock period, still analyzing the signal
recon_resume(); // RESUME!
big_display.set_style(&style_white);
}
@@ -1703,7 +1716,7 @@ namespace ui {
}
}
}
else // freq_lock < max_lock , LOCKING
else if( freq_lock >= 0 ) // freq_lock < max_lock , LOCKING
{
if( actual_db > squelch ) //MATCHING LEVEL
{
@@ -1734,6 +1747,7 @@ namespace ui {
{
timer = 0 ;
update_stats = true ;
recon_thread->set_freq_lock( -1 );
}
}
}
@@ -1795,17 +1809,17 @@ namespace ui {
}
void ReconView::user_pause() {
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
//button_pause.set_text("<RESUME>"); //PAUSED, show resume
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
button_pause.set_text("<RESUME>"); //PAUSED, show resume
userpause=true;
continuous_lock=false;
recon_pause();
}
void ReconView::user_resume() {
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
//button_pause.set_text("<PAUSE>"); //Show button for pause
userpause=false; // Resume recon
timer = 0 ; // Will trigger a recon_resume() on_statistics_update, also advancing to next freq.
button_pause.set_text("<PAUSE>"); //Show button for pause
userpause=false; // Resume recon
continuous_lock=false;
recon_resume();
}
+11 -5
View File
@@ -42,6 +42,12 @@
// maximum usable freq
#define MAX_UFREQ 7200000000
// 1Mhz helper
#ifdef OneMHz
#undef OneMHz
#endif
#define OneMHz 1000000
namespace ui {
class ReconThread {
@@ -59,8 +65,8 @@ namespace ui {
void set_match_mode( const uint32_t v );
uint32_t get_lock_nb_match();
void set_freq_lock(const uint32_t v);
uint32_t is_freq_lock();
void set_freq_lock(const int32_t v);
int32_t is_freq_lock();
int64_t get_current_freq();
void set_stepper(const int64_t v);
@@ -182,8 +188,8 @@ namespace ui {
int32_t squelch { 0 };
int32_t db { 0 };
int32_t timer { 0 };
int32_t wait { 5000 }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity
uint32_t lock_wait { 500 }; // in msec. Represent the maximum amount of time we will wait for a lock to complete before switching to next
int32_t wait { 1000 }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity
uint32_t lock_wait { 1000 }; // in msec. Represent the maximum amount of time we will wait for a lock to complete before switching to next
int32_t def_step { 0 };
freqman_db frequency_list = { };
uint32_t current_index { 0 };
@@ -200,7 +206,7 @@ namespace ui {
bool load_hamradios = { true };
bool update_ranges = { true };
bool fwd = { true };
uint32_t recon_lock_nb_match = { 10 };
uint32_t recon_lock_nb_match = { 3 };
uint32_t recon_lock_duration = { 50 };
uint32_t recon_match_mode = { 0 };
bool scanner_mode { false };
@@ -84,7 +84,21 @@ namespace ui {
file_position -= (file_data + 256 - line_start);
}
}
}
if( it < nb_params )
{
/* bad number of params, signal defaults */
input_file = "RECON" ;
output_file = "RECON_RESULTS" ;
recon_lock_duration = 50 ;
recon_lock_nb_match = 3 ;
recon_squelch_level = -14 ;
recon_match_mode = 0 ;
wait = 1000 ;
lock_wait = 1000 ;
volume = 40 ;
return false ;
}
if( it > 0 )
@@ -105,7 +119,7 @@ namespace ui {
if( it > 3 )
recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 );
else
recon_lock_nb_match = 10 ;
recon_lock_nb_match = 3 ;
if( it > 4 )
recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 );
@@ -120,7 +134,7 @@ namespace ui {
if( it > 6 )
wait = strtoll( params[ 6 ].c_str() , nullptr , 10 );
else
wait = 5000 ;
wait = 1000 ;
if( it > 7 )
lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 );
@@ -132,11 +146,6 @@ namespace ui {
else
volume = 40 ;
if( it < nb_params )
{
/* bad number of params, signal defaults */
return false ;
}
return true ;
}
+1 -1
View File
@@ -40,7 +40,7 @@ public:
void focus() override;
std::string title() const override { return "Flash Utility"; };
std::string title() const override { return "SD over USB"; };
private:
NavigationView& nav_;
+1 -1
View File
@@ -446,7 +446,7 @@ namespace ui {
else
{
auto result = delete_file( pmem_flag_file );
if( result != 0 )
if( result.code() != FR_OK )
{
text_pmem_status.set("!err. deleting pmem flagfile!");
}
+1 -2
View File
@@ -135,10 +135,9 @@ SondeView::SondeView(NavigationView& nav) {
logger = std::make_unique<SondeLogger>();
if (logger)
logger->append(u"sonde.txt");
logger->append( LOG_ROOT_DIR "/SONDE.TXT" );
// initialize audio:
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
-1
View File
@@ -79,7 +79,6 @@ TestView::TestView(NavigationView& nav) {
cal_value = raw_alt - 0x80;
};
logger = std::make_unique<TestLogger>();
if (logger)
logger->append("saucepan.txt");
+3 -2
View File
@@ -73,8 +73,8 @@ void NBFMConfig::apply(const uint8_t squelch_level) const {
void WFMConfig::apply() const {
const WFMConfigureMessage message {
taps_200k_wfm_decim_0,
taps_200k_wfm_decim_1,
decim_0, // taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0
decim_1, // taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1
taps_64_lp_156_198,
75000,
audio_48k_hpf_30hz_config,
@@ -84,6 +84,7 @@ void WFMConfig::apply() const {
audio::set_rate(audio::Rate::Hz_48000);
}
void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration) {
shared_memory.bb_data.tones_data.tone_defs[index].delta = delta;
shared_memory.bb_data.tones_data.tone_defs[index].duration = duration;
+3
View File
@@ -53,6 +53,9 @@ struct NBFMConfig {
};
struct WFMConfig {
const fir_taps_real<24> decim_0; // To handle both WFM filters , 200k and 40K for NOAA APT
const fir_taps_real<16> decim_1;
void apply() const;
};
+131
View File
@@ -2543,6 +2543,137 @@ static constexpr Bitmap bitmap_target {
{ 16, 16 }, bitmap_target_data
};
static constexpr uint8_t bitmap_icon_trash_data[] = {
0x00, 0x00,
0xC0, 0x01,
0x20, 0x02,
0xFC, 0x1F,
0x00, 0x00,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0xA8, 0x0A,
0x08, 0x08,
0xF0, 0x07,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_trash {
{ 16, 16 }, bitmap_icon_trash_data
};
static constexpr uint8_t bitmap_icon_copy_data[] = {
0x00, 0x00,
0xFC, 0x00,
0x84, 0x01,
0xC4, 0x0F,
0x74, 0x18,
0x44, 0x38,
0x44, 0x78,
0x74, 0x40,
0x44, 0x44,
0x44, 0x44,
0x74, 0x5F,
0x44, 0x44,
0x44, 0x44,
0x7C, 0x40,
0xC0, 0x7F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_copy {
{ 16, 16 }, bitmap_icon_copy_data
};
static constexpr uint8_t bitmap_icon_cut_data[] = {
0x00, 0x00,
0x10, 0x10,
0x30, 0x18,
0x20, 0x08,
0x60, 0x0C,
0x40, 0x04,
0xC0, 0x06,
0x80, 0x00,
0x80, 0x01,
0x80, 0x01,
0xC0, 0x03,
0x78, 0x1E,
0x44, 0x22,
0x44, 0x22,
0x44, 0x22,
0x38, 0x1C,
};
static constexpr Bitmap bitmap_icon_cut {
{ 16, 16 }, bitmap_icon_cut_data
};
static constexpr uint8_t bitmap_icon_paste_data[] = {
0x00, 0x00,
0xE0, 0x00,
0x18, 0x03,
0xE4, 0x04,
0x04, 0x04,
0x04, 0x04,
0x84, 0x3F,
0x84, 0x20,
0x84, 0x2E,
0x84, 0x20,
0x84, 0x2E,
0x84, 0x20,
0x84, 0x2E,
0xF8, 0x20,
0x80, 0x3F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_paste {
{ 16, 16 }, bitmap_icon_paste_data
};
static constexpr uint8_t bitmap_icon_new_dir_data[] = {
0x00, 0x00,
0x1E, 0x00,
0x21, 0x00,
0xE1, 0x7F,
0x01, 0xC0,
0x81, 0x81,
0x81, 0x81,
0x81, 0x81,
0xF1, 0x8F,
0xF1, 0x8F,
0x81, 0x81,
0x81, 0x81,
0x81, 0x81,
0x03, 0xC0,
0xFE, 0x7F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_new_dir {
{ 16, 16 }, bitmap_icon_new_dir_data
};
static constexpr uint8_t bitmap_icon_new_file_data[] = {
0x00, 0x00,
0xFC, 0x07,
0x04, 0x0C,
0x04, 0x1C,
0x04, 0x3C,
0x84, 0x21,
0x84, 0x21,
0x84, 0x21,
0xF4, 0x2F,
0xF4, 0x2F,
0x84, 0x21,
0x84, 0x21,
0x84, 0x21,
0x04, 0x20,
0xFC, 0x3F,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_new_file {
{ 16, 16 }, bitmap_icon_new_file_data
};
} /* namespace ui */
+128 -34
View File
@@ -124,50 +124,85 @@ Optional<File::Error> File::sync() {
}
}
static std::filesystem::path find_last_file_matching_pattern(const std::filesystem::path& pattern) {
std::filesystem::path last_match;
for(const auto& entry : std::filesystem::directory_iterator(u"", pattern)) {
if( std::filesystem::is_regular_file(entry.status()) ) {
/* Range used for filename matching.
* Start and end are inclusive positions of "???" */
struct pattern_range {
size_t start;
size_t end;
};
/* Finds the last file matching the specified pattern that
* can be automatically incremented (digits in pattern).
* NB: assumes a patten with contiguous '?' like "FOO_???.txt". */
static std::filesystem::path find_last_ordinal_match(
const std::filesystem::path& folder,
const std::filesystem::path& pattern,
pattern_range range
) {
auto last_match = std::filesystem::path();
auto can_increment = [range](const auto& path) {
for (auto i = range.start; i <= range.end; ++i)
if (!isdigit(path.native()[i]))
return false;
return true;
};
for (const auto& entry : std::filesystem::directory_iterator(folder, pattern)) {
if (std::filesystem::is_regular_file(entry.status()) && can_increment(entry.path())) {
const auto& match = entry.path();
if( match > last_match ) {
last_match = match;
}
}
}
return last_match;
}
static std::filesystem::path increment_filename_stem_ordinal(std::filesystem::path path) {
auto t = path.replace_extension().native();
auto it = t.rbegin();
/* Given a file path like "FOO_0001.txt" increment it to "FOO_0002.txt". */
static std::filesystem::path increment_filename_ordinal(
const std::filesystem::path& path, pattern_range range
) {
auto name = path.filename().native();
// Increment decimal number before the extension.
for(; it != t.rend(); ++it) {
const auto c = *it;
if( c < '0' ) {
for (auto i = range.end; i >= range.start; --i) {
auto& c = name[i];
// Not a digit or would overflow the counter.
if (c < u'0' || c > u'9' || (c == u'9' && i == range.start))
return { };
} else if( c < '9' ) {
*it += 1;
if (c == u'9')
c = '0';
else {
c++;
break;
} else if( c == '9' ) {
*it = '0';
} else {
return { };
}
}
return t;
return { name };
}
std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_pattern) {
const auto next_filename = find_last_file_matching_pattern(filename_pattern.replace_extension(u".*"));
if( next_filename.empty() ) {
auto pattern_s = filename_pattern.replace_extension().native();
std::replace(std::begin(pattern_s), std::end(pattern_s), '?', '0');
return pattern_s;
} else {
return increment_filename_stem_ordinal(next_filename);
std::filesystem::path next_filename_matching_pattern(const std::filesystem::path& filename_pattern) {
auto path = filename_pattern.parent_path();
auto pattern = filename_pattern.filename();
auto range = pattern_range {
pattern.native().find_first_of(u'?'),
pattern.native().find_last_of(u'?')
};
const auto match = find_last_ordinal_match(path, pattern, range);
if (match.empty()) {
auto pattern_str = pattern.native();
for (auto i = range.start; i <= range.end; ++i)
pattern_str[i] = u'0';
return path / pattern_str;
}
auto next_name = increment_filename_ordinal(match, range);
return next_name.empty() ? next_name : path / next_name;
}
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory,
@@ -197,12 +232,44 @@ std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::
return directory_list;
}
uint32_t delete_file(const std::filesystem::path& file_path) {
return f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str()));
std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path) {
return { f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str())) };
}
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name) {
return f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str()));
std::filesystem::filesystem_error rename_file(
const std::filesystem::path& file_path,
const std::filesystem::path& new_name
) {
return { f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str())) };
}
std::filesystem::filesystem_error copy_file(
const std::filesystem::path& file_path,
const std::filesystem::path& dest_path
) {
File src;
File dst;
constexpr size_t buffer_size = 128;
uint8_t buffer[buffer_size];
auto error = src.open(file_path);
if (error.is_valid()) return error.value();
error = dst.create(dest_path);
if (error.is_valid()) return error.value();
while (true) {
auto result = src.read(buffer, buffer_size);
if (result.is_error()) return result.error();
result = dst.write(buffer, result.value());
if (result.is_error()) return result.error();
if (result.value() < buffer_size)
break;
}
return { };
}
FATTimestamp file_created_date(const std::filesystem::path& file_path) {
@@ -213,8 +280,33 @@ FATTimestamp file_created_date(const std::filesystem::path& file_path) {
return { filinfo.fdate, filinfo.ftime };
}
uint32_t make_new_directory(const std::filesystem::path& dir_path) {
return f_mkdir(reinterpret_cast<const TCHAR*>(dir_path.c_str()));
std::filesystem::filesystem_error make_new_file(
const std::filesystem::path& file_path
) {
File f;
auto result = f.create(file_path);
return result.is_valid()
? result.value()
: std::filesystem::filesystem_error{ };
}
std::filesystem::filesystem_error make_new_directory(
const std::filesystem::path& dir_path
) {
return { f_mkdir(reinterpret_cast<const TCHAR*>(dir_path.c_str())) };
}
std::filesystem::filesystem_error ensure_directory(
const std::filesystem::path& dir_path
) {
if (dir_path.empty() || std::filesystem::file_exists(dir_path))
return { };
auto result = ensure_directory(dir_path.parent_path());
if (result.code())
return result;
return make_new_directory(dir_path);
}
namespace std {
@@ -339,8 +431,10 @@ directory_iterator::directory_iterator(
) : pattern { wild }
{
impl = std::make_shared<Impl>();
const auto result = f_findfirst(&impl->dir, &impl->filinfo, reinterpret_cast<const TCHAR*>(path.c_str()), reinterpret_cast<const TCHAR*>(pattern.c_str()));
if( result != FR_OK || impl->filinfo.fname[0] == (TCHAR)'\0') {
const auto result = f_findfirst(&impl->dir, &impl->filinfo,
reinterpret_cast<const TCHAR*>(path.c_str()),
reinterpret_cast<const TCHAR*>(pattern.c_str()));
if (result != FR_OK || impl->filinfo.fname[0] == (TCHAR)'\0') {
impl.reset();
// TODO: Throw exception if/when I enable exceptions...
}
+16 -5
View File
@@ -59,6 +59,10 @@ struct filesystem_error {
std::string what() const;
bool ok() const {
return err == FR_OK;
}
private:
uint32_t err { FR_OK };
};
@@ -252,16 +256,23 @@ struct FATTimestamp {
uint16_t FAT_time;
};
uint32_t delete_file(const std::filesystem::path& file_path);
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
std::filesystem::filesystem_error delete_file(const std::filesystem::path& file_path);
std::filesystem::filesystem_error rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
std::filesystem::filesystem_error copy_file(const std::filesystem::path& file_path, const std::filesystem::path& dest_path);
FATTimestamp file_created_date(const std::filesystem::path& file_path);
uint32_t make_new_directory(const std::filesystem::path& dir_path);
std::filesystem::filesystem_error make_new_file(const std::filesystem::path& file_path);
std::filesystem::filesystem_error make_new_directory(const std::filesystem::path& dir_path);
std::filesystem::filesystem_error ensure_directory(const std::filesystem::path& dir_path);
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension);
std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory);
/* Gets an auto incrementing filename. */
std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern);
/* Gets an auto incrementing filename stem.
* Pattern should be like "FOO_???.txt" where ??? will be replaced by digits.
* Pattern may also contain a folder path like "LOGS/FOO_???.txt".
* Pattern '?' must be contiguous (bad: "FOO?_??")
* Returns empty path if a filename could not be created. */
std::filesystem::path next_filename_matching_pattern(const std::filesystem::path& pattern);
/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */
static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected.");
+35 -34
View File
@@ -47,44 +47,46 @@ options_t freqman_entry_bandwidths[ 4 ] = {
},
{ //WFM
{ "200k" , 0 },
{ "180k" , 1 },
{ " 40k" , 2 },
}
};
options_t freqman_entry_steps = {
{ "0.1KHz " , 100 },
{ "1KHz " , 1000 },
{ "5KHz (SA AM)" , 5000 },
{ "6.25KHz(NFM)" , 6250 },
{ "8.33KHz(AIR)" , 8330 },
{ "9KHz (EU AM)" , 9000 },
{ "10KHz(US AM)" , 10000 },
{ "12.5KHz(NFM)" , 12500 },
{ "15KHz (HFM)" , 15000 },
{ "25KHz (N1)" , 25000 },
{ "30KHz (OIRT)" , 30000 },
{ "50KHz (FM1)" , 50000 },
{ "100KHz (FM2)" , 100000 },
{ "250KHz (N2)" , 250000 },
{ "500KHz (WFM)" , 500000 },
{ "0.1kHz " , 100 },
{ "1kHz " , 1000 },
{ "5kHz (SA AM)" , 5000 },
{ "6.25kHz(NFM)" , 6250 },
{ "8.33kHz(AIR)" , 8330 },
{ "9kHz (EU AM)" , 9000 },
{ "10kHz(US AM)" , 10000 },
{ "12.5kHz(NFM)" , 12500 },
{ "15kHz (HFM)" , 15000 },
{ "25kHz (N1)" , 25000 },
{ "30kHz (OIRT)" , 30000 },
{ "50kHz (FM1)" , 50000 },
{ "100kHz (FM2)" , 100000 },
{ "250kHz (N2)" , 250000 },
{ "500kHz (WFM)" , 500000 },
{ "1MHz " , 1000000 }
};
options_t freqman_entry_steps_short = {
{ "0.1KHz" , 100 },
{ "1KHz" , 1000 },
{ "5KHz" , 5000 },
{ "6.25KHz" , 6250 },
{ "8.33KHz" , 8330 },
{ "9KHz" , 9000 },
{ "10KHz" , 10000 },
{ "12.5KHz" , 12500 },
{ "15KHz" , 15000 },
{ "25KHz" , 25000 },
{ "30KHz" , 30000 },
{ "50KHz" , 50000 },
{ "100KHz" , 100000 },
{ "250KHz" , 250000 },
{ "500KHz" , 500000 },
{ "0.1kHz" , 100 },
{ "1kHz" , 1000 },
{ "5kHz" , 5000 },
{ "6.25kHz" , 6250 },
{ "8.33kHz" , 8330 },
{ "9kHz" , 9000 },
{ "10kHz" , 10000 },
{ "12.5kHz" , 12500 },
{ "15kHz" , 15000 },
{ "25kHz" , 25000 },
{ "30kHz" , 30000 },
{ "50kHz" , 50000 },
{ "100kHz" , 100000 },
{ "250kHz" , 250000 },
{ "500kHz" , 500000 },
{ "1MHz" , 1000000 }
};
@@ -144,17 +146,15 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
// Reset line_start to beginning of buffer
line_start = file_data;
if (!strstr(file_data, "f=") && !strstr(file_data, "a=") && !strstr(file_data, "r=") )
break;
// Look for complete lines in buffer
while ((line_end = strstr(line_start, "\x0A"))) {
*line_end = 0; // Stop strstr() searches below at EOL
modulation = -1 ;
bandwidth = -1 ;
step = -1 ;
tone = -1 ;
type=SINGLE ;
type = ERROR_TYPE;
frequency_a = frequency_b = 0;
// Read frequency
@@ -162,6 +162,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
if(pos) {
pos += 2;
frequency_a = strtoll(pos, nullptr, 10);
type = SINGLE;
} else {
// ...or range
pos = strstr(line_start, "a=");
+1 -1
View File
@@ -87,7 +87,7 @@ struct freqman_entry {
freqman_entry_type type { }; // SINGLE,RANGE,HAMRADIO
freqman_index_t modulation { }; // AM,NFM,WFM
freqman_index_t bandwidth { }; // AM_DSB, ...
freqman_index_t step { }; // 5Khz (SA AM,...
freqman_index_t step { }; // 5khz (SA AM,...
tone_index tone { }; // 0XZ, 11 1ZB,...
};
+7
View File
@@ -27,11 +27,18 @@
#include "file.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#define LOG_ROOT_DIR "LOGS"
class LogFile {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
auto result = ensure_directory(filename.parent_path());
if (result.code())
return { result };
return file.append(filename);
}
+4 -2
View File
@@ -52,8 +52,10 @@ static constexpr std::array<baseband::NBFMConfig, 3> nbfm_configs { {
{ taps_16k0_decim_0, taps_16k0_decim_1, taps_16k0_channel, 5000 },
} };
static constexpr std::array<baseband::WFMConfig, 1> wfm_configs { {
{ },
static constexpr std::array<baseband::WFMConfig, 3> wfm_configs { {
{taps_200k_wfm_decim_0, taps_200k_wfm_decim_1 },
{taps_180k_wfm_decim_0, taps_180k_wfm_decim_1 },
{taps_40k_wfm_decim_0, taps_40k_wfm_decim_1 },
} };
} /* namespace */
+11 -2
View File
@@ -256,8 +256,17 @@ double get_decimals(double num, int16_t mult, bool round) {
return intnum;
}
std::string trimr(std::string str)
{
std::string trim(const std::string& str) {
auto first = str.find_first_not_of(' ');
auto last = str.find_last_not_of(' ');
return str.substr(first, last - first);
}
std::string trimr(std::string str) {
size_t last = str.find_last_not_of(' ');
return (last!=std::string::npos) ? str.substr(0, last+1) : ""; // Remove the trailing spaces
}
std::string truncate(const std::string& str, size_t length) {
return str.length() <= length ? str : str.substr(0, length);
}
+2
View File
@@ -59,6 +59,8 @@ std::string to_string_FAT_timestamp(const FATTimestamp& timestamp);
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision);
double get_decimals(double num, int16_t mult, bool round = false); //euquiq added
std::string trim(const std::string& str); // Remove whitespace at ends.
std::string trimr(std::string str); // Remove trailing spaces
std::string truncate(const std::string& str, size_t length);
#endif/*__STRING_FORMAT_H__*/
+4
View File
@@ -167,6 +167,10 @@ void MenuView::clear() {
offset = 0;
}
size_t MenuView::item_count() const {
return menu_items.size();
}
void MenuView::add_item(MenuItem new_item) {
menu_items.push_back(new_item);
+1
View File
@@ -83,6 +83,7 @@ public:
void add_item(MenuItem new_item);
void add_items(std::initializer_list<MenuItem> new_items);
void clear();
size_t item_count() const;
MenuItemView* item_view(size_t index) const;
+16 -1
View File
@@ -87,7 +87,22 @@ bool FrequencyField::on_key(const ui::KeyEvent event) {
}
bool FrequencyField::on_encoder(const EncoderEvent delta) {
set_value(value() + (delta * step));
if (step == 0) { // 'Auto' mode.'
auto ms = RTT2MS(halGetCounterValue());
auto delta_ms = last_ms_ <= ms ? ms - last_ms_ : ms;
last_ms_ = ms;
// The goal is to map 'scale' to a range of about 10 to 10M.
// The faster the encoder is rotated, the larger the step.
// Linear doesn't feel right. Hyperbolic felt better.
// To get these magic numbers, I graphed the function until the
// curve shape seemed about right then tested on device.
delta_ms = std::min(145ull, delta_ms) + 5; // Prevent DIV/0
int64_t scale = 200'000'000 / (0.001'55 * pow(delta_ms, 5.45)) + 8;
set_value(value() + (delta * scale));
} else {
set_value(value() + (delta * step));
}
return true;
}
+2
View File
@@ -63,6 +63,7 @@ private:
const range_t range;
rf::Frequency value_ { 0 };
rf::Frequency step { 25000 };
uint64_t last_ms_ { 0 };
rf::Frequency clamp_value(rf::Frequency value);
};
@@ -247,6 +248,7 @@ public:
parent_pos,
5,
{
{ " Auto", 0 }, /* Faster == larger step. */
{ " 10", 10 }, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
{ " 50", 50 }, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
{ " 100", 100 },
-131
View File
@@ -61,137 +61,6 @@ void text_prompt(
}*/
}
/* TextField ***********************************************************/
TextField::TextField(
std::string& str,
size_t max_length,
Point position,
uint32_t length
) : Widget{ { position, { 8 * static_cast<int>(length), 16 } } },
text_{ str },
max_length_{ std::max<size_t>(max_length, str.length()) },
char_count_{ std::max<uint32_t>(length, 1) },
cursor_pos_{ text_.length() },
insert_mode_{ true }
{
set_focusable(true);
}
const std::string& TextField::value() const {
return text_;
}
void TextField::set_cursor(uint32_t pos) {
cursor_pos_ = std::min<size_t>(pos, text_.length());
set_dirty();
}
void TextField::set_insert_mode() {
insert_mode_ = true;
}
void TextField::set_overwrite_mode() {
insert_mode_ = false;
}
void TextField::char_add(char c) {
// Don't add if inserting and at max_length and
// don't overwrite if past the end of the text.
if ((text_.length() >= max_length_ && insert_mode_) ||
(cursor_pos_ >= text_.length() && !insert_mode_))
return;
if (insert_mode_)
text_.insert(cursor_pos_, 1, c);
else
text_[cursor_pos_] = c;
cursor_pos_++;
set_dirty();
}
void TextField::char_delete() {
if (cursor_pos_ == 0)
return;
cursor_pos_--;
text_.erase(cursor_pos_, 1);
set_dirty();
}
void TextField::paint(Painter& painter) {
constexpr int char_width = 8;
auto rect = screen_rect();
auto text_style = has_focus() ? style().invert() : style();
auto offset = 0;
// Does the string need to be shifted?
if (cursor_pos_ >= char_count_)
offset = cursor_pos_ - char_count_ + 1;
// Clear the control.
painter.fill_rectangle(rect, text_style.background);
// Draw the text starting at the offset.
for (uint32_t i = 0; i < char_count_ && i + offset < text_.length(); i++) {
painter.draw_char(
{ rect.location().x() + (static_cast<int>(i) * char_width), rect.location().y() },
text_style,
text_[i + offset]
);
}
// Determine cursor position on screen (either the cursor position or the last char).
int32_t cursor_x = char_width * (offset > 0 ? char_count_ - 1 : cursor_pos_);
Point cursor_point{ screen_pos().x() + cursor_x, screen_pos().y() };
auto cursor_style = text_style.invert();
// Invert the cursor character when in overwrite mode.
if (!insert_mode_ && (cursor_pos_) < text_.length())
painter.draw_char(cursor_point, cursor_style, text_[cursor_pos_]);
// Draw the cursor.
Rect cursor_box{ cursor_point, { char_width, 16 } };
painter.draw_rectangle(cursor_box, cursor_style.background);
}
bool TextField::on_key(const KeyEvent key) {
if (key == KeyEvent::Left && cursor_pos_ > 0)
cursor_pos_--;
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
cursor_pos_++;
else if (key == KeyEvent::Select)
insert_mode_ = !insert_mode_;
else
return false;
set_dirty();
return true;
}
bool TextField::on_encoder(const EncoderEvent delta) {
int32_t new_pos = cursor_pos_ + delta;
// Let the encoder wrap around the ends of the text.
if (new_pos < 0)
new_pos = text_.length();
else if (static_cast<size_t>(new_pos) > text_.length())
new_pos = 0;
set_cursor(new_pos);
return true;
}
bool TextField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start)
focus();
set_dirty();
return true;
}
/* TextEntryView ***********************************************************/
void TextEntryView::char_delete() {
-46
View File
@@ -28,52 +28,6 @@
namespace ui {
// A TextField is bound to a string reference and allows the string
// to be manipulated. The field itself does not provide the UI for
// setting the value. It provides the UI of rendering the text,
// a cursor, and an API to edit the string content.
class TextField : public Widget {
public:
TextField(std::string& str, Point position, uint32_t length = 30)
: TextField{str, 64, position, length} { }
// Str: the string containing the content to edit.
// Max_length: max length the string is allowed to use.
// Position: the top-left corner of the control.
// Length: the number of characters to display.
// - Characters are 8 pixels wide.
// - The screen can show 30 characters max.
// - The control is 16 pixels tall.
TextField(std::string& str, size_t max_length, Point position, uint32_t length = 30);
TextField(const TextField&) = delete;
TextField(TextField&&) = delete;
TextField& operator=(const TextField&) = delete;
TextField& operator=(TextField&&) = delete;
const std::string& value() const;
void set_cursor(uint32_t pos);
void set_insert_mode();
void set_overwrite_mode();
void char_add(char c);
void char_delete();
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
protected:
std::string& text_;
size_t max_length_;
uint32_t char_count_;
uint32_t cursor_pos_;
bool insert_mode_;
};
class TextEntryView : public View {
public:
std::function<void(std::string&)> on_changed { };
+4 -2
View File
@@ -367,13 +367,15 @@ void SystemStatusView::on_bias_tee() {
}*/
void SystemStatusView::on_camera() {
auto path = next_filename_stem_matching_pattern(u"SCR_????");
ensure_directory("SCREENSHOTS");
auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG");
if( path.empty() ) {
return;
}
PNGWriter png;
auto create_error = png.create(path.replace_extension(u".PNG"));
auto create_error = png.create(path);
if( create_error.is_valid() ) {
return;
}
+1 -1
View File
@@ -135,7 +135,7 @@ namespace ui
Color::dark_grey()};
ImageButton button_back{
{0, 0 * 16, 12 * 8, 16},//back button is long enough to cover the title area to make it easier to touch
{0, 0 * 16, 12 * 8, 16}, // Back button also covers the title for easier touch.
&bitmap_icon_previous,
Color::white(),
Color::dark_grey()};
+10 -6
View File
@@ -55,16 +55,19 @@ namespace ui {
RecordView::RecordView(
const Rect parent_rect,
std::filesystem::path filename_stem_pattern,
const std::filesystem::path& filename_stem_pattern,
const std::filesystem::path& folder,
const FileType file_type,
const size_t write_size,
const size_t buffer_count
) : View { parent_rect },
filename_stem_pattern { filename_stem_pattern },
folder { folder },
file_type { file_type },
write_size { write_size },
buffer_count { buffer_count }
{
ensure_directory(folder);
add_children({
&rect_background,
//&button_pitch_rssi,
@@ -154,7 +157,6 @@ void RecordView::start() {
return;
}
std::filesystem::path base_path;
if(filename_date_frequency) {
rtcGetTime(&RTCD1, &datetime);
@@ -167,9 +169,11 @@ void RecordView::start() {
to_string_dec_uint(datetime.minute()) +
to_string_dec_uint(datetime.second());
base_path = filename_stem_pattern.string() + "_" + date_time + "_" + to_string_freq(receiver_model.tuning_frequency()) + "Hz";
base_path = filename_stem_pattern.string() + "_" + date_time + "_" +
trim(to_string_freq(receiver_model.tuning_frequency())) + "Hz";
base_path = folder / base_path;
} else {
base_path = next_filename_stem_matching_pattern(filename_stem_pattern);
base_path = next_filename_matching_pattern(folder / filename_stem_pattern);
}
if( base_path.empty() ) {
@@ -217,7 +221,7 @@ void RecordView::start() {
};
if( writer ) {
text_record_filename.set(base_path.replace_extension().string());
text_record_filename.set(truncate(base_path.filename().string(), 8));
button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>(
std::move(writer),
@@ -275,7 +279,7 @@ void RecordView::on_tick_second() {
void RecordView::update_status_display() {
if( is_active() ) {
const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent());
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "%";
text_record_dropped.set(s);
}
+3 -1
View File
@@ -46,7 +46,8 @@ public:
RecordView(
const Rect parent_rect,
std::filesystem::path filename_stem_pattern,
const std::filesystem::path& filename_stem_pattern,
const std::filesystem::path& folder,
FileType file_type,
const size_t write_size,
const size_t buffer_count
@@ -83,6 +84,7 @@ private:
rtc::RTC datetime { };
const std::filesystem::path filename_stem_pattern;
const std::filesystem::path folder;
const FileType file_type;
const size_t write_size;
const size_t buffer_count;
+1 -1
View File
@@ -67,7 +67,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
// 16 bits LFSR .taps: 16, 15, 13, 4 ;feedback polynomial: x^16 + x^15 + x^13 + x^4 + 1
// Periode 65535= 2^n-1, quite continuous .
if (counter == 0) { // we slow down the shift register, because the pseudo random noise clock freq was too high for modulator.
bit_16 = ((lfsr_16 >> 0) ^ (lfsr_16 >> 1) ^ (lfsr_16 >> 3) ^ (lfsr_16 >> 4) ^ (lfsr_16 >> 12) & 1);
bit_16 = ((lfsr_16 >> 0) ^ (lfsr_16 >> 1) ^ (lfsr_16 >> 3) ^ (lfsr_16 >> 4) ^ ((lfsr_16 >> 12) & 1) );
lfsr_16 = (lfsr_16 >> 1) | (bit_16 << 15);
sample = (lfsr_16 & 0x00FF); // main pseudo random noise generator.
}
+53
View File
@@ -450,6 +450,59 @@ constexpr fir_taps_real<64> taps_64_lp_156_198 {
} },
};
// WFM 180kHZ General purpose filter with sharp transition , it improves Commercial WFM S/N in weak signals //////////////////////////////////////////////
// IFIR image-reject filter: fs=3072000, pass=90000, stop=250000, decim=4, fout=768000
constexpr fir_taps_real<24> taps_180k_wfm_decim_0 = {
.low_frequency_normalized = -90000.0f / 3072000.0f,
.high_frequency_normalized = 90000.0f / 3072000.0f,
.transition_normalized = 160000.0f / 3072000.0f,
.taps = { {
55, 122, 244, 424, 666, 965, 1308, 1669,
2019, 2321, 2544, 2663, 2663, 2544, 2321, 2019,
1669, 1308, 965, 666, 424, 244, 122, 55,
} },
};
// IFIR prototype filter: fs=768000, pass=90000, stop=110000, decim=2, fout=384000
constexpr fir_taps_real<16> taps_180k_wfm_decim_1 = {
.low_frequency_normalized = -90000.0f / 768000.0f,
.high_frequency_normalized = 90000.0f / 768000.0f,
.transition_normalized = 20000.0f / 768000.0f,
.taps = { {
55, 19, -356, -916, -529, 2139, 6695, 10392,
10392, 6695, 2139, -529, -916, -356, 19, 55,
} },
};
// WFM 40kHZ filter for NOAA APT reception in 137Mhz band with sharp transition //////////////////////////////////////////////
// IFIR image-reject filter: fs=3072000, pass=20000, stop=97000, decim=4, fout=768000
constexpr fir_taps_real<24> taps_40k_wfm_decim_0 = {
.low_frequency_normalized = -20000.0f / 3072000.0f,
.high_frequency_normalized = 20000.0f / 3072000.0f,
.transition_normalized = 67000.0f / 3072000.0f,
.taps = { {
46, 112, 230, 408, 650, 953, 1301, 1671,
2029, 2340, 2570, 2692, 2692, 2570, 2340, 2029,
1671, 1301, 953, 650, 408, 230, 112, 46,
} },
};
// IFIR prototype filter: fs=768000, pass=20000, stop=55000, decim=2, fout=384000
constexpr fir_taps_real<16> taps_40k_wfm_decim_1 = {
.low_frequency_normalized = -20000.0f / 768000.0f,
.high_frequency_normalized = 20000.0f / 768000.0f,
.transition_normalized = 35000.0f / 768000.0f,
.taps = { {
83, 299, 743, 1456, 2396, 3418, 4297, 4808,
4808, 4297, 3418, 2396, 1456, 743, 299, 83,
} },
};
// TPMS decimation filters ////////////////////////////////////////////////
// IFIR image-reject filter: fs=2457600, pass=100000, stop=407200, decim=4, fout=614400
+142 -2
View File
@@ -1143,9 +1143,18 @@ NewButton::NewButton(
Rect parent_rect,
std::string text,
const Bitmap* bitmap
) : NewButton { parent_rect, text, bitmap, Color::dark_cyan() }
{ }
NewButton::NewButton(
Rect parent_rect,
std::string text,
const Bitmap* bitmap,
Color color
) : Widget { parent_rect },
text_ { text },
bitmap_ (bitmap)
bitmap_ { bitmap },
color_ { color }
{
set_focusable(true);
}
@@ -1535,6 +1544,137 @@ bool OptionsField::on_touch(const TouchEvent event) {
return true;
}
/* TextField ***********************************************************/
TextField::TextField(
std::string& str,
size_t max_length,
Point position,
uint32_t length
) : Widget{ { position, { 8 * static_cast<int>(length), 16 } } },
text_{ str },
max_length_{ std::max<size_t>(max_length, str.length()) },
char_count_{ std::max<uint32_t>(length, 1) },
cursor_pos_{ text_.length() },
insert_mode_{ true }
{
set_focusable(true);
}
const std::string& TextField::value() const {
return text_;
}
void TextField::set_cursor(uint32_t pos) {
cursor_pos_ = std::min<size_t>(pos, text_.length());
set_dirty();
}
void TextField::set_insert_mode() {
insert_mode_ = true;
}
void TextField::set_overwrite_mode() {
insert_mode_ = false;
}
void TextField::char_add(char c) {
// Don't add if inserting and at max_length and
// don't overwrite if past the end of the text.
if ((text_.length() >= max_length_ && insert_mode_) ||
(cursor_pos_ >= text_.length() && !insert_mode_))
return;
if (insert_mode_)
text_.insert(cursor_pos_, 1, c);
else
text_[cursor_pos_] = c;
cursor_pos_++;
set_dirty();
}
void TextField::char_delete() {
if (cursor_pos_ == 0)
return;
cursor_pos_--;
text_.erase(cursor_pos_, 1);
set_dirty();
}
void TextField::paint(Painter& painter) {
constexpr int char_width = 8;
auto rect = screen_rect();
auto text_style = has_focus() ? style().invert() : style();
auto offset = 0;
// Does the string need to be shifted?
if (cursor_pos_ >= char_count_)
offset = cursor_pos_ - char_count_ + 1;
// Clear the control.
painter.fill_rectangle(rect, text_style.background);
// Draw the text starting at the offset.
for (uint32_t i = 0; i < char_count_ && i + offset < text_.length(); i++) {
painter.draw_char(
{ rect.location().x() + (static_cast<int>(i) * char_width), rect.location().y() },
text_style,
text_[i + offset]
);
}
// Determine cursor position on screen (either the cursor position or the last char).
int32_t cursor_x = char_width * (offset > 0 ? char_count_ - 1 : cursor_pos_);
Point cursor_point{ screen_pos().x() + cursor_x, screen_pos().y() };
auto cursor_style = text_style.invert();
// Invert the cursor character when in overwrite mode.
if (!insert_mode_ && (cursor_pos_) < text_.length())
painter.draw_char(cursor_point, cursor_style, text_[cursor_pos_]);
// Draw the cursor.
Rect cursor_box{ cursor_point, { char_width, 16 } };
painter.draw_rectangle(cursor_box, cursor_style.background);
}
bool TextField::on_key(const KeyEvent key) {
if (key == KeyEvent::Left && cursor_pos_ > 0)
cursor_pos_--;
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
cursor_pos_++;
else if (key == KeyEvent::Select)
insert_mode_ = !insert_mode_;
else
return false;
set_dirty();
return true;
}
bool TextField::on_encoder(const EncoderEvent delta) {
int32_t new_pos = cursor_pos_ + delta;
// Let the encoder wrap around the ends of the text.
if (new_pos < 0)
new_pos = text_.length();
else if (static_cast<size_t>(new_pos) > text_.length())
new_pos = 0;
set_cursor(new_pos);
return true;
}
bool TextField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start)
focus();
set_dirty();
return true;
}
/* NumberField ***********************************************************/
NumberField::NumberField(
@@ -1560,7 +1700,7 @@ int32_t NumberField::value() const {
void NumberField::set_value(int32_t new_value, bool trigger_change) {
if (can_loop) {
if (new_value >= 0)
if (new_value >= range.first)
new_value = new_value % (range.second + 1);
else
new_value = range.second + new_value + 1;
+48 -4
View File
@@ -414,7 +414,6 @@ private:
bool instant_exec_ { false };
};
class ButtonWithEncoder : public Widget {
public:
std::function<void(ButtonWithEncoder&)> on_select { };
@@ -457,8 +456,6 @@ private:
bool instant_exec_ { false };
};
class NewButton : public Widget {
public:
std::function<void(void)> on_select { };
@@ -469,6 +466,7 @@ public:
NewButton(const NewButton&) = delete;
NewButton& operator=(const NewButton&) = delete;
NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap);
NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap, Color color);
NewButton(
) : NewButton { { }, { }, { } }
{
@@ -489,8 +487,8 @@ public:
private:
std::string text_;
Color color_ = Color::dark_cyan();
const Bitmap* bitmap_;
Color color_;
};
class Image : public Widget {
@@ -610,6 +608,52 @@ private:
size_t selected_index_ { 0 };
};
// A TextField is bound to a string reference and allows the string
// to be manipulated. The field itself does not provide the UI for
// setting the value. It provides the UI of rendering the text,
// a cursor, and an API to edit the string content.
class TextField : public Widget {
public:
TextField(std::string& str, Point position, uint32_t length = 30)
: TextField{str, 64, position, length} { }
// Str: the string containing the content to edit.
// Max_length: max length the string is allowed to use.
// Position: the top-left corner of the control.
// Length: the number of characters to display.
// - Characters are 8 pixels wide.
// - The screen can show 30 characters max.
// - The control is 16 pixels tall.
TextField(std::string& str, size_t max_length, Point position, uint32_t length = 30);
TextField(const TextField&) = delete;
TextField(TextField&&) = delete;
TextField& operator=(const TextField&) = delete;
TextField& operator=(TextField&&) = delete;
const std::string& value() const;
void set_cursor(uint32_t pos);
void set_insert_mode();
void set_overwrite_mode();
void char_add(char c);
void char_delete();
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
protected:
std::string& text_;
size_t max_length_;
uint32_t char_count_;
uint32_t cursor_pos_;
bool insert_mode_;
};
class NumberField : public Widget {
public:
std::function<void(NumberField&)> on_select { };
Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
f=92500000,m=WFM,bw=200k,d=NRK Oslo
f=94800000,m=WFM,bw=200k,d=NRK Bergen
f=97200000,m=WFM,bw=200k,d=NRK Melhus
f=98600000,m=WFM,bw=200k,d=NRK Arendal
@@ -9,7 +9,6 @@ f=91300000,m=WFM,bw=200k,d=RMF Łobez
f=91500000,m=WFM,bw=200k,d=RMF Ostroleka
f=91700000,m=WFM,bw=200k,d=RMF Wagrowiec
f=91900000,m=WFM,bw=200k,d=RMF Siedlce
f=92500000,m=WFM,bw=200k,d=NRK Oslo
f=92600000,m=WFM,bw=200k,d=RMF Elbląg
f=92900000,m=WFM,bw=200k,d=RMF Wroclaw
f=93000000,m=WFM,bw=200k,d=RMF Katowice
@@ -19,7 +18,6 @@ f=93600000,m=WFM,bw=200k,d=RMF Ryki
f=93800000,m=WFM,bw=200k,d=RMF Luban
f=94300000,m=WFM,bw=200k,d=RMF Płock
f=94600000,m=WFM,bw=200k,d=RMF Poznan
f=94800000,m=WFM,bw=200k,d=NRK Bergen
f=94800000,m=WFM,bw=200k,d=RMF Zagan
f=95100000,m=WFM,bw=200k,d=RMF Suwałki
f=95300000,m=WFM,bw=200k,d=RMF Olsztyn
@@ -30,10 +28,8 @@ f=96100000,m=WFM,bw=200k,d=RMF Gorzow Wielkopolski
f=96400000,m=WFM,bw=200k,d=RMF Bialogard
f=96600000,m=WFM,bw=200k,d=RMF Walcz
f=97100000,m=WFM,bw=200k,d=RMF Włoszczowa
f=97200000,m=WFM,bw=200k,d=NRK Melhus
f=98000000,m=WFM,bw=200k,d=RMF Kalisz
f=98400000,m=WFM,bw=200k,d=RMF Gdansk
f=98600000,m=WFM,bw=200k,d=NRK Arendal
f=98900000,m=WFM,bw=200k,d=RMF Konin
f=99300000,m=WFM,bw=200k,d=RMF Opole
f=99500000,m=WFM,bw=200k,d=RMF Kluczbork
+15 -15
View File
@@ -1,6 +1,6 @@
a=150000,b=285000,m=AM,bw=DSB,s=5KHz,d=Longwave broadcast band|BCB (EU)
a=525000,b=1605000,m=AM,bw=DSB,s=5KHz,d=AM broadcast band|BCB (EU & J)
a=530000,b=1710000,m=AM,bw=DSB,s=5KHz,d=AM broadcast band|BCB (US)
a=150000,b=285000,m=AM,bw=DSB,s=5kHz,d=Longwave broadcast band|BCB (EU)
a=525000,b=1605000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (EU & J)
a=530000,b=1710000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (US)
a=1800000,b=29700000,m=WFM,bw=16k,d=Amateur_radio|Amateur
a=26900000,b=27400000,m=WFM,bw=16k,d=Citizens_band_radio|Citizens band
a=28000000,b=30000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
@@ -8,15 +8,15 @@ a=29000000,b=54000000,m=WFM,bw=16k,d=Land mobile
a=50000000,b=54000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
a=65000000,b=85000000,m=WFM,bw=16k,d=Land mobile (EU)
a=108000000,b=136000000,m=WFM,bw=16k,d=Aircraft
a=120000000,b=160000000,m=WFM,bw=16k,s=50KHz,d=Land mobile (EU)
a=132000000,b=174000000,m=WFM,bw=16k,s=50KHz,d=Land mobile
a=142000000,b=170000000,m=WFM,bw=16k,s=50KHz,d=Land mobile (J)
a=144000000,b=148000000,m=WFM,bw=16k,s=50KHz,d=Amateur_radio|Amateur
a=216000000,b=222000000,m=WFM,bw=16k,s=50KHz,d=Land mobile
a=222000000,b=225000000,m=WFM,bw=16k,s=50KHz,d=Amateur_radio|Amateur
a=335000000,b=384000000,m=WFM,bw=16k,s=50KHz,d=Land mobile (J)
a=406000000,b=512000000,m=WFM,bw=16k,s=50KHz,d=Land mobile
a=450000000,b=470000000,m=WFM,bw=16k,s=50KHz,d=Land mobile (J)
a=430000000,b=450000000,m=WFM,bw=16k,s=50KHz,d=Amateur_radio|Amateur
a=806000000,b=947000000,m=WFM,bw=16k,s=50KHz,d=Land mobile
a=1200000000,b=1600000000,m=WFM,bw=16k,s=50KHz,d=Amateur|Land mobile|GPS
a=120000000,b=160000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (EU)
a=132000000,b=174000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
a=142000000,b=170000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
a=144000000,b=148000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
a=216000000,b=222000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
a=222000000,b=225000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
a=335000000,b=384000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
a=406000000,b=512000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
a=450000000,b=470000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
a=430000000,b=450000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
a=806000000,b=947000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
a=1200000000,b=1600000000,m=WFM,bw=16k,s=50kHz,d=Amateur|Land mobile|GPS
+36 -7
View File
@@ -2,21 +2,50 @@
# (Freq range in MHz, min separation 3MHz)
# (Description up to 20 char)
# (fields comma delimiter)
140,380,VHF MICS AND MARINE
260,500,315/433 MHz KEYFOBS
390,420,RADIOSONDES
420,660,UHF MICS
# WATER METERS
850,900,Water meters
902,928,ISM 900MHz
# PAGERS https://www.fcc.gov/wireless/bureau-divisions/mobility-division/paging
34,36,USA PAGERS 35
43,45,USA PAGERS 44
151,153,USA PAGERS 152
453,454,USA PAGERS 454
929,931,USA PAGERS 930
# KEYFOBS
260,500,315/433 MHz KEYFOBS
# MICS, RADIOSONDES, LEYFOB
140,380,VHF MICS AND MARINE
390,420,RADIOSONDES
420,660,UHF MICS
# LoRa
433,435,LoRa 443EU
863,870,LoRa 868EU
902,928,LoRa 915US
779,787,LoRa 780CN
# BROADCAST RADIO
65,74,FM BROADCAST RUSSIA
76,95,FM BROADCAST JAPAN
76,108,FM BROADCAST BRAZIL
87,108,FM BROADCAST
# AVIATION
108,137,AVIATION 108
960,1215,AVIATION 960
# HAM RADIO
28,30,HAM 10-METER
50,54,HAM 6-METER
144,148,HAM 2-METER
219,225,HAM 1.25-METER
420,450,HAM 70-CM
902,928,HAM 33-CM
1240,1300,HAM 23-CM
# WATER METERS
850,900,Water meters
902,928,ISM 900MHz
# WIFI
2320,2560,BL / WIFI 2.4GHz
5160,5900,WIFI 5GHz
# ITU-DEFINED BANDS
1,3,MF BAND
3,30,HF BAND
30,300,VHF BAND
300,3000,UHF BAND
3000,7250,SHF BAND
# FULL
10,7250,FULL RANGE
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
sample_rate=500000
center_frequency=433920000
+11
View File
@@ -0,0 +1,11 @@
# Aurora RGB
More info: [Aurora](https://www.aurora-gaming.pl/portfolio-item/passion-v5/)
Captured by: [nnesetto](https://github.com/nnesetto)
The controller operates at the frequency of 433.92 MHz.
![Remote](remote.jpeg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
![Case](https://www.aurora-gaming.pl/wp-content/uploads/2019/01/1-1-1030x1030.jpg)
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More