Compare commits

...

105 Commits

Author SHA1 Message Date
Brumi-2021 6bb2a3559d Minor change to the Mic App UI , (adding colour warning indication, based on RF settings GAIN , AMP) (#1001)
* Add colour Power_tx UI warning to Mic App
* Cleaning compile warnings
2023-05-17 12:26:14 +02:00
gullradriel 1a829707f4 fixing PR comments (#991)
* cleanings, factorisation, indentation.
2023-05-15 10:50:21 +02:00
Mark Thompson 22430fb8d4 Support frequency files that don't have a C/R at the end of the last line (#997)
* Support FREQ files that don't have 0x0A at end of last line
2023-05-15 09:11:56 +02:00
jLynx acc20844cd Added clang formatting spec and PR validation (#992)
* Update .clang-format
2023-05-15 11:53:13 +12:00
Bernd Herzog e694e73a75 fixed crash when BtnGridView has more than one page (#993)
* fixed crash when BtnGridView has more than one page

* removed empty buttons from view
2023-05-15 00:08:48 +02:00
Bernd Herzog b4da86d491 New app: Spectrum Painter (#988)
* added spectrum painter app
2023-05-14 23:09:43 +02:00
gullradriel a27881ecd6 Recon event nothread (#984)
* deleted unreliable Thread system and use exclusively the statistics events instead
* reducing used variables, reducing number of functions, revamped main drawing and locking system
* max speed is fixed at a maximum of 10 scans/secs due to statistics not coming quicker than that
2023-05-14 21:20:10 +02:00
Mark Thompson 9ce2f3fdea Fixed FREQMAN "Range" files to use kHz vs KHz, added 2 HAM freq files, and use valid BW settings for WFM (#985)
* Added HAM 2M & 70CM band file
* Update BW to a valid value
Valid WFM BW values are 40k,180k,or 200k
* Use 11k for HAM bandwidth (typically 12.5k but not an option currently)
* Changes FM_STANDARD step value to 100kHz per ITU 1984
200kHz is best here in the USA, but I understand that 100kHz is better in the EU
2023-05-14 13:55:41 +02:00
gullradriel 2a4c49c38c Freqman 40k fix (#987)
* removing space which would prevent correct reading of 'bw=40k,' in freqman files

* setting correct 4 char long size in WFM bandwidth options

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-05-14 19:16:01 +12:00
gullradriel 415d8cfa54 fixing indent (#982)
resolving conversations in #968
2023-05-13 08:56:29 +02:00
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
jLynx 8ba5715db0 Update version.txt 2023-05-06 08:03:59 +12:00
jLynx c7e8506ad0 Update past_version.txt 2023-05-06 08:03:44 +12:00
gullradriel 8cb0a91bb5 Merge pull request #958 from gullradriel/recon-skip-fix
fix skip consecutive match
2023-05-05 17:03:00 +02:00
GullCode 1bcbefeb96 fix skip consecutive match 2023-05-05 17:01:08 +02:00
gullradriel 31031edbd1 Merge pull request #956 from bernd-herzog/boot_improvements
removed need for cpld mode setup for QFP100
2023-05-05 15:37:58 +02:00
bernd-herzog 7116f92d07 fixed spelling 2023-05-05 13:39:04 +02:00
bernd-herzog df8e79f9e6 refactoring 2023-05-05 12:58:28 +02:00
bernd-herzog e80e4e3bfd refactoring 2023-05-05 12:58:14 +02:00
bernd-herzog da6c6bb03c refactoring 2023-05-05 12:46:59 +02:00
bernd-herzog 75718c79b9 removed need for cpld mode setup for QFP100 2023-05-05 11:15:49 +02:00
gullradriel 9b263def37 Merge pull request #954 from gullradriel/recon-fix
Recon fix
2023-05-04 15:27:47 +02:00
GullCode c7d88da1a6 restoring bad substitue on text_ctcss filler 2023-05-04 15:26:33 +02:00
GullCode 7b6f8b271c Fixed so ui glitches, start when empty file and direct click on config. Added yellow coloration when loaded file was truncated (too big). Added/changed some error messages 2023-05-04 15:20:12 +02:00
GullCode 1b18b3ac45 fixing limit so we can detect if a list is the same size as the limit or truncated 2023-05-04 15:19:13 +02:00
gullradriel 68fc2a143f Merge pull request #953 from gullradriel/freqman-limits
fixing freqman limits
2023-05-04 12:11:15 +02:00
GullCode 1deebaff09 fixing freqman limits 2023-05-04 12:09:42 +02:00
gullradriel d6118c9fc4 Merge pull request #951 from kallanreed/fileloadview-fix
Fix nav.pop order on FileLoadView
2023-05-04 07:43:54 +02:00
Kyle Reed 44a62aef21 Fix nav.pop order on FileLoadView 2023-05-03 15:29:01 -07:00
gullradriel 0742fc169d Merge pull request #950 from kallanreed/fileman_ux2
Fileman fit and finish
2023-05-03 22:32:00 +02:00
Kyle Reed 62859f901f Fix indents 2023-05-03 13:23:15 -07:00
Kyle Reed c6316f5aa6 Fileman fit and finish 2023-05-03 13:13:15 -07:00
gullradriel 5f2043d229 Merge pull request #949 from gullradriel/pmem-defaults
Pmem defaults
2023-05-03 19:51:47 +02:00
gullradriel 074658c4bb Merge branch 'eried:next' into pmem-defaults 2023-05-03 19:51:15 +02:00
GullCode d641ae5b47 Fixed size of things 2023-05-03 19:50:12 +02:00
gullradriel bec70b4ee5 Merge pull request #948 from gullradriel/pmem-defaults
Pmem defaults
2023-05-03 19:48:05 +02:00
GullCode c4373d1560 indentation 2023-05-03 19:45:01 +02:00
GullCode 1dae2c0d25 indentation 2023-05-03 19:44:08 +02:00
GullCode 658d0c9b3a Added reset persitent memory 2023-05-03 19:42:57 +02:00
gullradriel 2f343adf21 Merge pull request #941 from kallanreed/fileman_ux
Fileman UI and Perf Fixes
2023-05-03 17:13:05 +02:00
gullradriel f7a5f2c437 Merge pull request #946 from Brumi-2021/noise_Signal_Generator_LFSR_polynomial_of_16_32_bits
Leaving only 1 Noise signal generator GUI option , (the best one , using  LFSR polynomial of 16 bits)
2023-05-03 13:57:06 +02:00
Brumi-2021 5a336d5e71 Compact with unique, best Noise Gererator option. 2023-05-03 13:49:50 +02:00
gullradriel 91675e26cb Merge pull request #945 from bernd-herzog/touchscreen_multiinput_fix
fixed touch handling issue causing multiple inputs
2023-05-03 11:43:34 +02:00
bernd-herzog 078da8ca16 Improved sensitivity for non pointy touches 2023-05-03 11:18:37 +02:00
bernd-herzog 77260bc68a fixed touch handling issue causing multiple inputs 2023-05-03 10:47:43 +02:00
gullradriel ca143f9788 Merge pull request #944 from joyel24/meteomodem-M20-radiosondes-altitude
Add Meteomodem M20 radiosondes altitude
2023-05-03 06:31:47 +02:00
Kyle Reed 3fc23354ce Fix path separator, revert UI 2023-05-02 20:49:41 -07:00
joyel24 d48e25167f fix altitude inaccuracy 2023-05-03 05:37:31 +02:00
joyel24 cfe5a6bfe4 Merge branch 'eried:next' into meteomodem-M20-radiosondes-altitude 2023-05-03 02:09:20 +02:00
Brumi-2021 d77102426a Finally Noise Signal with best option 16 bit LFSR 2023-05-02 21:29:35 +02:00
Kyle Reed 11f4edc892 Modal dialog for partner file action, fix lifetime 2023-05-02 09:38:08 -07:00
Brumi-2021 804fa0d3c4 Minor GUI corrections-Signal Generator 2023-05-02 17:41:00 +02:00
Brumi-2021 d5f20c45b9 Adding 16 bit LFSR , to Noise Signal Generator App 2023-05-02 17:19:23 +02:00
Kyle Reed 2cba96ff36 Support for partner file rename/delete 2023-05-01 09:25:32 -07:00
Joel M 7139abb947 alt divided by 100 2023-05-01 16:54:29 +02:00
Kyle Reed bf4ed416bd Remove expensive path.string() calls, UI changes 2023-04-30 22:42:28 -07:00
gullradriel 06643df6a5 Merge pull request #938 from gullradriel/pmemsettings-create-folder
Added SETTINGS folder creating for PMem files
2023-04-30 18:42:51 +02:00
GullCode 81752c8d78 Added SETTINGS folder creating for PMem files 2023-04-30 18:41:24 +02:00
gullradriel 0cb8c9200a Merge pull request #937 from gullradriel/hackrf-flashing-tool-update
Updating to release 2023.01.1
2023-04-30 18:27:41 +02:00
GullCode 8e64221384 Updating to release 2023.01.1 https://github.com/greatscottgadgets/hackrf/releases/tag/v2023.01.1 2023-04-30 18:26:22 +02:00
gullradriel fe1837ec23 Merge pull request #936 from Brumi-2021/solving_bug_in_noise_option_Signal_Generator_tool
Solving Noise generation in Signal gen App
2023-04-30 17:33:18 +02:00
Brumi-2021 ee53b28e60 Solving Noise generation in Signal gen App 2023-04-30 18:03:40 +02:00
gullradriel 18c986cd76 Merge pull request #931 from kallanreed/cursor_text_input
Add a TextField widget that supports a cursor.
2023-04-30 17:28:42 +02:00
gullradriel a1c3cbcea9 Merge pull request #935 from gullradriel/looking-glass-update
Looking glass update
2023-04-30 15:01:57 +02:00
GullCode 1135a42932 better roundings/per pixel marker 2023-04-30 14:56:17 +02:00
Joel M a74d0e5167 altitude 3 bytes 2023-04-30 13:59:21 +02:00
gullradriel c5579e25fd Merge pull request #932 from joyel24/meteomodem-M20-radiosondes
Identifying meteomodem m20 radiosondes & extract GNSS location
2023-04-30 13:25:42 +02:00
Joel M e8b8f0ca5c Disable inaccurate altitude for the moment 2023-04-30 12:06:37 +02:00
GullCode 98e7116230 Changed to per pixel marker, short freq display, adjusted <24Mhz view to look like SPEC one 2023-04-29 21:46:19 +02:00
Joel M c916eaf43f Detects M20 radiosondes and decode GNSS location 2023-04-29 14:00:28 +02:00
Joel M e3169a3495 add Meteomodem_M20 to packet class in sonde_packet.hpp 2023-04-29 13:49:27 +02:00
Kyle Reed 4b37f1bb2f Add a TestField widget that supports a cursor. 2023-04-28 19:26:53 -07:00
gullradriel cb93f2636e Merge pull request #929 from gullradriel/freq-disp-fix
Freq disp fix
2023-04-28 13:01:12 +02:00
GullCode 596c304b08 Took out unused func declaration 2023-04-28 12:59:51 +02:00
GullCode 63f7be02b6 Used correct to_string_short_freq instead of erroneous calculation 2023-04-28 12:57:00 +02:00
GullCode de99afa19b Took out unused func declaration 2023-04-28 12:55:58 +02:00
gullradriel 5c4e27ea29 Merge pull request #927 from gullradriel/dsp-warning-fix
Dsp warning fix
2023-04-27 16:08:18 +02:00
GullCode 54e4230191 indentation 2023-04-27 16:07:57 +02:00
GullCode f069383c2a Fix unused warnings in ssb_execute 2023-04-27 16:06:47 +02:00
gullradriel d3a356d415 Merge pull request #926 from gullradriel/gitignore-fix
restoring gitignore
2023-04-27 15:51:54 +02:00
GullCode f0e4fdcbcc restoring gitignore, bad move on last commit 2023-04-27 15:50:59 +02:00
ittercho d5f6f42fae Addition of water meters (#892)
Water Meters frequency list
NRK and RMF radio stations frequencies list
Fixed and ordered as much as possible PRESETS.TXT
Co-authored-by: gullradriel
2023-04-27 15:44:32 +02:00
gullradriel 7a89858cc6 Merge pull request #925 from gullradriel/level-mem-workaround
Automatically reduce rssi graph history
2023-04-27 15:42:06 +02:00
gullradriel 15c9a76536 Merge pull request #924 from bernd-herzog/dfu_menu
added frame drop rate for m4 signal processing
2023-04-27 15:41:46 +02:00
GullCode d7359a8cd5 Automatically reduce rssi graph history when hidden, restore size on show. Prevent Memory exhaustion in apps like 'Level' or any app eating a bit too much mem before launching a FrequencyPadView 2023-04-27 15:38:29 +02:00
Bernd Herzog 50e5bc60ee added frame miss rate for m4 signal processing 2023-04-26 22:28:14 +02:00
165 changed files with 4647 additions and 2352 deletions
+7
View File
@@ -0,0 +1,7 @@
---
BasedOnStyle: Chromium
IndentWidth: '2'
TabWidth: '2'
UseTab: Always
...
+18
View File
@@ -0,0 +1,18 @@
name: Check formatting
on: #[push, pull_request] To be enabled once formatting is fixed in repo
workflow_dispatch:
jobs:
check_date:
runs-on: ubuntu-latest
name: Check formatting
steps:
- uses: actions/checkout@v2
- name: print latest_commit
run: echo ${{ github.sha }}
- name: clang-format Check
uses: jidicula/clang-format-action@v4.11.0
with:
check-path: "firmware"
fallback-style: Chromium
+1 -1
View File
@@ -1 +1 @@
v1.5.4
v1.6.0
+1 -1
View File
@@ -1 +1 @@
v1.6.0
v1.7.0
+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
+3
View File
@@ -281,6 +281,9 @@ set(CPPSRC
apps/ui_level.cpp
apps/tpms_app.cpp
apps/tpms_app.cpp
apps/ui_spectrum_painter.cpp
apps/ui_spectrum_painter_image.cpp
apps/ui_spectrum_painter_text.cpp
protocols/aprs.cpp
protocols/ax25.cpp
protocols/bht.cpp
+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 },
4,
{ // 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
+19 -16
View File
@@ -29,6 +29,23 @@
#include "ui_record_view.hpp"
#include "ui_spectrum.hpp"
#define BW_OPTIONS \
{ " 8k5", 8500 }, \
{ " 11k", 11000 }, \
{ " 16k", 16000 }, \
{ " 25k", 25000 }, \
{ " 50k", 50000 }, \
{ " 100k", 100000 }, \
{ " 250k", 250000 }, \
{ " 500k", 500000 }, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
{ " 600k", 600000 }, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
{ " 750k", 750000 }, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \
{ "1100k", 1100000 }, \
{ "1750k", 1750000 }, \
{ "2000k", 2000000 }, \
{ "2500k", 2500000 }, \
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
namespace ui {
class CaptureAppView : public View {
@@ -88,27 +105,13 @@ private:
{ 5 * 8, 1 * 16 },
5,
{
{ " 8k5", 8500 },
{ " 11k ", 11000 },
{ " 16k ", 16000 },
{ " 25k ", 25000 },
{ " 50k ", 50000 },
{ "100k ", 100000 },
{ "250k ", 250000 },
{ "500k ", 500000 }, // Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.
{ "600k ", 600000 }, // That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec)
{ "750k ", 750000 }, // From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size)
{ "1100k", 1100000 },
{ "1750k", 1750000 },
{ "2000k", 2000000 },
{ "2500k", 2500000 },
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
BW_OPTIONS
}
};
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
@@ -206,7 +206,7 @@ void SoundBoardView::refresh_list() {
file_list[n].string().substr(0, 30),
ui::Color::white(),
nullptr,
[this](){
[this](KeyEvent){
on_select_entry();
}
});
+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));
}
}
+10 -9
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 },
};
Checkbox check_log {
{ 0 * 8, 1 * 16 },
3,
"LOG",
false
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
{ 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 }
};
+9 -6
View File
@@ -35,7 +35,8 @@ DfuMenu::DfuMenu(NavigationView& nav) : nav_ (nav) {
&text_info_line_4,
&text_info_line_5,
&text_info_line_6,
&text_info_line_7
&text_info_line_7,
&text_info_line_8
});
}
@@ -48,14 +49,16 @@ void DfuMenu::paint(Painter& painter) {
text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_cpu_usage, 6));
text_info_line_7.set(to_string_dec_uint(chTimeNow()/1000, 6));
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_8.set(to_string_dec_uint(chTimeNow()/1000, 6));
constexpr auto margin = 5;
constexpr auto lines = 8 + 2;
painter.fill_rectangle(
{
{6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{15 * CHARACTER_WIDTH + margin * 2, 9 * LINE_HEIGHT + margin * 2}
{15 * CHARACTER_WIDTH + margin * 2, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::black()
);
@@ -63,7 +66,7 @@ void DfuMenu::paint(Painter& painter) {
painter.fill_rectangle(
{
{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, 9 * LINE_HEIGHT + margin * 2}
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::dark_cyan()
);
@@ -71,7 +74,7 @@ void DfuMenu::paint(Painter& painter) {
painter.fill_rectangle(
{
{21 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, 9 * LINE_HEIGHT + margin * 2}
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::dark_cyan()
);
@@ -86,7 +89,7 @@ void DfuMenu::paint(Painter& painter) {
painter.fill_rectangle(
{
{5 * CHARACTER_WIDTH - margin, 12 * LINE_HEIGHT + margin},
{5 * CHARACTER_WIDTH - margin, (lines+3) * LINE_HEIGHT + margin},
{17 * CHARACTER_WIDTH + margin * 2, 8}
},
ui::Color::dark_cyan()
+3 -1
View File
@@ -54,7 +54,8 @@ private:
{ { 6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT }, "M4 heap:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT }, "M4 stack:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH,10 * LINE_HEIGHT }, "M4 cpu %:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH,11 * LINE_HEIGHT }, "uptime:", Color::dark_cyan() }
{ { 6 * CHARACTER_WIDTH,11 * LINE_HEIGHT }, "M4 miss:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH,12 * LINE_HEIGHT }, "uptime:", Color::dark_cyan() }
};
Text text_info_line_1 {{ 15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
@@ -64,6 +65,7 @@ private:
Text text_info_line_5 {{ 15 * CHARACTER_WIDTH, 9 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
Text text_info_line_6 {{ 15 * CHARACTER_WIDTH,10 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
Text text_info_line_7 {{ 15 * CHARACTER_WIDTH,11 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
Text text_info_line_8 {{ 15 * CHARACTER_WIDTH,12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
};
} /* namespace ui */
+467 -243
View File
@@ -20,83 +20,199 @@
* Boston, MA 02110-1301, USA.
*/
/* TODO:
* - Paging menu items
*/
#include <algorithm>
#include "ui_fileman.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
using namespace portapack;
namespace fs = std::filesystem;
namespace {
using namespace ui;
bool is_hidden_file(const fs::path& path) {
return !path.empty() && path.native()[0] == u'.';
}
// Gets a truncated name from a path for display.
std::string truncate(const fs::path& path, size_t max_length) {
return ::truncate(path.string(), max_length);
}
// Gets a human readable file size string.
std::string get_pretty_size(uint32_t file_size) {
static const std::string suffix[5] = { "B", "kB", "MB", "GB", "??" };
size_t suffix_index = 0;
while (file_size >= 1024) {
file_size /= 1024;
suffix_index++;
}
if (suffix_index > 4)
suffix_index = 4;
return to_string_dec_uint(file_size) + suffix[suffix_index];
}
// Case insensitive path equality on underlying "native" string.
bool iequal(
const fs::path& lhs,
const fs::path& rhs
) {
const auto& lhs_str = lhs.native();
const auto& rhs_str = rhs.native();
// NB: Not correct for Unicode/locales.
if (lhs_str.length() == rhs_str.length()) {
for (size_t i = 0; i < lhs_str.length(); ++i)
if (towupper(lhs_str[i]) != towupper(rhs_str[i]))
return false;
return true;
}
return false;
}
// Inserts the entry into the entry list sorted directories first then by file name.
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
auto it = std::lower_bound(std::begin(entries), std::end(entries), entry,
[](const fileman_entry& lhs, const fileman_entry& rhs) {
if (lhs.is_directory && !rhs.is_directory)
return true;
else if (!lhs.is_directory && rhs.is_directory)
return false;
else
return lhs.path < rhs.path;
});
entries.insert(it, std::move(entry));
}
// Returns the partner file path or an empty path if no partner is found.
fs::path get_partner_file(fs::path path) {
if (fs::is_directory(path))
return { };
const fs::path txt_path{ u".TXT" };
const fs::path c16_path{ u".C16" };
auto ext = path.extension();
if (iequal(ext, txt_path))
ext = c16_path;
else if (iequal(ext, c16_path))
ext = txt_path;
else
return { };
path.replace_extension(ext);
return fs::file_exists(path) && !fs::is_directory(path) ? path : fs::path{ };
}
// Modal prompt to update the partner file if it exists.
// Runs continuation on_partner_action to update the partner file.
// Returns true is a partner is found, otherwise false.
// Path must be the full path to the file.
bool partner_file_prompt(
NavigationView& nav,
const fs::path& path,
std::string action_name,
std::function<void(const fs::path&, bool)> on_partner_action
) {
auto partner = get_partner_file(path);
if (partner.empty())
return false;
nav.push_under_current<ModalMessageView>(
"Partner File",
partner.filename().string() + "\n" + action_name + " this file too?",
YESNO,
[&nav, partner, on_partner_action](bool choice) {
if (on_partner_action)
on_partner_action(partner, choice);
}
);
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 {
void FileManBaseView::load_directory_contents(const std::filesystem::path& dir_path) {
/* FileManBaseView ***********************************************************/
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
current_path = dir_path;
text_current.set(dir_path.string().length()? dir_path.string().substr(0, 30 - 6):"(sd root)");
entry_list.clear();
auto filtering = (bool)extension_filter.size();
// List directories and files, put directories up top
if (dir_path.string().length())
entry_list.push_back({ u"..", 0, true });
auto filtering = !extension_filter.empty();
for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*")) {
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
// Hide files starting with '.' (hidden / tmp).
if (is_hidden_file(entry.path()))
continue;
// do not display dir / files starting with '.' (hidden / tmp)
if (entry.path().string().length() && entry.path().filename().string()[0] != '.') {
if (std::filesystem::is_regular_file(entry.status())) {
bool matched = true;
if (filtering) {
auto entry_extension = entry.path().extension().string();
for (auto &c: entry_extension)
c = toupper(c);
if (entry_extension != extension_filter)
matched = false;
}
if (matched)
entry_list.push_back({ entry.path(), (uint32_t)entry.size(), false });
} else if (std::filesystem::is_directory(entry.status())) {
entry_list.insert(entry_list.begin(), { entry.path(), 0, true });
}
if (fs::is_regular_file(entry.status())) {
if (!filtering || iequal(entry.path().extension(), extension_filter))
insert_sorted(entry_list, { entry.path(), (uint32_t)entry.size(), false });
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, { entry.path(), 0, true });
}
}
// Add "parent" directory if not at the root.
if (!dir_path.empty())
entry_list.insert(entry_list.begin(), { parent_dir_path, 0, true });
}
std::filesystem::path FileManBaseView::get_selected_path() {
auto selected_path_str = current_path.string();
auto entry_path = entry_list[menu_view.highlighted_index()].entry_path.string();
if (entry_path == "..") {
selected_path_str = get_parent_dir().string();
} else {
if (selected_path_str.back() != '/')
selected_path_str += '/';
selected_path_str += entry_path;
}
return selected_path_str;
fs::path FileManBaseView::get_selected_full_path() const {
if (get_selected_entry().path == parent_dir_path)
return current_path.parent_path();
return current_path / get_selected_entry().path;
}
std::filesystem::path FileManBaseView::get_parent_dir() {
auto current_path_str = current_path.string();
return current_path.string().substr(0, current_path_str.find_last_of('/'));
const fileman_entry& FileManBaseView::get_selected_entry() const {
// TODO: return reference to an "empty" entry on OOB?
return entry_list[menu_view.highlighted_index()];
}
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
});
@@ -105,121 +221,117 @@ FileManBaseView::FileManBaseView(
};
if (!sdcIsCardInserted(&SDCD1)) {
empty_root=true;
empty_ = EmptyReason::NoSDC;
text_current.set("NO SD CARD!");
return;
}
load_directory_contents(current_path);
if (!entry_list.size()) {
empty_ = EmptyReason::NoFiles;
text_current.set("EMPTY SD CARD!");
} else {
load_directory_contents(current_path);
if (!entry_list.size())
{
empty_root = true;
text_current.set("EMPTY SD CARD!");
} else {
menu_view.on_left = [&nav, this]() {
load_directory_contents(get_parent_dir());
refresh_list();
};
}
menu_view.on_left = [this]() {
pop_dir();
};
}
}
void FileManBaseView::focus() {
if (empty_root) {
if (empty_ != EmptyReason::NotEmpty) {
button_exit.focus();
} else {
menu_view.focus();
}
}
void FileManBaseView::push_dir(const fs::path& path) {
if (path == parent_dir_path) {
pop_dir();
} else {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current();
}
}
void FileManBaseView::pop_dir() {
if (saved_index_stack.empty())
return;
current_path = current_path.parent_path();
reload_current();
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
}
void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
auto prev_highlight = menu_view.highlighted_index();
menu_view.clear();
for (size_t n = 0; n < entry_list.size(); n++) {
auto entry = &entry_list[n];
auto entry_name = entry->entry_path.filename().string().substr(0, 20);
if (entry->is_directory) {
for (const auto& entry : entry_list) {
auto entry_name = truncate(entry.path, 20);
if (entry.is_directory) {
menu_view.add_item({
entry_name,
ui::Color::yellow(),
&bitmap_icon_dir,
[this](){
[this](KeyEvent key) {
if (on_select_entry)
on_select_entry();
on_select_entry(key);
}
});
} else {
auto file_size = entry->size;
size_t suffix_index = 0;
while (file_size >= 1024) {
file_size /= 1024;
suffix_index++;
}
if (suffix_index > 4)
suffix_index = 4;
std::string size_str = to_string_dec_uint(file_size) + suffix[suffix_index];
auto entry_extension = entry->entry_path.extension().string();
for (auto &c: entry_extension)
c = toupper(c);
// Associate extension to icon and color
size_t c;
for (c = 0; c < file_types.size() - 1; c++) {
if (entry_extension == file_types[c].extension)
break;
}
const auto& assoc = get_assoc(entry.path.extension());
auto size_str = get_pretty_size(entry.size);
menu_view.add_item({
entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
file_types[c].color,
file_types[c].icon,
[this](){
assoc.color,
assoc.icon,
[this](KeyEvent key) {
if (on_select_entry)
on_select_entry();
on_select_entry(key);
}
});
}
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
}
menu_view.set_highlighted(0); // Refresh
text_info.set(menu_view.item_count() >= max_items_shown ? "Too many files!" : "");
menu_view.set_highlighted(prev_highlight);
}
/*void FileSaveView::on_save_name() {
text_prompt(nav_, &filename_buffer, 8, [this](std::string * buffer) {
nav_.pop();
});
void FileManBaseView::reload_current() {
load_directory_contents(current_path);
refresh_list();
}
FileSaveView::FileSaveView(
NavigationView& nav
) : FileManBaseView(nav)
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const
{
name_buffer.clear();
add_children({
&text_save,
&button_save_name,
&live_timestamp
});
button_save_name.on_select = [this, &nav](Button&) {
on_save_name();
};
}*/
size_t index = 0;
void FileLoadView::refresh_widgets(const bool v) {
(void)v; //avoid unused warning
set_dirty();
for (; index < file_types.size() - 1; ++index)
if (iequal(ext, file_types[index].extension))
return file_types[index];
// Default to last entry in the list.
return file_types[index];
}
/* FileLoadView **************************************************************/
FileLoadView::FileLoadView(
NavigationView& nav,
std::string filter
@@ -238,156 +350,268 @@ FileLoadView::FileLoadView(
refresh_list();
on_select_entry = [&nav, this]() {
if (entry_list[menu_view.highlighted_index()].is_directory) {
load_directory_contents(get_selected_path());
refresh_list();
on_select_entry = [this](KeyEvent) {
if (get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else {
nav_.pop();
if (on_changed)
on_changed(current_path.string() + '/' + entry_list[menu_view.highlighted_index()].entry_path.string());
on_changed(get_selected_full_path());
}
};
}
void FileManagerView::on_rename(NavigationView& nav) {
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
std::string destination_path = current_path.string();
if (destination_path.back() != '/')
destination_path += '/';
destination_path = destination_path + buffer;
rename_file(get_selected_path(), destination_path);
load_directory_contents(current_path);
refresh_list();
});
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
void FileManagerView::on_refactor(NavigationView& nav) {
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
std::string destination_path = current_path.string();
if (destination_path.back() != '/')//if the path is not ended with '/', add '/'
destination_path += '/';
auto selected_path = get_selected_path();
auto extension = selected_path.extension().string();
if(extension.empty()){// Is Dir
destination_path = destination_path + buffer;
extension_buffer = "";
}else{//is File
destination_path = destination_path + buffer + extension_buffer;
}
rename_file(get_selected_path(), destination_path); //rename the selected file
if (!extension.empty() && selected_path.string().back() != '/' && extension.substr(1) == "C16") { //substr(1) is for ignore the dot
// Rename its partner ( C16 <-> TXT ) file.
auto partner_file_path = selected_path.string().substr(0, selected_path.string().size() - 4) + ".TXT";
destination_path = destination_path.substr(0, destination_path.size() - 4) + ".TXT";
rename_file(partner_file_path, destination_path);
} else if (!extension.empty() && selected_path.string().back() != '/' && extension.substr(1) == "TXT") {
// If the file user choose is a TXT file.
auto partner_file_path = selected_path.string().substr(0, selected_path.string().size() - 4) + ".C16";
destination_path = destination_path.substr(0, destination_path.size() - 4) + ".C16";
rename_file(partner_file_path, destination_path);
}
load_directory_contents(current_path);
refresh_list();
/* 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() {
auto& entry = get_selected_entry();
name_buffer = entry.path.filename().string();
uint32_t cursor_pos = (uint32_t)name_buffer.length();
if (auto pos = name_buffer.find_last_of(".");
pos != name_buffer.npos && !entry.is_directory)
cursor_pos = pos;
text_prompt(nav_, name_buffer, cursor_pos, max_filename_length,
[this](std::string& renamed) {
auto renamed_path = fs::path{ renamed };
rename_file(get_selected_full_path(), current_path / renamed_path);
auto has_partner = partner_file_prompt(nav_, get_selected_full_path(), "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(partner, current_path / new_name);
}
reload_current();
}
);
if (!has_partner)
reload_current();
});
}
void FileManagerView::on_delete() {
delete_file(get_selected_path());
load_directory_contents(current_path);
refresh_list();
auto name = get_selected_entry().path.filename().string();
nav_.push<ModalMessageView>("Delete", "Delete " + name + "\nAre you sure?", YESNO,
[this](bool choice) {
if (choice) {
delete_file(get_selected_full_path());
auto has_partner = partner_file_prompt(
nav_, get_selected_full_path(), "Delete",
[this](const fs::path& partner, bool should_delete) {
if (should_delete)
delete_file(partner);
reload_current();
}
);
if (!has_partner)
reload_current();
}
}
);
}
void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current();
});
}
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;
}
void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v);
button_new_dir.hidden(v);
button_refactor.hidden(v);
button_delete.hidden(v);
set_dirty();
}
button_cut.hidden(v);
button_copy.hidden(v);
button_paste.hidden(v);
button_new_dir.hidden(v);
button_new_file.hidden(v);
FileManagerView::~FileManagerView() {
// Flush ?
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_refactor,
&button_new_dir,
&button_delete
});
menu_view.on_highlight = [this]() {
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
};
refresh_list();
on_select_entry = [this]() {
if (entry_list[menu_view.highlighted_index()].is_directory) {
load_directory_contents(get_selected_path());
refresh_list();
} else
button_rename.focus();
};
button_new_dir.on_select = [this, &nav](Button&) {
name_buffer.clear();
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
make_new_directory(current_path.string() + '/' + buffer);
load_directory_contents(current_path);
refresh_list();
});
};
button_rename.on_select = [this, &nav](Button&) {
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
on_rename(nav);
};
// Don't bother with the UI in the case of no SDC.
if (empty_ == EmptyReason::NoSDC)
return;
button_refactor.on_select = [this, &nav](Button&) {
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
size_t pos = name_buffer.find_last_of(".");
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
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();
if (pos != std::string::npos) {
extension_buffer = name_buffer.substr(pos);
name_buffer = name_buffer.substr(0, pos);
}
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();
};
on_refactor(nav);
};
button_delete.on_select = [this]() {
if (selected_is_valid())
on_delete();
};
button_delete.on_select = [this, &nav](Button&) {
// Use display_modal ?
nav.push<ModalMessageView>("Delete", "Delete " + entry_list[menu_view.highlighted_index()].entry_path.filename().string() + "\nAre you sure?", YESNO,
[this](bool choice) {
if (choice)
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();
};
}
}
+170 -72
View File
@@ -31,11 +31,23 @@
namespace ui {
struct fileman_entry {
std::filesystem::path entry_path { };
std::filesystem::path path { };
uint32_t size { };
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,50 +55,58 @@ public:
std::string filter
);
void focus() override;
void load_directory_contents(const std::filesystem::path& dir_path);
std::filesystem::path get_selected_path();
virtual ~FileManBaseView() { }
void focus() override;
std::string title() const override { return "Fileman"; };
void push_dir(const std::filesystem::path& path);
protected:
NavigationView& nav_;
static constexpr size_t max_filename_length = 30 - 2;
const std::string suffix[5] = { "B", "kB", "MB", "GB", "??" };
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 100;
struct file_assoc_t {
std::string extension;
std::filesystem::path extension;
const Bitmap* icon;
ui::Color color;
};
const std::vector<file_assoc_t> file_types = {
{ ".TXT", &bitmap_icon_file_text, ui::Color::white() },
{ ".PNG", &bitmap_icon_file_image, ui::Color::green() },
{ ".BMP", &bitmap_icon_file_image, ui::Color::green() },
{ ".C8", &bitmap_icon_file_iq, ui::Color::dark_cyan() },
{ ".C16", &bitmap_icon_file_iq, ui::Color::dark_cyan() },
{ ".WAV", &bitmap_icon_file_wav, ui::Color::dark_magenta() },
{ "", &bitmap_icon_file, ui::Color::light_grey() }
{ u".TXT", &bitmap_icon_file_text, ui::Color::white() },
{ u".PNG", &bitmap_icon_file_image, ui::Color::green() },
{ u".BMP", &bitmap_icon_file_image, ui::Color::green() },
{ u".C8", &bitmap_icon_file_iq, ui::Color::dark_cyan() },
{ u".C16", &bitmap_icon_file_iq, ui::Color::dark_cyan() },
{ u".WAV", &bitmap_icon_file_wav, ui::Color::dark_magenta() },
{ u"", &bitmap_icon_file, ui::Color::light_grey() } // NB: Must be last.
};
bool empty_root { false };
std::function<void(void)> on_select_entry { nullptr };
std::function<void(bool)> on_refresh_widgets { nullptr };
std::vector<fileman_entry> entry_list { };
std::filesystem::path current_path { u"" };
std::string extension_filter { "" };
void change_category(int32_t category_id);
std::filesystem::path get_parent_dir();
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
void pop_dir();
void refresh_list();
void reload_current();
void load_directory_contents(const std::filesystem::path& dir_path);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
NavigationView& nav_;
EmptyReason empty_ { EmptyReason::NotEmpty };
std::function<void(KeyEvent)> on_select_entry { nullptr };
std::function<void(bool)> on_refresh_widgets { nullptr };
const std::filesystem::path parent_dir_path { u".." };
std::filesystem::path current_path { u"" };
std::filesystem::path extension_filter { u"" };
std::vector<fileman_entry> entry_list { };
std::vector<uint32_t> saved_index_stack { };
Labels labels {
{ { 0, 0 }, "Path:", Color::light_grey() }
};
Text text_current {
{ 6 * 8, 0 * 8, 24 * 8, 16 },
"",
@@ -96,59 +116,109 @@ 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::string extension_buffer { };
std::filesystem::path clipboard_path { };
ClipboardMode clipboard_mode { ClipboardMode::None };
void refresh_widgets(const bool v);
void on_rename(NavigationView& nav);
void on_refactor(NavigationView& nav);
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;
Labels labels {
{ { 0, 26 * 8 }, "Created ", Color::light_grey() }
@@ -159,26 +229,54 @@ private:
""
};
Button button_rename {
{ 0 * 8, 29 * 8, 9 * 8, 32 },
"Rename"
NewButton button_rename {
{ 0 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_rename,
Color::dark_blue()
};
Button button_refactor{
{ 10 * 8, 29 * 8, 10 * 8, 32 },
"Refactor"
NewButton button_delete {
{ 4 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_trash,
Color::red()
};
Button button_delete {
{ 21 * 8, 29 * 8, 9 * 8, 32 },
"Delete"
NewButton button_cut {
{ 9 * 8, 29 * 8, 4 * 8, 32 },
{ },
&bitmap_icon_cut,
Color::dark_grey()
};
Button button_new_dir {
{ 0 * 8, 34 * 8, 14 * 8, 32 },
"New dir"
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()
};
};
} /* namespace ui */
@@ -44,7 +44,7 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav) : nav_ (nav) {
filename.string().substr(0, max_filename_length),
ui::Color::red(),
&bitmap_icon_temperature,
[this, path]() {
[this, path](KeyEvent) {
this->firmware_selected(path);
}
});
+1 -1
View File
@@ -120,7 +120,7 @@ void FreqManBaseView::refresh_list() {
freqman_item_string(database[n], 30),
ui::Color::white(),
nullptr,
[this](){
[this](KeyEvent){
if (on_select_frequency)
on_select_frequency();
}
-2
View File
@@ -48,8 +48,6 @@ namespace ui {
void focus() override;
void big_display_freq( int64_t f );
const Style style_grey { // level
.font = font::fixed_8x16,
.background = Color::black(),
@@ -28,7 +28,7 @@ namespace ui
{
void GlassView::focus()
{
field_marker.focus();
button_marker.focus();
}
GlassView::~GlassView()
@@ -43,11 +43,6 @@ namespace ui
return ((num / multiplier) + 1) * multiplier;
}
// Returns the previous multiple of num that is a multiple of multiplier
//int64_t GlassView::prev_mult_of(int64_t num, int64_t multiplier) {
// return (num / multiplier) * multiplier;
//}
void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) {
int64_t span = *f_max - *f_min;
int64_t num_intervals = span / width;
@@ -130,7 +125,7 @@ namespace ui
last_max_freq = max_freq_hold ;
freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) );
}
PlotMarker(field_marker.value());
PlotMarker( marker );
}
else
{
@@ -144,13 +139,84 @@ namespace ui
// Each having the radio signal power for it's corresponding frequency slot
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
{
// default fast scan offset
uint8_t offset = 2 ;
baseband::spectrum_streaming_stop();
if( fast_scan || ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) )
{
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
// All things said and done, we actually need 240 of those bins:
// Spectrum.db has 256 bins.
// All things said and done, we actually need 240 of those bins
for (uint8_t bin = 0; bin < 240; bin++)
{
// if the view is done in one pass, show it like in analog_audio_app
if( ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) )
{
// Center 16 bins are ignored (DC spike is blanked)
if (bin < 120)
{
if (spectrum.db[256 - 120 + bin] > max_power) // 134
max_power = spectrum.db[256 - 120 + bin];
}
else
{
if (spectrum.db[ bin - 120] > max_power) // 118
max_power = spectrum.db[bin - 120];
}
}
else // view is made in multiple pass, use original bin picking
{
// Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
if (bin < 120)
{
if (spectrum.db[134 + bin] > max_power) // 134
max_power = spectrum.db[134 + bin];
}
else
{
if (spectrum.db[bin - 118] > max_power) // 118
max_power = spectrum.db[bin - 118];
}
}
if( bin == 120 )
{
bins_Hz_size += 12 * each_bin_size; // add DC bin Hz count into the "pixel fulfilled bag of Hz"
}
else
{
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
}
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (min_color_power < max_power)
add_spectrum_pixel(max_power); // Pixel will represent max_power
else
add_spectrum_pixel(0); // Filtered out, show black
max_power = 0;
if (!pixel_index) // Received indication that a waterfall line has been completed
{
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX
return;
}
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
}
f_center += ( 256 - ( 2 * offset ) ) * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
// lost bins are taken in account so next slice first ignored bins overlap previous kept ones
}
else //slow scan
{
offset = 32 ;
uint8_t bin_length = 80 ;
for (uint8_t bin = offset ; bin < bin_length + offset ; bin++)
{
if (bin < 120)
{
@@ -165,38 +231,6 @@ namespace ui
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (min_color_power < max_power)
add_spectrum_pixel(max_power); // Pixel will represent max_power
else
add_spectrum_pixel(0); // Filtered out, show black
max_power = 0;
if (!pixel_index) // Received indication that a waterfall line has been completed
{
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
f_center = f_center_ini - 2 * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX
return;
}
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
}
f_center += 240 * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
}
else //slow scan
{
for (uint8_t bin = 0; bin < 120 ; bin++)
{
if (spectrum.db[134 + bin] > max_power) // 134
max_power = spectrum.db[134 + bin];
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (min_color_power < max_power)
@@ -209,7 +243,7 @@ namespace ui
if (!pixel_index) // Received indication that a waterfall line has been completed
{
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
f_center = f_center_ini - 2 * each_bin_size ; // Start a new sweep
f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX
@@ -218,7 +252,7 @@ namespace ui
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
}
f_center += 120 * each_bin_size ;
f_center += bin_length * each_bin_size ;
}
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(5);
@@ -243,9 +277,7 @@ namespace ui
f_min = field_frequency_min.value();
f_max = field_frequency_max.value();
search_span = f_max - f_min;
field_marker.set_range(f_min, f_max); // Move the marker between range
field_marker.set_value(f_min + (search_span / 2)); // Put MARKER AT MIDDLE RANGE
if( locked_range )
{
button_range.set_text(">"+to_string_dec_uint(search_span)+"<");
@@ -260,25 +292,16 @@ namespace ui
adjust_range( &f_min , &f_max , 240 );
marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
marker = f_min + (f_max - f_min) / 2 ;
button_marker.set_text( to_string_short_freq( marker ) );
PlotMarker( marker ); // Refresh marker on screen
int32_t marker_step = marker_pixel_step / MHZ_DIV;
if (!marker_step)
field_marker.set_step(1); // in case selected range is less than 240 (pixels)
else
field_marker.set_step(marker_step); // step needs to be a pixel wide.
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); // Initial center frequency for sweep
PlotMarker(field_marker.value()); // Refresh marker on screen
f_center = f_center_ini; // Reset sweep into first slice
pixel_index = 0; // reset pixel counter
max_power = 0;
bins_Hz_size = 0; // reset amount of Hz filled up by pixels
if( next_mult_of( (f_max - f_min) , 240 ) <= LOOKING_GLASS_SLICE_WIDTH_MAX )
if( (f_max - f_min) <= LOOKING_GLASS_SLICE_WIDTH_MAX )
{
LOOKING_GLASS_SLICE_WIDTH = next_mult_of( (f_max - f_min) , 240 );
LOOKING_GLASS_SLICE_WIDTH = (f_max - f_min) ;
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH);
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH/2);
}
@@ -288,20 +311,26 @@ namespace ui
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH);
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH);
}
if( next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 ) > LOOKING_GLASS_SLICE_WIDTH_MAX )
LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX ;
else
LOOKING_GLASS_SLICE_WIDTH = next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 );
receiver_model.set_squelch_level(0);
each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 240 ;
each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 256 ;
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2) ; // Initial center frequency for sweep
f_center = f_center_ini ; // Reset sweep into first slice
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice
}
void GlassView::PlotMarker(rf::Frequency pos)
{
pos = pos * MHZ_DIV;
pos -= f_min;
pos = pos / marker_pixel_step; // Real pixel
uint8_t shift_y = 0 ;
if( live_frequency_view > 0 )
if( live_frequency_view > 0 ) // plot one line down when in live view
{
shift_y = 16 ;
}
@@ -329,8 +358,7 @@ namespace ui
&filter_config,
&field_rf_amp,
&range_presets,
&field_marker,
&text_marker_pm,
&button_marker,
&field_trigger,
&button_jump,
&button_rst,
@@ -509,16 +537,21 @@ namespace ui
this->on_range_changed();
};
field_marker.on_change = [this](int32_t v)
button_marker.on_change = [this]()
{
PlotMarker(v); // Refresh marker on screen
marker = marker + button_marker.get_encoder_delta() * marker_pixel_step ;
if( marker < f_min )
marker = f_min ;
if( marker > f_max )
marker = f_max ;
button_marker.set_text( to_string_short_freq( marker ) );
button_marker.set_encoder_delta( 0 );
PlotMarker( marker ); // Refresh marker on screen
};
field_marker.on_select = [this](NumberField &)
button_marker.on_select = [this](ButtonWithEncoder &)
{
f_center = field_marker.value();
f_center = f_center * MHZ_DIV;
receiver_model.set_tuning_frequency(f_center); // Center tune rx in marker freq.
receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq.
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop();
nav_.push<AnalogAudioView>(); // Jump into audio view
@@ -37,7 +37,7 @@
namespace ui
{
#define LOOKING_GLASS_SLICE_WIDTH_MAX 19999920
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define MHZ_DIV 1000000
#define X2_MHZ_DIV 2000000
@@ -81,14 +81,13 @@ namespace ui
std::vector<preset_entry> presets_db{};
// Each slice bandwidth 20 MHz and a multiple of 240
// since we are using LOOKING_GLASS_SLICE_WIDTH/240 as the each_bin_size
// Each slice bandwidth 20 MHz and a multiple of 256
// since we are using LOOKING_GLASS_SLICE_WIDTH/256 as the each_bin_size
// it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq
int64_t LOOKING_GLASS_SLICE_WIDTH = 19999920;
int64_t LOOKING_GLASS_SLICE_WIDTH = 20000000;
// frequency rounding helpers
int64_t next_mult_of(int64_t num, int64_t multiplier);
//int64_t prev_mult_of(int64_t num, int64_t multiplier);
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
void on_channel_spectrum(const ChannelSpectrum& spectrum);
@@ -108,8 +107,9 @@ namespace ui
rf::Frequency search_span { 0 };
rf::Frequency f_center { 0 };
rf::Frequency f_center_ini { 0 };
rf::Frequency marker { 0 };
rf::Frequency marker_pixel_step { 0 };
rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 240 };
rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 256 };
rf::Frequency bins_Hz_size { 0 };
uint8_t min_color_power { 0 };
uint32_t pixel_index { 0 };
@@ -129,7 +129,7 @@ namespace ui
{{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()},
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
{{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()},
{{0, 3 * 16}, "MARKER: MHz", Color::light_grey()},
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}
};
@@ -180,16 +180,10 @@ namespace ui
{" NONE (WIFI 2.4GHz)", 0},
}};
NumberField field_marker{
{7 * 8, 3 * 16},
4,
{0, 7200},
25,
' '};
Text text_marker_pm{
{20 * 8, 3 * 16, 2 * 8, 16},
""};
ButtonWithEncoder button_marker{
{7 * 8, 3 * 16 , 10 * 8 , 16},
" "
};
NumberField field_trigger{
{4 * 8, 4 * 16},
+18 -30
View File
@@ -90,9 +90,7 @@ void MicTXView::set_tx(bool enable) {
rxaudio(false); //Then turn off audio RX
transmitting = true;
configure_baseband();
transmitter_model.set_tuning_frequency(tx_frequency);
transmitter_model.set_tx_gain(tx_gain);
transmitter_model.set_rf_amp(rf_amp);
transmitter_model.set_tuning_frequency(tx_frequency); // Now,no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp);
transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
} else {
@@ -167,14 +165,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);
@@ -234,8 +232,7 @@ MicTXView::MicTXView(
&field_va_attack,
&field_va_decay,
&field_bw,
&field_rfgain,
&field_rfamp,
&tx_view, // it already integrates previous rfgain, rfamp.
&options_mode,
&field_frequency,
&options_tone_key,
@@ -264,8 +261,7 @@ MicTXView::MicTXView(
&field_va_attack,
&field_va_decay,
&field_bw,
&field_rfgain,
&field_rfamp,
&tx_view, // it already integrates previous rfgain, rfamp.
&options_mode,
&field_frequency,
&options_tone_key,
@@ -366,20 +362,8 @@ MicTXView::MicTXView(
};
field_bw.set_value(10); // pre-default first time, TX deviation FM for NFM / FM
tx_gain = transmitter_model.tx_gain();
field_rfgain.on_change = [this](int32_t v) {
tx_gain = v;
};
field_rfgain.set_value(tx_gain);
// now , no need direct update , field_rfgain , field_rfamp (it is done in ui_transmitter.cpp)
rf_amp = transmitter_model.rf_amp();
field_rfamp.on_change = [this](int32_t v) {
rf_amp = (bool)v;
};
field_rfamp.set_value(rf_amp ? 14 : 0);
options_mode.on_change = [this](size_t, int32_t v) { //{ "NFM/FM", 0 }, { " WFM ", 1 },{ "AM", 2 },{ "USB", 3 },{ "LSB", 4 },{ "DSB", 5 }
enable_am = false;
enable_usb = false;
@@ -423,10 +407,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 +422,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 +436,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 +447,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 +551,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)
}
}
};
+11 -25
View File
@@ -31,6 +31,7 @@
#include "tone_key.hpp"
#include "message.hpp"
#include "receiver_model.hpp"
#include "ui_transmitter.hpp"
namespace ui {
@@ -115,11 +116,9 @@ private:
{ { 17 * 8, 1 * 8 }, "Boost", Color::light_grey() },
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
{ { 15 * 8, 3 * 8 }, "FM TXBW: kHz", Color::light_grey() }, // to be more symetric and consistent to the below FM RXBW
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() },
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() },
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() }, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() }, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() }, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{ {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() },
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
@@ -136,11 +135,9 @@ private:
{ { 17 * 8, 1 * 8 }, "ALC", Color::light_grey() },
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
{ { 15 * 8, 3 * 8 }, "FM TXBW: kHz", Color::light_grey() },
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() },
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() },
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() }, // now , no need to handle GAIN , Amp here It is handled by ui_transmitter.cpp
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() }, // we delete { { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() }, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{ {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() },
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
@@ -212,22 +209,11 @@ OptionsField options_wm8731_boost_mode {
1,
' '
};
NumberField field_rfgain {
{ 8 * 8, 5 * 8 },
2,
{ 0, 47 },
1,
' '
TransmitterView2 tx_view { // new handling of NumberField field_rfgain, NumberField field_rfamp
2 * 8 // y line position.
};
NumberField field_rfamp {
{ 15 * 8, 5 * 8 },
2,
{ 0, 14 },
14,
' '
};
OptionsField options_mode {
{ 24 * 8, 5 * 8 },
4,
-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 }
};
File diff suppressed because it is too large Load Diff
+67 -126
View File
@@ -38,79 +38,10 @@
#include "string_format.hpp"
#include "file.hpp"
#include "app_settings.hpp"
#include "ui_recon_settings.hpp"
namespace ui {
class ReconThread {
public:
ReconThread(freqman_db *database );
~ReconThread();
void set_recon(const bool v);
void set_freq_delete(const bool v);
bool is_recon();
void set_lock_duration( const uint32_t v );
uint32_t get_lock_duration();
void set_lock_nb_match( const uint32_t v );
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();
int64_t get_current_freq();
void set_stepper(const int64_t v);
void set_index_stepper(const int64_t v);
void change_recon_direction();
bool get_recon_direction();
void set_recon_direction( const bool v);
void set_continuous(const bool v);
void set_default_modulation( freqman_index_t index );
freqman_index_t get_current_modulation();
void set_default_bandwidth( freqman_index_t index );
freqman_index_t get_current_bandwidth();
void set_default_step( freqman_index_t index );
void set_freq_index( int16_t index );
int16_t get_freq_index();
void run();
void stop();
ReconThread(const ReconThread&) = delete;
ReconThread(ReconThread&&) = delete;
ReconThread& operator=(const ReconThread&) = delete;
ReconThread& operator=(ReconThread&&) = delete;
private:
freqman_db &frequency_list_ ;
Thread* thread { nullptr };
int64_t freq = 0 ;
uint32_t step = 0 ;
freqman_index_t def_modulation = 0 ;
freqman_index_t def_bandwidth = 0 ;
freqman_index_t def_step = 0 ;
tone_index tone = 0 ;
freqman_entry last_entry = { } ;
int16_t frequency_index = 0 ;
bool _recon { true };
bool _freq_delete { false };
bool _fwd { true };
bool _continuous { true };
bool entry_has_changed = false ;
int64_t _stepper { 0 };
int64_t _index_stepper { 0 };
int32_t _freq_lock { 0 };
uint32_t _lock_duration { 50 };
uint32_t _lock_nb_match { 10 };
static msg_t static_fn(void* arg);
};
class ReconView : public View {
public:
ReconView(NavigationView& nav);
@@ -118,39 +49,37 @@ namespace ui {
void focus() override;
void big_display_freq( int64_t f );
const Style style_grey { // recon
const Style style_grey { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
const Style style_white { // recon
const Style style_white { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
const Style style_yellow { //Found signal
const Style style_yellow { //found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_green { //Found signal
const Style style_green { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_red { //erasing freq
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_blue { // quick recon, wait == 0
const Style style_blue { // quick recon, wait == 0
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::blue(),
@@ -163,30 +92,31 @@ namespace ui {
private:
NavigationView& nav_;
void start_recon_thread();
void audio_output_start();
bool check_sd_card();
size_t change_mode( freqman_index_t mod_type);
void show_max( bool refresh_display = false );
void recon_pause();
void recon_resume();
void user_pause();
void user_resume();
void frequency_file_load( bool stop_all_before = false);
void on_statistics_update(const ChannelStatistics& statistics);
void on_headphone_volume_changed(int32_t v);
void set_display_freq( int64_t freq );
void handle_retune( int64_t freq , uint32_t index );
bool check_sd_card();
void on_index_delta(int32_t v);
void on_stepper_delta(int32_t v);
void recon_redraw();
void handle_retune();
void handle_coded_squelch(const uint32_t value);
bool ReconSetupLoadStrings( const std::string &source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , int32_t &volume );
bool ReconSetupSaveStrings( const std::string &dest, const std::string &input_file , const std::string &output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , int32_t volume );
bool ReconSaveFreq( const std::string &freq_file_path , size_t index , bool warn_if_exists );
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual recon task too...
jammer::jammer_range_t frequency_range { false, 0, MAX_UFREQ }; //perfect for manual recon task too...
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 def_step { 0 };
int32_t wait { RECON_DEF_WAIT_DURATION }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity
freqman_db frequency_list = { };
uint32_t current_index { 0 };
int32_t current_index { 0 };
bool userpause { false };
bool continuous_lock { false };
std::string input_file = { "RECON" };
@@ -200,15 +130,41 @@ namespace ui {
bool load_hamradios = { true };
bool update_ranges = { true };
bool fwd = { true };
// maximum usable freq
long long int MAX_UFREQ = { 7200000000 };
uint32_t recon_lock_nb_match = { 10 };
uint32_t recon_lock_duration = { 50 };
uint32_t recon_match_mode = { 0 };
bool recon = true ;
uint32_t recon_lock_nb_match = { 3 };
uint32_t recon_lock_duration = { RECON_MIN_LOCK_DURATION };
uint32_t recon_match_mode = { RECON_MATCH_CONTINUOUS };
bool scanner_mode { false };
bool manual_mode { false };
bool sd_card_mounted = false ;
int32_t volume = 40 ;
int32_t stepper = 0 ;
int32_t index_stepper = 0 ;
int64_t freq = 0 ;
uint32_t step = 0 ;
freqman_index_t def_modulation = 0 ;
freqman_index_t def_bandwidth = 0 ;
freqman_index_t def_step = 0 ;
tone_index tone = 0 ;
freqman_entry last_entry = { } ;
bool entry_has_changed = false ;
uint32_t freq_lock { 0 };
int64_t minfreq = 0 ;
int64_t maxfreq = 0 ;
bool has_looped = false ;
int8_t status = -1 ; // 0 recon , 1 locking , 2 locked
int32_t last_timer = -1 ;
int8_t last_db = -127 ;
uint16_t last_nb_match = 999 ;
uint16_t last_freq_lock = 999 ;
size_t last_list_size = 0 ;
int8_t last_rssi_min = -127 ;
int8_t last_rssi_med = -127 ;
int8_t last_rssi_max = -127 ;
int32_t last_index = -1 ;
int32_t last_squelch_index = -1 ;
int64_t last_freq = 0 ;
std::string freq_file_path = { } ;
Labels labels
{
@@ -243,7 +199,7 @@ namespace ui {
{ 3 * 8, 1 * 16 },
6,
{ }
};
};
NumberField field_squelch {
{ 12 * 8, 1 * 16 },
@@ -256,21 +212,21 @@ namespace ui {
NumberField field_wait {
{ 20 * 8, 1 * 16 },
5,
{ -9000, 9000 },
100,
{ -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION },
STATS_UPDATE_INTERVAL,
' ',
};
NumberField field_lock_wait {
{ 26 * 8, 1 * 16 },
4,
{ 100 , 9000 },
100,
{ RECON_MIN_LOCK_DURATION , RECON_MAX_LOCK_DURATION },
STATS_UPDATE_INTERVAL,
' ',
};
RSSI rssi {
{ 0 * 16, 2 * 16, 240 - 8 * 8 + 4 , 16 },
{ 0 * 16, 2 * 16, SCREEN_W - 8 * 8 + 4 , 16 },
};
ButtonWithEncoder text_cycle {
@@ -279,28 +235,23 @@ namespace ui {
};
Text text_max {
{ 4 * 8, 3 * 16, 240 - 7 * 8 - 4 * 8 , 16 },
{ 4 * 8, 3 * 16, SCREEN_W - 7 * 8 - 4 * 8 , 16 },
};
Text desc_cycle {
{0, 4 * 16, 240 , 16 },
{0, 4 * 16, SCREEN_W , 16 },
};
/* BigFrequency big_display { //Show frequency in glamour
{ 4, 7 * 16 - 8 , 28 * 8, 52 },
0
}; */
Text big_display { //Show frequency in text mode
Text big_display { //Show frequency in text mode
{ 0, 5 * 16 , 23 * 8, 16 },
};
Text freq_stats { //Show frequency stats in text mode
Text freq_stats { //Show frequency stats in text mode
{ 0, 6 * 16 , 23 * 8, 16 },
};
// TIMER: 9999
Text text_timer { //Show frequency stats in text mode
Text text_timer { //Show frequency stats in text mode
{ 0, 7 * 16 , 11 * 8, 16 },
};
@@ -311,23 +262,23 @@ namespace ui {
};
Button button_recon_setup {
{ 240 - 7 * 8 , 2 * 16 , 7 * 8, 28 },
{ SCREEN_W - 7 * 8 , 2 * 16 , 7 * 8, 28 },
"CONFIG"
};
Button button_looking_glass {
{ 240 - 7 * 8 , 5 * 16 , 7 * 8, 28 },
{ SCREEN_W - 7 * 8 , 5 * 16 , 7 * 8, 28 },
"GLASS"
};
// Button can be RECON or SCANNER
Button button_scanner_mode {
{ 240 - 7 * 8 , 8 * 16 , 7 * 8, 28 },
{ SCREEN_W - 7 * 8 , 8 * 16 , 7 * 8, 28 },
"RECON"
};
Text file_name { //Show file used
{ 0 , 8 * 16 + 6 , 240 - 7 * 8, 16 },
Text file_name { //show file used
{ 0 , 8 * 16 + 6 , SCREEN_W - 7 * 8, 16 },
};
ButtonWithEncoder button_manual_start {
@@ -396,8 +347,6 @@ namespace ui {
"<REMOVE>"
};
std::unique_ptr<ReconThread> recon_thread { };
MessageHandlerRegistration message_handler_coded_squelch {
Message::ID::CodedSquelch,
[this](const Message* const p) {
@@ -406,14 +355,6 @@ namespace ui {
}
};
MessageHandlerRegistration message_handler_retune {
Message::ID::Retune,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const RetuneMessage*>(p);
this->handle_retune(message.freq,message.range);
}
};
MessageHandlerRegistration message_handler_stats {
Message::ID::ChannelStatistics,
[this](const Message* const p) {
@@ -421,7 +362,7 @@ namespace ui {
}
};
// app save settings
std::app_settings settings { };
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
};
+3 -128
View File
@@ -34,131 +34,6 @@ using namespace portapack;
namespace ui {
bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume )
{
File settings_file;
size_t length, file_position = 0;
char * pos;
char * line_start;
char * line_end;
char file_data[257];
uint32_t it = 0 ;
uint32_t nb_params = 9 ;
std::string params[ 9 ];
bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ;
if( check_sd_card )
{
auto result = settings_file.open( source );
if( !result.is_valid() )
{
while( it < nb_params )
{
// Read a 256 bytes block from file
settings_file.seek(file_position);
memset(file_data, 0, 257);
auto read_size = settings_file.read(file_data, 256);
if (read_size.is_error())
break ;
file_position += 256;
// Reset line_start to beginning of buffer
line_start = file_data;
pos=line_start;
while ((line_end = strstr(line_start, "\x0A"))) {
length = line_end - line_start - 1 ;
params[ it ] = string( pos , length );
it ++ ;
line_start = line_end + 1;
pos=line_start ;
if (line_start - file_data >= 256)
break;
if( it >= nb_params )
break ;
}
if (read_size.value() != 256)
break; // End of file
// Restart at beginning of last incomplete line
file_position -= (file_data + 256 - line_start);
}
}
}
if( it > 0 )
input_file = params[ 0 ];
else
input_file = "RECON" ;
if( it > 1 )
output_file= params[ 1 ];
else
output_file = "RECON_RESULTS" ;
if( it > 2 )
recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 );
else
recon_lock_duration = 50 ;
if( it > 3 )
recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 );
else
recon_lock_nb_match = 10 ;
if( it > 4 )
recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 );
else
recon_squelch_level = -14 ;
if( it > 5 )
recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 );
else
recon_match_mode = 0 ;
if( it > 6 )
wait = strtoll( params[ 6 ].c_str() , nullptr , 10 );
else
wait = 5000 ;
if( it > 7 )
lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 );
else
lock_wait = 1000 ;
if( it > 8 )
volume = strtoll( params[ 8 ].c_str() , nullptr , 10 );
else
volume = 40 ;
if( it < nb_params )
{
/* bad number of params, signal defaults */
return false ;
}
return true ;
}
bool ReconSetupSaveStrings( std::string dest, std::string input_file , std::string output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume )
{
File settings_file;
auto result = settings_file.create( dest );
if( result.is_valid() )
return false ;
settings_file.write_line( input_file );
settings_file.write_line( output_file );
settings_file.write_line( to_string_dec_uint( recon_lock_duration ) );
settings_file.write_line( to_string_dec_uint( recon_lock_nb_match ) );
settings_file.write_line( to_string_dec_int( recon_squelch_level ) );
settings_file.write_line( to_string_dec_uint( recon_match_mode ) );
settings_file.write_line( to_string_dec_int( wait ) );
settings_file.write_line( to_string_dec_uint( lock_wait ) );
settings_file.write_line( to_string_dec_int( volume ) );
return true ;
}
ReconSetupViewMain::ReconSetupViewMain( NavigationView &nav , Rect parent_rect , std::string input_file , std::string output_file ) : View( parent_rect ) , _input_file { input_file } , _output_file { output_file }
{
hidden(true);
@@ -178,8 +53,8 @@ namespace ui {
checkbox_continuous.set_value( persistent_memory::recon_continuous() );
checkbox_clear_output.set_value( persistent_memory::recon_clear_output() );
text_input_file.set( _input_file );
button_output_file.set_text( _output_file );
text_input_file.set( _input_file );
button_output_file.set_text( _output_file );
button_load_freqs.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
@@ -203,7 +78,7 @@ namespace ui {
std::string str_file_path = new_file_path.string();
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
_output_file = new_file_path.stem().string();
button_output_file.set_text( _output_file );
button_output_file.set_text( _output_file );
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
+50 -17
View File
@@ -20,6 +20,9 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef _UI_RECON_SETTINGS
#define _UI_RECON_SETTINGS
#include "serializer.hpp"
#include "ui.hpp"
#include "ui_widget.hpp"
@@ -27,10 +30,38 @@
#include "ui_navigation.hpp"
#include "string_format.hpp"
namespace ui {
// maximum usable freq
#define MAX_UFREQ 7200000000
bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume );
bool ReconSetupSaveStrings( std::string dest, const std::string input_file , const std::string output_file , const uint32_t recon_lock_duration , const uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume );
// 1Mhz helper
#ifdef OneMHz
#undef OneMHz
#endif
#define OneMHz 1000000
// modes
#define RECON_MATCH_CONTINUOUS 0
#define RECON_MATCH_SPARSE 1
// statistics update interval in ms (change here if the statistics API is changing it's pace)
#define STATS_UPDATE_INTERVAL 100
// maximum lock duration
#define RECON_MAX_LOCK_DURATION 9900
// default number of match to have a lock
#define RECON_DEF_NB_MATCH 3
#define RECON_MIN_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL
#define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps
// screen size helper
#define SCREEN_W 240
//#define SCREEN_H 320
// recon settings nb params
#define RECON_SETTINGS_NB_PARAMS 8
namespace ui {
class ReconSetupViewMain : public View {
public:
@@ -57,7 +88,7 @@ namespace ui {
};
Button button_output_file {
{ 1 * 8 , 5 * 16 - 2, 18 * 8, 22 },
"RECON_RESULTS"
"RECON_RESULTS"
};
Checkbox checkbox_autosave_freqs {
@@ -86,7 +117,7 @@ namespace ui {
class ReconSetupViewMore : public View {
public:
ReconSetupViewMore( NavigationView& nav, Rect parent_rect , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode );
ReconSetupViewMore( NavigationView& nav, Rect parent_rect , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode );
void Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode );
@@ -94,9 +125,9 @@ namespace ui {
private:
const uint32_t _recon_lock_duration = 50 ;
const uint32_t _recon_lock_nb_match = 10 ;
const uint32_t _recon_match_mode = 0 ;
uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL ;
uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH ;
uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS ;
Checkbox checkbox_load_freqs {
{ 1 * 8, 12 },
@@ -128,10 +159,10 @@ namespace ui {
NumberField field_recon_lock_duration {
{ 1 * 8, 132 }, // position X , Y
4, // number of displayed digits (even empty)
{ 50 , 990 }, // range of number
10, // rotary encoder increment
' ', // filling character
false // can loop
{ -RECON_MAX_LOCK_DURATION , RECON_MAX_LOCK_DURATION }, // range of number
STATS_UPDATE_INTERVAL, // rotary encoder increment
' ', // filling character
false // can loop
};
Text text_recon_lock_nb {
{ 1 * 8 , 162 , 25 * 8 , 22 },
@@ -158,7 +189,7 @@ namespace ui {
class ReconSetupView : public View {
public:
ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode );
ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode );
std::function<void( std::vector<std::string> messages )> on_changed { };
@@ -172,11 +203,11 @@ namespace ui {
std::string input_file = { "RECON" };
std::string output_file = { "RECON_RESULTS" };
uint32_t recon_lock_duration = 50 ;
uint32_t recon_lock_nb_match = 10 ;
uint32_t recon_match_mode = 0 ;
uint32_t recon_lock_duration = STATS_UPDATE_INTERVAL ;
uint32_t recon_lock_nb_match = RECON_DEF_NB_MATCH ;
uint32_t recon_match_mode = RECON_MATCH_CONTINUOUS ;
Rect view_rect = { 0, 3 * 8, 240, 230 };
Rect view_rect = { 0, 3 * 8, SCREEN_W, 230 };
ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file };
ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode };
@@ -192,3 +223,5 @@ namespace ui {
};
} /* namespace ui */
#endif
-2
View File
@@ -88,8 +88,6 @@ public:
void focus() override;
void big_display_freq(rf::Frequency f);
const Style style_grey { // scanning
.font = font::fixed_8x16,
.background = Color::black(),
+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_;
+17 -3
View File
@@ -41,7 +41,6 @@ using namespace portapack;
#include "freqman.hpp"
namespace ui {
SetDateTimeView::SetDateTimeView(
NavigationView& nav
) {
@@ -306,7 +305,6 @@ namespace ui {
checkbox_load_app_settings.set_value(persistent_memory::load_app_settings());
checkbox_save_app_settings.set_value(persistent_memory::save_app_settings());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_load_app_settings(checkbox_load_app_settings.value());
persistent_memory::set_save_app_settings(checkbox_save_app_settings.value());
@@ -405,11 +403,16 @@ namespace ui {
&check_load_mem_at_startup,
&button_save_mem_to_file,
&button_load_mem_from_file,
&button_load_mem_defaults,
&button_return
});
bool load_mem_at_startup = false ;
File pmem_flag_file_handle ;
std::string folder = "SETTINGS";
make_new_directory(folder);
std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ;
auto result = pmem_flag_file_handle.open(pmem_flag_file);
if(!result.is_valid())
@@ -443,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!");
}
@@ -479,6 +482,17 @@ namespace ui {
}
};
button_load_mem_defaults.on_select = [&nav, this](Button&) {
nav.push<ModalMessageView>(
"Warning!",
"This will reset the p.mem\nand set the default settings",
YESNO,
[this](bool choice) {
if (choice) {
portapack::persistent_memory::cache::defaults();
}
} );
};
button_return.on_select = [&nav, this](Button&) {
nav.pop();
+9 -4
View File
@@ -314,7 +314,7 @@ private:
25,
"Save app settings"
};
Button button_save {
{ 2 * 8, 16 * 16, 12 * 8, 32 },
"Save"
@@ -464,15 +464,20 @@ private:
};
Button button_save_mem_to_file {
{ 0, 9 * 16, 240, 32 },
{ 0, 8 * 16, 240, 32 },
"save p.mem to sdcard"
};
Button button_load_mem_from_file {
{ 0, 12 * 16, 240, 32 },
{ 0, 10 * 16 + 4 , 240, 32 },
"load p.mem from sdcard"
};
Button button_load_mem_defaults {
{ 0, 12 * 16 + 8 , 240, 32 },
"! reset p.mem, load defaults !"
};
Button button_return {
{ 16 * 8, 16 * 16, 12 * 8, 32 },
"Return",
+13 -13
View File
@@ -49,26 +49,26 @@ private:
void on_tx_progress(const uint32_t progress, const bool done);
const std::string shape_strings[7] = {
"CW",
"Sine",
"Triangle",
"Saw up",
"Saw down",
"Square",
"Noise"
"CW-just carrier",
"Sine signal ",
"Triangle signal",
"Saw up signal ",
"Saw down signal",
"Square signal ",
"Noise signal " // using 16 bits LFSR register, 16 order polynomial feedback.
};
bool auto_update { false };
Labels labels {
{ { 6 * 8, 4 + 10 }, "Shape:", Color::light_grey() },
{ { 7 * 8, 7 * 8 }, "Tone: Hz", Color::light_grey() },
{ { 3 * 8, 4 + 10 }, "Shape:", Color::light_grey() },
{ { 6 * 8, 7 * 8 }, "Tone: Hz", Color::light_grey() },
{ { 22 * 8, 15 * 8 + 4 }, "s.", Color::light_grey() },
{ { 8 * 8, 20 * 8 }, "Modulation: FM", Color::light_grey() }
};
ImageOptionsField options_shape {
{ 13 * 8, 4, 32, 32 },
{ 10 * 8, 4, 32, 32 },
Color::white(),
Color::black(),
{
@@ -83,7 +83,7 @@ private:
};
Text text_shape {
{ 18 * 8, 4 + 10, 8 * 8, 16 },
{ 15 * 8, 4 + 10, 8 * 8, 16 },
""
};
@@ -94,12 +94,12 @@ private:
};
Button button_update {
{ 6 * 8, 10 * 8, 8 * 8, 3 * 8 },
{ 5 * 8, 10 * 8, 8 * 8, 3 * 8 },
"Update"
};
Checkbox checkbox_auto {
{ 16 * 8, 10 * 8 },
{ 15 * 8, 10 * 8 },
4,
"Auto"
};
+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) {
@@ -0,0 +1,233 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
SpectrumPainterView::SpectrumPainterView(
NavigationView& nav
) : nav_ (nav) {
baseband::run_image(portapack::spi_flash::image_tag_spectrum_painter);
add_children({
&labels,
&tab_view,
&input_image,
&input_text,
&progressbar,
&field_frequency,
&field_rfgain,
&field_rfamp,
&check_loop,
&button_play,
&option_bandwidth,
&field_duration,
&field_pause,
});
Rect view_rect = { 0, 3 * 8, 240, 80 };
input_image.set_parent_rect(view_rect);
input_text.set_parent_rect(view_rect);
field_frequency.set_value(target_frequency());
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
tx_gain = 10;
field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file.
tx_gain = v;
portapack::transmitter_model.set_tx_gain(tx_gain);
};
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file.
rf_amp = (bool)v;
portapack::transmitter_model.set_rf_amp(rf_amp);
};
input_image.on_input_avaliable = [this]() {
image_input_avaliable = true;
};
button_play.on_select = [this](ImageButton&) {
if (tx_active == false) {
tx_mode = tab_view.selected();
if (tx_mode == 0 && image_input_avaliable == false)
return;
//Enable Bias Tee if selected
radio::set_antenna_bias(portapack::get_antenna_bias());
radio::enable({
portapack::receiver_model.tuning_frequency(),
3072000U,
1750000,
rf::Direction::Transmit,
rf_amp,
static_cast<int8_t>(portapack::receiver_model.lna()),
static_cast<int8_t>(portapack::receiver_model.vga())
});
if (portapack::persistent_memory::stealth_mode()){
DisplaySleepMessage message;
EventDispatcher::send_message(message);
}
button_play.set_bitmap(&bitmap_stop);
if (tx_mode == 0) {
tx_current_max_lines = input_image.get_height();
tx_current_width = input_image.get_width();
}
else {
tx_current_max_lines = input_text.get_height();
tx_current_width = input_text.get_width();
}
progressbar.set_max(tx_current_max_lines);
progressbar.set_value(0);
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, false, option_bandwidth.selected_index_value());
}
else {
stop_tx();
}
};
option_bandwidth.on_change = [this](size_t, ui::OptionsField::value_t value) {
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, true, value);
};
field_duration.set_value(10);
field_pause.set_value(5);
}
void SpectrumPainterView::start_tx() {
tx_current_line = 0;
tx_active = true;
tx_timestamp_start = chTimeNow();
}
void SpectrumPainterView::stop_tx() {
button_play.set_bitmap(&bitmap_play);
portapack::transmitter_model.disable();
tx_active = false;
tx_current_line = 0;
}
void SpectrumPainterView::frame_sync() {
if (tx_active) {
if (fifo->is_empty()) {
int32_t sequence_duration = (field_duration.value() + ( check_loop.value() ? field_pause.value() : 0)) * 1000;
int32_t sequence_time = tx_time_elapsed() % sequence_duration;
bool is_pausing = sequence_time > field_duration.value() * 1000;
if (is_pausing) {
fifo->in(std::vector<uint8_t>(tx_current_width));
} else {
auto current_time_line = sequence_time * tx_current_max_lines / (field_duration.value() * 1000);
if (tx_current_line > current_time_line && !check_loop.value()) {
fifo->in(std::vector<uint8_t>(tx_current_width));
stop_tx();
return;
}
tx_current_line = current_time_line;
progressbar.set_value(current_time_line);
if (tx_mode == 0) {
std::vector<uint8_t> line = input_image.get_line(current_time_line);
fifo->in(line);
}
else {
std::vector<uint8_t> line = input_text.get_line(current_time_line);
fifo->in(line);
}
}
}
}
}
SpectrumPainterView::~SpectrumPainterView() {
portapack::transmitter_model.disable();
hackrf::cpld::load_sram_no_verify();
baseband::shutdown();
}
void SpectrumPainterView::focus() {
tab_view.focus();
}
void SpectrumPainterView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f);
}
void SpectrumPainterView::set_target_frequency(const rf::Frequency new_value) {
portapack::persistent_memory::set_tuned_frequency(new_value);
}
rf::Frequency SpectrumPainterView::target_frequency() const {
return portapack::persistent_memory::tuned_frequency();
}
void SpectrumPainterView::paint(Painter& painter) {
View::paint(painter);
size_t c;
Point pos = { 0, screen_pos().y() + 8 + footer_location };
for (c = 0; c < 20; c++) {
painter.draw_bitmap(
pos,
bitmap_stripes,
ui::Color(191, 191, 0),
ui::Color::black()
);
if (c != 9)
pos += { 24, 0 };
else
pos = { 0, screen_pos().y() + 8 + footer_location + 32 + 8 };
}
}
}
@@ -0,0 +1,177 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "capture_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
#include "ui_spectrum_painter_image.hpp"
#include "ui_spectrum_painter_text.hpp"
namespace ui {
class SpectrumPainterView : public View {
public:
SpectrumPainterView(NavigationView& nav);
~SpectrumPainterView();
SpectrumPainterView(const SpectrumPainterView&) = delete;
SpectrumPainterView(SpectrumPainterView&&) = delete;
SpectrumPainterView& operator=(const SpectrumPainterView&) = delete;
SpectrumPainterView& operator=(SpectrumPainterView&&) = delete;
void focus() override;
void paint(Painter& painter) override;
std::string title() const override { return "Spec.Painter"; };
private:
void on_target_frequency_changed(rf::Frequency f);
void set_target_frequency(const rf::Frequency new_value);
rf::Frequency target_frequency() const;
NavigationView& nav_;
bool image_input_avaliable { false };
bool tx_active { false };
uint32_t tx_mode { 0 };
uint16_t tx_current_line { 0 };
uint16_t tx_current_max_lines { 0 };
uint16_t tx_current_width { 0 };
systime_t tx_timestamp_start { 0 };
inline uint32_t tx_time_elapsed() {
auto now = chTimeNow();
return now - tx_timestamp_start;
}
int32_t tx_gain { 47 };
bool rf_amp { false };
SpectrumInputImageView input_image { nav_ };
SpectrumInputTextView input_text { nav_ };
std::array<View*, 2> input_views { { &input_image, &input_text } };
TabView tab_view {
{ "Image", Color::white(), input_views[0] },
{ "Text", Color::white(), input_views[1] }
};
static constexpr int32_t footer_location = 15 * 16 + 8;
ProgressBar progressbar {
{ 4, footer_location - 16, 240-8, 16 }
};
Labels labels {
{ { 10 * 8, footer_location + 1 * 16 }, "GAIN A:", Color::light_grey() },
{ { 1 * 8, footer_location + 2 * 16 }, "BW: Du: P:", Color::light_grey() },
};
FrequencyField field_frequency {
{ 0 * 8, footer_location + 1 * 16 },
};
NumberField field_rfgain {
{ 14 * 8, footer_location + 1 * 16 },
2,
{ 0, 47 },
1,
' '
};
NumberField field_rfamp {
{ 19 * 8, footer_location + 1 * 16 },
2,
{ 0, 14 },
14,
' '
};
Checkbox check_loop {
{ 21 * 8, footer_location + 1 * 16 },
4,
"Loop",
true
};
ImageButton button_play {
{ 28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16 },
&bitmap_play,
Color::green(),
Color::black()
};
OptionsField option_bandwidth {
{ 4 * 8, footer_location + 2 * 16 },
5,
{
BW_OPTIONS
}
};
NumberField field_duration {
{ 13 * 8, footer_location + 2 * 16 },
3,
{ 1, 999 },
1,
' '
};
NumberField field_pause {
{ 19 * 8, footer_location + 2 * 16 },
2,
{ 0, 99 },
1,
' '
};
SpectrumPainterFIFO* fifo { nullptr };
void start_tx();
void frame_sync();
void stop_tx();
MessageHandlerRegistration message_handler_fifo_signal {
Message::ID::SpectrumPainterBufferResponseConfigure,
[this](const Message* const p) {
const auto message = static_cast<const SpectrumPainterBufferConfigureResponseMessage*>(p);
this->fifo = message->fifo;
this->start_tx();
}
};
MessageHandlerRegistration message_handler_sample {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}
};
};
}
@@ -0,0 +1,265 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter_image.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
hidden(true);
add_children({
&button_load_image
});
button_load_image.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".bmp");
constexpr auto data_directory = u"SPECTRUM";
if (std::filesystem::is_directory(data_directory) == false) {
if (make_new_directory(data_directory).ok())
open_view->push_dir(data_directory);
}
else
open_view->push_dir(data_directory);
open_view->on_changed = [this](std::filesystem::path new_file_path) {
this->file = new_file_path.string();
painted = false;
this->set_dirty();
this->on_input_avaliable();
};
};
}
SpectrumInputImageView::~SpectrumInputImageView() {
}
void SpectrumInputImageView::focus() {
button_load_image.focus();
}
bool SpectrumInputImageView::drawBMP_scaled(const ui::Rect r, const std::string file) {
File bmpimage;
size_t file_pos = 0;
uint16_t pointer = 0;
int16_t px = 0, py, zoom_factor = 0;
bmp_header_t bmp_header;
char buffer[257];
ui::Color line_buffer[240];
auto result = bmpimage.open(file);
if(result.is_valid())
return false;
bmpimage.seek(file_pos);
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header));
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature
(bmp_header.planes == 1) && // Seems always to be 1
(bmp_header.compression == 0 || bmp_header.compression == 3 ))) { // No compression
return false;
}
switch(bmp_header.bpp) {
case 16:
file_pos = 0x36;
memset(buffer, 0, 16);
bmpimage.read(buffer, 16);
if(buffer[1] == 0x7C)
type = 3; // A1R5G5B5
else
type = 0; // R5G6B5
break;
case 24:
type = 1;
break;
case 32:
default:
type = 2;
break;
}
width = bmp_header.width;
height = bmp_header.height;
data_start = file_pos = bmp_header.image_data;
while (r.width() < (width >> zoom_factor) || r.height() < (height >> zoom_factor)) {
zoom_factor++;
}
py = height + 16;
while(1) {
while(px < width) {
bmpimage.seek(file_pos);
memset(buffer, 0, 257);
read_size = bmpimage.read(buffer, 256);
if (read_size.is_error())
return false; // Read error
pointer = 0;
while(pointer < 256) {
if(pointer + 4 > 256)
break;
switch(type) {
case 0: // R5G6B5
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
pointer += 2;
file_pos += 2;
break;
case 3: // A1R5G5B5
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
pointer += 2;
file_pos += 2;
break;
case 1: // 24
default:
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 3;
file_pos += 3;
break;
case 2: // 32
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 4;
file_pos += 4;
break;
}
px++;
if(px >= width) {
break;
}
}
if(read_size.value() != 256)
break;
}
if ((((1 << zoom_factor) - 1) & py) == 0x00)
portapack::display.render_line({ r.left(), r.top() + (py >> zoom_factor) }, px >> zoom_factor, line_buffer);
px = 0;
py--;
if(read_size.value() < 256 || py < 0)
break;
}
return true;
}
uint16_t SpectrumInputImageView::get_width(){
return this->width;
}
uint16_t SpectrumInputImageView::get_height(){
return this->height;
}
std::vector<uint8_t> SpectrumInputImageView::get_line(uint16_t y) {
File bmpimage;
bmpimage.open(this->file);
//seek to line
uint32_t line_size = width * (type == 2 ? 4 : (type == 1 ? 3 : 2));
uint32_t line_offset = y * line_size;
bmpimage.seek(data_start + line_offset);
// allocate memory and read
auto buffer = new uint8_t[line_size];
auto bytes_read = bmpimage.read(buffer, line_size);
// greyscale
auto grey_buffer = new uint8_t[width];
int pointer = 0;
for (uint16_t px = 0; px < width; px++) {
ui::Color color;
switch(type) {
case 0: // R5G6B5
pointer = px * 2;
color = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
break;
case 3: // A1R5G5B5
pointer = px * 2;
color = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
break;
case 1: // 24
default:
pointer = px * 3;
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
break;
case 2: // 32
pointer = px * 4;
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
break;
}
grey_buffer[px] = color.to_greyscale();
}
delete buffer;
std::vector<uint8_t> values(width);
for(int i = 0; i < width; i++) {
values[i] = grey_buffer[i];
}
delete grey_buffer;
return values;
}
void SpectrumInputImageView::paint(Painter& painter) {
painter.fill_rectangle(
{{0, 40}, {240, 204}},
style().background
);
if (!painted) {
// This is very slow for big pictures. Do only once.
this->drawBMP_scaled({{ 0, 40 }, {240, 160}}, this->file);
painted = true;
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "ui_transmitter.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
class SpectrumInputImageView : public View {
public:
SpectrumInputImageView(NavigationView& nav);
~SpectrumInputImageView();
void focus() override;
void paint(Painter&) override;
uint16_t get_width();
uint16_t get_height();
std::vector<uint8_t> get_line(uint16_t);
std::function<void()> on_input_avaliable { };
private:
bool painted {false};
std::string file {""};
uint16_t width {0};
uint16_t height {0};
uint8_t type {0};
uint32_t data_start {0};
Button button_load_image {
{ 0 * 8, 11 * 16 - 4, 30 * 8, 28 },
"Load Image ..."
};
bool drawBMP_scaled(const ui::Rect r, const std::string file);
};
}
@@ -0,0 +1,111 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter_text.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
hidden(true);
add_children({
&text_message_0,
&text_message_1,
&text_message_2,
&text_message_3,
&text_message_4,
&text_message_5,
&text_message_6,
&text_message_7,
&text_message_8,
&text_message_9,
&button_message
});
button_message.on_select = [this, &nav](Button&) {
this->on_set_text(nav);
};
}
SpectrumInputTextView::~SpectrumInputTextView() {
}
void SpectrumInputTextView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 300);
}
void SpectrumInputTextView::focus() {
button_message.focus();
}
void SpectrumInputTextView::paint(Painter& painter) {
message = buffer;
for (uint32_t i = 0 ; i < text_message.size(); i++) {
if (message.length() > i * 30)
text_message[i]->set(message.substr(i * 30, 30));
else
text_message[i]->set("");
}
painter.fill_rectangle(
{{0, 40}, {240, 204}},
style().background
);
}
constexpr uint32_t pixel_repeat = 32;
uint16_t SpectrumInputTextView::get_width(){
return 16 * pixel_repeat;
}
uint16_t SpectrumInputTextView::get_height(){
return this->message.length() * 8;
}
std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
auto character_position = y / 8;
auto character = this->message[character_position];
auto glyph_data = ui::font::fixed_8x16.glyph(character).pixels();
auto line_in_character = y % 8;
std::vector<uint8_t> data(16 * pixel_repeat);
for (uint32_t index = 0; index < 16; index++) {
auto glyph_byte = index;
auto glyph_bit = line_in_character;
uint8_t glyph_pixel = (glyph_data[glyph_byte] & (1 << glyph_bit)) >> glyph_bit;
for (uint32_t j = 0; j < pixel_repeat; j++)
data[index*pixel_repeat + j] = glyph_pixel * 255;
}
return data;
}
}
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "ui_transmitter.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
class SpectrumInputTextView : public View {
public:
SpectrumInputTextView(NavigationView& nav);
~SpectrumInputTextView();
void focus() override;
void paint(Painter&) override;
uint16_t get_width();
uint16_t get_height();
std::vector<uint8_t> get_line(uint16_t);
private:
std::string buffer { "PORTAPACK" };
std::string message { };
void on_set_text(NavigationView& nav);
Text text_message_0 {
{ 0 * 8, 0 * 16, 30 * 8, 16 },
""
};
Text text_message_1 {
{ 0 * 8, 1 * 16, 30 * 8, 16 },
""
};
Text text_message_2 {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
""
};
Text text_message_3 {
{ 0 * 8, 3 * 16, 30 * 8, 16 },
""
};
Text text_message_4 {
{ 0 * 8, 4 * 16, 30 * 8, 16 },
""
};
Text text_message_5 {
{ 0 * 8, 5 * 16, 30 * 8, 16 },
""
};
Text text_message_6 {
{ 0 * 8, 6 * 16, 30 * 8, 16 },
""
};
Text text_message_7 {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
""
};
Text text_message_8 {
{ 0 * 8, 8 * 16, 30 * 8, 16 },
""
};
Text text_message_9 {
{ 0 * 8, 9 * 16, 30 * 8, 16 },
""
};
std::array<Text*, 10> text_message { {
&text_message_0,
&text_message_1,
&text_message_2,
&text_message_3,
&text_message_4,
&text_message_5,
&text_message_6,
&text_message_7,
&text_message_8,
&text_message_9,
} };
Button button_message {
{ 0 * 8, 11 * 16 - 4, 30 * 8, 28 },
"Set message"
};
};
}
-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");
+8 -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;
@@ -298,6 +299,11 @@ void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t d
send_message(&message);
}
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw){
SpectrumPainterBufferConfigureRequestMessage message { width, height, update, bw };
send_message(&message);
}
static bool baseband_image_running = false;
void run_image(const portapack::spi_flash::image_tag_t image_tag) {
+4
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;
};
@@ -88,6 +91,7 @@ void set_rds_data(const uint16_t message_length);
void set_spectrum(const size_t sampling_rate, const size_t trigger);
void set_siggen_tone(const uint32_t tone);
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
void request_beep();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
+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 */
+171 -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 {
@@ -250,6 +342,15 @@ std::string filesystem_error::what() const {
}
}
path path::parent_path() const {
const auto index = _s.find_last_of(preferred_separator);
if( index == _s.npos ) {
return { }; // NB: Deviation from STL.
} else {
return _s.substr(0, index);
}
}
path path::extension() const {
const auto t = filename().native();
const auto index = t.find_last_of(u'.');
@@ -296,6 +397,14 @@ path& path::replace_extension(const path& replacement) {
return *this;
}
bool operator==(const path& lhs, const path& rhs) {
return lhs.native() == rhs.native();
}
bool operator!=(const path& lhs, const path& rhs) {
return !(lhs == rhs);
}
bool operator<(const path& lhs, const path& rhs) {
return lhs.native() < rhs.native();
}
@@ -304,14 +413,28 @@ bool operator>(const path& lhs, const path& rhs) {
return lhs.native() > rhs.native();
}
path operator+(const path& lhs, const path& rhs) {
path result = lhs;
result += rhs;
return result;
}
path operator/(const path& lhs, const path& rhs) {
path result = lhs;
result /= rhs;
return result;
}
directory_iterator::directory_iterator(
std::filesystem::path path,
std::filesystem::path wild
) : 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 ) {
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...
}
@@ -333,6 +456,20 @@ bool is_regular_file(const file_status s) {
return !(s & AM_DIR);
}
bool file_exists(const path& file_path) {
FILINFO filinfo;
auto fr = f_stat(reinterpret_cast<const TCHAR*>(file_path.c_str()), &filinfo);
return fr == FR_OK;
}
bool is_directory(const path& file_path) {
FILINFO filinfo;
auto fr = f_stat(reinterpret_cast<const TCHAR*>(file_path.c_str()), &filinfo);
return fr == FR_OK && is_directory(static_cast<file_status>(filinfo.fattrib));
}
space_info space(const path& p) {
DWORD free_clusters { 0 };
FATFS* fs;
+31 -4
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 };
};
@@ -123,6 +127,7 @@ struct path {
return *this;
}
path parent_path() const;
path extension() const;
path filename() const;
path stem() const;
@@ -151,14 +156,25 @@ struct path {
return *this;
}
path& operator/=(const path& p) {
if (_s.back() != preferred_separator)
_s += preferred_separator;
_s += p._s;
return *this;
}
path& replace_extension(const path& replacement = path());
private:
string_type _s;
};
bool operator==(const path& lhs, const path& rhs);
bool operator!=(const path& lhs, const path& rhs);
bool operator<(const path& lhs, const path& rhs);
bool operator>(const path& lhs, const path& rhs);
path operator+(const path& lhs, const path& rhs);
path operator/(const path& lhs, const path& rhs);
using file_status = BYTE;
@@ -227,6 +243,8 @@ inline bool operator!=(const directory_iterator& lhs, const directory_iterator&
bool is_directory(const file_status s);
bool is_regular_file(const file_status s);
bool file_exists(const path& file_path);
bool is_directory(const path& file_path);
space_info space(const path& p);
@@ -238,14 +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);
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.");
+39 -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,19 @@ 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;
// If EOF reached, insert 0x0A after, in case the last line doesn't have a C/R
if (read_size.value() < 256)
*(line_start + read_size.value()) = 0x0A;
// 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 +166,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=");
@@ -229,7 +234,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
{
db.push_back({ frequency_a, frequency_b, description, type , modulation , bandwidth , step , tone });
n++;
if (n >= FREQMAN_MAX_PER_FILE) return true;
if (n > FREQMAN_MAX_PER_FILE) return true;
}
line_start = line_end + 1;
+5 -4
View File
@@ -31,9 +31,10 @@
#include "string_format.hpp"
#include "ui_widget.hpp"
#define FREQMAN_DESC_MAX_LEN 30
#define FREQMAN_MAX_PER_FILE 500 /* MAX PER FILES */
#define FREQMAN_MAX_PER_FILE_STR "500" /*STRING OF FREQMAN_MAX_PER_FILE */
#define FREQMAN_DESC_MAX_LEN 24 // This is the number of characters that can be drawn in front of "R: TEXT..." before taking a full screen line
#define FREQMAN_MAX_PER_FILE 115 // Maximum of entries we can read. This is a hardware limit
// It was tested and lowered to leave a bit of space to the caller
#define FREQMAN_MAX_PER_FILE_STR "115" // STRING OF FREQMAN_MAX_PER_FILE
using namespace ui;
using namespace std;
@@ -86,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);
}
+6 -3
View File
@@ -489,10 +489,13 @@ bool init() {
chThdSleepMilliseconds(10);
if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config());
if ( result == portapack::cpld::CpldUpdateStatus::Program_failed ) {
chThdSleepMilliseconds(10);
// If using a "2021/12 QFP100", press and hold the left button while booting. Should only need to do once.
if (load_config() != 3 && load_config() != 4){
// Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update
// Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update
if (load_config() != 3 /* left */ && load_config() != 4 /* right */){
shutdown_base();
return false;
}
+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__*/
+11 -6
View File
@@ -81,16 +81,21 @@ void Manager::feed(const Frame& frame) {
const auto touch_raw = frame.touch;
//const auto touch_stable = touch_debounce.state();
const auto touch_stable = frame.touch;
bool touch_pressure = false;
bool touch_down_pressure = false;
bool touch_up_pressure = false;
// Only feed coordinate averaging if there's a touch.
// TODO: Separate threshold to gate coordinates for filtering?
if( touch_raw ) {
const auto metrics = calculate_metrics(frame);
// TODO: Add touch pressure hysteresis?
touch_pressure = (metrics.r < r_touch_threshold);
if( touch_pressure ) {
constexpr float r_touch_down_threshold = 3200.0f;
constexpr float r_touch_up_threshold = r_touch_down_threshold * 2.0f;
touch_down_pressure = (metrics.r < r_touch_down_threshold);
touch_up_pressure = (metrics.r < r_touch_up_threshold);
if( touch_down_pressure ) {
filter_x.feed(metrics.x * 1024);
filter_y.feed(metrics.y * 1024);
}
@@ -101,7 +106,7 @@ void Manager::feed(const Frame& frame) {
switch(state) {
case State::NoTouch:
if( touch_stable && touch_pressure && !persistent_memory::disable_touchscreen()) {
if( touch_stable && touch_down_pressure && !persistent_memory::disable_touchscreen()) {
if( point_stable() ) {
state = State::TouchDetected;
touch_started();
@@ -110,7 +115,7 @@ void Manager::feed(const Frame& frame) {
break;
case State::TouchDetected:
if( touch_stable && touch_pressure ) {
if( touch_stable && touch_up_pressure ) {
touch_moved();
} else {
state = State::NoTouch;
-1
View File
@@ -219,7 +219,6 @@ private:
TouchDetected,
};
static constexpr float r_touch_threshold = 640;
static constexpr size_t touch_count_threshold { 3 };
static constexpr uint32_t touch_stable_bound { 8 };
+1 -16
View File
@@ -30,10 +30,6 @@
namespace ui {
void AlphanumView::paint(Painter&) {
draw_cursor();
}
AlphanumView::AlphanumView(
NavigationView& nav,
std::string& str,
@@ -41,7 +37,7 @@ AlphanumView::AlphanumView(
) : TextEntryView(nav, str, max_length)
{
size_t n;
add_children({
&button_mode,
&text_raw,
@@ -77,16 +73,7 @@ AlphanumView::AlphanumView(
field_raw.set_value('0');
field_raw.on_select = [this](NumberField&) {
char_add(field_raw.value());
update_text();
};
button_ok.on_select = [this, &nav](Button&) {
if (on_changed)
on_changed(_str);
nav.pop();
};
update_text();
}
void AlphanumView::set_mode(const uint32_t new_mode) {
@@ -120,8 +107,6 @@ void AlphanumView::on_button(Button& button) {
char_delete();
else
char_add(c);
update_text();
}
bool AlphanumView::on_encoder(const EncoderEvent delta) {
-1
View File
@@ -40,7 +40,6 @@ public:
AlphanumView& operator=(const AlphanumView&) = delete;
AlphanumView& operator=(AlphanumView&&) = delete;
void paint(Painter& painter) override;
bool on_encoder(const EncoderEvent delta) override;
private:
+21 -10
View File
@@ -136,14 +136,22 @@ void BtnGridView::update_items() {
more = false;
for (NewButton* item : menu_item_views) {
if (i >= menu_items.size()) break;
// Assign item data to NewButtons according to offset
item->set_text(menu_items[i + offset].text);
item->set_bitmap(menu_items[i + offset].bitmap);
item->set_color(menu_items[i + offset].color);
item->on_select = menu_items[i + offset].on_select;
item->set_dirty();
if ((i + offset) >= menu_items.size()) {
item->hidden(true);
item->set_text(" ");
item->set_bitmap(nullptr);
item->on_select = [](){};
item->set_dirty();
}
else {
// Assign item data to NewButtons according to offset
item->hidden(false);
item->set_text(menu_items[i + offset].text);
item->set_bitmap(menu_items[i + offset].bitmap);
item->set_color(menu_items[i + offset].color);
item->on_select = menu_items[i + offset].on_select;
item->set_dirty();
}
i++;
}
@@ -167,18 +175,21 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
highlighted_item = new_value;
offset = new_value - displayed_max + rows_;
update_items();
set_dirty();
} else if ((uint32_t)new_value < offset) {
// Shift BtnGridView down
highlighted_item = new_value;
offset = (new_value / rows_) * rows_;
update_items();
set_dirty();
} else {
// Just update highlight
highlighted_item = new_value;
if (visible())
item_view(highlighted_item - offset)->focus();
}
if (visible())
item_view(highlighted_item - offset)->focus();
return true;
}
+9 -3
View File
@@ -163,6 +163,12 @@ void MenuView::clear() {
}
menu_items.clear();
highlighted_item = 0;
offset = 0;
}
size_t MenuView::item_count() const {
return menu_items.size();
}
void MenuView::add_item(MenuItem new_item) {
@@ -209,7 +215,7 @@ MenuItemView* MenuView::item_view(size_t index) const {
bool MenuView::set_highlighted(int32_t new_value) {
int32_t item_count = (int32_t)menu_items.size();
if (new_value < 0)
if (new_value < 0 || item_count == 0)
return false;
if (new_value >= item_count)
@@ -238,7 +244,7 @@ bool MenuView::set_highlighted(int32_t new_value) {
return true;
}
uint32_t MenuView::highlighted_index() {
uint32_t MenuView::highlighted_index() const {
return highlighted_item;
}
@@ -262,7 +268,7 @@ bool MenuView::on_key(const KeyEvent key) {
case KeyEvent::Select:
case KeyEvent::Right:
if( menu_items[highlighted_item].on_select ) {
menu_items[highlighted_item].on_select();
menu_items[highlighted_item].on_select(key);
}
return true;
+3 -2
View File
@@ -39,7 +39,7 @@ struct MenuItem {
std::string text;
ui::Color color;
const Bitmap* bitmap;
std::function<void(void)> on_select;
std::function<void(KeyEvent)> on_select;
// TODO: Prevent default-constructed MenuItems.
// I managed to construct a menu with three extra, unspecified menu items
@@ -83,11 +83,12 @@ 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;
bool set_highlighted(int32_t new_value);
uint32_t highlighted_index();
uint32_t highlighted_index() const;
void set_parent_rect(const Rect new_parent_rect) override;
void on_focus() override;
+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 },
+17
View File
@@ -434,6 +434,23 @@ namespace ui {
void RSSIGraph::set_nb_columns( int16_t nb )
{
nb_columns = nb ;
while( graph_list.size() > nb_columns )
{
graph_list.erase( graph_list.begin() );
}
}
void RSSIGraph::on_hide() {
nb_columns_before_hide = nb_columns ;
nb_columns = 1 ;
while( graph_list.size() > nb_columns )
{
graph_list.erase( graph_list.begin() );
}
}
void RSSIGraph::on_show() {
nb_columns = nb_columns_before_hide ;
}
void RSSI::on_focus() {
+4
View File
@@ -115,8 +115,12 @@ namespace ui {
void paint(Painter& painter) override;
void add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db );
void set_nb_columns( int16_t nb );
void on_hide() override ;
void on_show() override ;
private:
uint16_t nb_columns_before_hide = 16 ;
uint16_t nb_columns = 16 ;
RSSIGraphList graph_list { } ;
};
+27 -49
View File
@@ -29,9 +29,25 @@ using namespace portapack;
namespace ui {
void text_prompt(NavigationView& nav, std::string& str, const size_t max_length, const std::function<void(std::string&)> on_done) {
void text_prompt(
NavigationView& nav,
std::string& str,
size_t max_length,
std::function<void(std::string&)> on_done
) {
text_prompt(nav, str, str.length(), max_length, on_done);
}
void text_prompt(
NavigationView& nav,
std::string& str,
uint32_t cursor_pos,
size_t max_length,
std::function<void(std::string&)> on_done
) {
//if (persistent_memory::ui_config_textentry() == 0) {
auto te_view = nav.push<AlphanumView>(str, max_length);
te_view->set_cursor(cursor_pos);
te_view->on_changed = [on_done](std::string& value) {
if (on_done)
on_done(value);
@@ -45,76 +61,38 @@ void text_prompt(NavigationView& nav, std::string& str, const size_t max_length,
}*/
}
void TextEntryView::update_text() {
if (cursor_pos < 30)
text_input.set(_str + std::string(_max_length - _str.length(), ' '));
else
text_input.set('<' + _str.substr(cursor_pos - 29, 29));
draw_cursor();
}
/* TextEntryView ***********************************************************/
void TextEntryView::char_delete() {
if (!cursor_pos) return;
cursor_pos--;
_str.resize(cursor_pos);
text_input.char_delete();
}
void TextEntryView::char_add(const char c) {
if (cursor_pos >= _max_length) return;
_str += c;
cursor_pos++;
text_input.char_add(c);
}
void TextEntryView::draw_cursor() {
Point draw_pos;
draw_pos = { text_input.screen_rect().location().x() + std::min((Coord)cursor_pos, (Coord)28) * 8,
text_input.screen_rect().location().y() + 16 };
// Erase previous
display.fill_rectangle(
{ { text_input.screen_rect().location().x(), draw_pos.y() }, { text_input.screen_rect().size().width(), 4 } },
Color::black()
);
// Draw new
display.fill_rectangle(
{ draw_pos, { 8, 4 } },
Color::white()
);
void TextEntryView::set_cursor(uint32_t pos) {
text_input.set_cursor(pos);
}
void TextEntryView::focus() {
button_ok.focus();
text_input.focus();
}
TextEntryView::TextEntryView(
NavigationView& nav,
std::string& str,
size_t max_length
) : _str(str),
_max_length(max_length)
) : text_input{ str, max_length, { 0, 0 } }
{
// Trim from right
//_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), _str->end());
if (_str.length() > _max_length)
_str.resize(_max_length);
_str.reserve(_max_length);
cursor_pos = _str.length();
add_children({
&text_input,
&button_ok
});
button_ok.on_select = [this, &nav](Button&) {
_str.resize(cursor_pos);
button_ok.on_select = [this, &str, &nav](Button&) {
if (on_changed)
on_changed(_str);
on_changed(str);
nav.pop();
};
}
+19 -11
View File
@@ -34,6 +34,8 @@ public:
void focus() override;
std::string title() const override { return "Text entry"; };
void set_cursor(uint32_t pos);
protected:
TextEntryView(NavigationView& nav, std::string& str, size_t max_length);
@@ -45,24 +47,30 @@ protected:
void char_add(const char c);
void char_delete();
void draw_cursor();
void update_text();
std::string& _str;
size_t _max_length;
uint32_t cursor_pos { 0 };
Text text_input {
{ 0, 0, 240, 16 }
};
TextField text_input;
Button button_ok {
{ 10 * 8, 33 * 8, 9 * 8, 32 },
"OK"
};
};
void text_prompt(NavigationView& nav, std::string& str, size_t max_length, const std::function<void(std::string&)> on_done = nullptr);
// Show the TextEntry view to receive keyboard input.
// NB: This function returns immediately. 'str' is taken
// by reference and its lifetime must be ensured by the
// caller until the TextEntry view is dismissed.
void text_prompt(
NavigationView& nav,
std::string& str,
size_t max_length,
std::function<void(std::string&)> on_done = nullptr);
void text_prompt(
NavigationView& nav,
std::string& str,
uint32_t cursor_pos,
size_t max_length,
std::function<void(std::string&)> on_done = nullptr);
} /* namespace ui */
@@ -189,4 +189,72 @@ TransmitterView::~TransmitterView() {
baseband::shutdown();
}
/* TransmitterView2 *******************************************************/
void TransmitterView2::paint(Painter& painter) {
// Not using TransmitterView2, but if we delete it,we got , top line 1 a blanking rect.
(void) painter; // Avoid warning: unused parameter .
}
void TransmitterView2::on_tx_gain_changed(int32_t tx_gain) {
transmitter_model.set_tx_gain(tx_gain);
update_gainlevel_styles();
}
void TransmitterView2::on_tx_amp_changed(bool rf_amp) {
transmitter_model.set_rf_amp(rf_amp);
update_gainlevel_styles();
}
void TransmitterView2::update_gainlevel_styles() {
const Style *new_style_ptr = NULL;
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
if(tot_gain > POWER_THRESHOLD_HIGH) {
new_style_ptr = &style_power_high;
} else if(tot_gain > POWER_THRESHOLD_MED) {
new_style_ptr = &style_power_med;
} else if(tot_gain > POWER_THRESHOLD_LOW) {
new_style_ptr = &style_power_low;
}
field_gain.set_style(new_style_ptr);
text_gain.set_style(new_style_ptr);
field_amp.set_style(new_style_ptr);
text_amp.set_style(new_style_ptr);
}
void TransmitterView2::on_show() {
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
update_gainlevel_styles();
}
TransmitterView2::TransmitterView2( const Coord y)
{
set_parent_rect({ 3*8, y, 20 * 8, 1 * 8 }); // set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
add_children({
&text_gain,
&field_gain,
&text_amp,
&field_amp,
});
field_gain.on_change = [this](uint32_t tx_gain) {
on_tx_gain_changed(tx_gain);
};
field_amp.on_change = [this](uint32_t rf_amp) {
on_tx_amp_changed((bool) rf_amp);
};
}
TransmitterView2::~TransmitterView2() {
audio::output::stop();
transmitter_model.disable();
baseband::shutdown();
}
} /* namespace ui */
@@ -165,6 +165,69 @@ private:
void update_gainlevel_styles(void);
};
class TransmitterView2 : public View {
public:
TransmitterView2(const Coord y);
~TransmitterView2();
void on_show() override;
void paint(Painter& painter) override;
private:
const Style style_power_low {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_power_med {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::orange(),
};
const Style style_power_high {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
Text text_gain {
{ 0, 3 * 8, 5 * 8, 1 * 16 },
"Gain:"
};
NumberField field_gain {
{ 5 * 8, 3 * 8 },
2,
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
max2837::tx::gain_db_step,
' '
};
Text text_amp {
{ 8 * 8, 3 * 8, 5 * 8, 1 * 16 },
"Amp:"
};
NumberField field_amp {
{ 12 * 8, 3 * 8 },
2,
{ 0, 14 },
14,
' '
};
void on_tx_gain_changed(int32_t tx_gain);
void on_tx_amp_changed(bool rf_amp);
void update_gainlevel_styles(void);
};
} /* namespace ui */
#endif/*__UI_TRANSMITTER_H__*/
+6 -2
View File
@@ -73,6 +73,7 @@
#include "ui_whipcalc.hpp"
#include "ui_flash_utility.hpp"
#include "ui_sd_over_usb.hpp"
#include "ui_spectrum_painter.hpp"
//#include "acars_app.hpp"
#include "ais_app.hpp"
@@ -367,13 +368,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;
}
@@ -594,6 +597,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{ "TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push<LCRView>(); } },
{ "TouchTune", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push<TouchTunesView>(); } },
{ "Playlist", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push<PlaylistView>(); } },
{ "S.Painter", ui::Color::orange(), &bitmap_icon_morse, [&nav](){ nav.push<SpectrumPainterView>(); } },
//{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } },
});
}
+10 -1
View File
@@ -76,6 +76,15 @@ namespace ui
{
return reinterpret_cast<T *>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
// Pushes a new view under the current on the stack so the current view returns into this new one.
template <class T, class... Args>
void push_under_current(Args &&...args)
{
auto new_view = std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...));
view_stack.insert(view_stack.end() - 1, std::move(new_view));
}
template <class T, class... Args>
T *replace(Args &&...args)
{
@@ -126,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;
+8
View File
@@ -140,6 +140,7 @@ set(CPPSRC
debug.cpp
${COMMON}/gcc.cpp
${COMMON}/performance_counter.cpp
${COMMON}/random.cpp
tone_gen.cpp
)
@@ -463,6 +464,13 @@ set(MODE_CPPSRC
)
DeclareTargets(PSIG siggen)
### Spectrum painter
set(MODE_CPPSRC
proc_spectrum_painter.cpp
)
DeclareTargets(PSPT spectrum_painter)
### SSTV TX
set(MODE_CPPSRC
+9
View File
@@ -20,6 +20,7 @@
*/
#include "baseband_dma.hpp"
#include "portapack_shared_memory.hpp"
#include <cstdint>
#include <cstddef>
@@ -103,8 +104,12 @@ static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gp
static ThreadWait thread_wait;
volatile uint32_t buffer_transfered = 0;
volatile uint32_t buffer_handled = 0;
static void transfer_complete() {
const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
buffer_transfered++;
thread_wait.wake_from_interrupt(next_lli_index);
}
@@ -158,6 +163,10 @@ void disable() {
baseband::buffer_t wait_for_buffer() {
const auto next_index = thread_wait.sleep();
buffer_handled++;
auto buffer_missed = buffer_transfered - buffer_handled;
shared_memory.m4_buffer_missed = buffer_missed;
if( next_index >= 0 ) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
+6
View File
@@ -83,6 +83,12 @@ SSB::SSB() : hilbert() {
}
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer,TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider ) {
//unused
(void)configured_in ;
(void)new_beep_index ;
(void)new_beep_timer ;
(void)new_txprogress_message ;
// No way to activate correctly the roger beep in this option, Maybe not enough M4 CPU power , Let's block roger beep in SSB selection by now .
int32_t sample = 0;
int8_t re = 0, im = 0;
+29 -12
View File
@@ -49,7 +49,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
// Sine
sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]);
} else if (tone_shape == 2) {
// Tri
// Triangle
int8_t a = (tone_phase & 0xFF000000) >> 24;
sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80;
} else if (tone_shape == 3) {
@@ -61,24 +61,40 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
} else if (tone_shape == 5) {
// Square
sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128;
} else if (tone_shape == 6) {
// Noise
sample = (lfsr & 0xFF000000) >> 24;
feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1;
lfsr = (lfsr << 1) | feedback;
if (!lfsr) lfsr = 0x1337; // Shouldn't do this :(
} else if (tone_shape == 6) {
// Noise generator, pseudo random noise generator, 16 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci.
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register
// 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));
lfsr_16 = (lfsr_16 >> 1) | (bit_16 << 15);
sample = (lfsr_16 & 0x00FF); // main pseudo random noise generator.
}
if (counter == 5) { // after many empiric test, that combination mix of >>4 and >>5, gives a reasonable trade off white noise / good rf power level .
sample = ((lfsr_16 & 0b0000111111110000) >> 4); // just changing the spectrum shape .
}
if (counter == 10) {
sample = ((lfsr_16 & 0b0001111111100000) >> 5); // just changing the spectrum shape .
}
counter++;
if (counter ==15) {
counter=0;
}
}
if (tone_shape < 6) { // we are in periodic signals, we need tone phases update.
tone_phase += tone_delta;
}
tone_phase += tone_delta;
// Do FM
// Do FM modulation
delta = sample * fm_delta;
phase += delta;
sphase = phase + (64 << 24);
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
im = (sine_table_i8[( phase & 0xFF000000) >> 24]);
}
buffer.p[i] = {re, im};
@@ -104,7 +120,8 @@ void SigGenProcessor::on_message(const Message* const msg) {
fm_delta = message.bw * (0xFFFFFFULL / 1536000);
tone_shape = message.shape;
lfsr = 0x54DF0119;
// lfsr = seed_value ; // Finally not used , init lfsr 8 bits.
lfsr_16 = seed_value_16; // init lfsr 16 bits.
configured = true;
break;
+10 -6
View File
@@ -38,14 +38,18 @@ private:
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
uint32_t tone_delta { 0 }, fm_delta { };
uint32_t lfsr { }, feedback { }, tone_shape { };
uint32_t tone_delta { 0 }, fm_delta { },tone_phase { 0 };
uint8_t tone_shape { };
uint32_t sample_count { 0 };
bool auto_off { };
uint32_t tone_phase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 };
int8_t sample { 0 };
int8_t re { 0 }, im { 0 };
int32_t phase { 0 }, sphase { 0 }, delta { 0 }; // they may have sign in the pseudo random sample generation.
int8_t sample { 0 }, re { 0 }, im { 0 }; // they have sign + and -.
uint16_t seed_value_16 = {0xACE1}; // seed 16 bits lfsr : any nonzero start state will work.
uint16_t lfsr_16 { }, bit_16 { }; // bit must be 16-bit to allow bit<<15 later in the code */
uint8_t counter {0};
// uint8_t seed_value = {0x56}; // Finally not used lfsr of 8 bits , seed 8blfsr : any nonzero start state will work.
// uint8_t lfsr { }, bit { }; // Finally not used lfsr of 8 bits , bit must be 8-bit to allow bit<<7 later in the code */
TXProgressMessage txprogress_message { };
};
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "proc_spectrum_painter.hpp"
#include "event_m4.hpp"
#include "dsp_fft.hpp"
#include "random.hpp"
#include <cstdint>
// TODO move to class members SpectrumPainterProcessor
complex16_t *current_line_data = nullptr;
complex16_t * volatile next_line_data = nullptr;
uint32_t current_line_index = 0;
uint32_t current_line_width = 0;
int32_t current_bw = 0;
std::vector<uint8_t> fifo_data[1 << SpectrumPainterBufferConfigureResponseMessage::fifo_k] { };
SpectrumPainterFIFO fifo { fifo_data, SpectrumPainterBufferConfigureResponseMessage::fifo_k };
int max_val = 127;
// This is called at 3072000/2048 = 1500Hz
void SpectrumPainterProcessor::execute(const buffer_c8_t& buffer) {
for (uint32_t i = 0; i < buffer.count; i++) {
if (current_line_data != nullptr) {
auto data = current_line_data[(current_line_index++ * current_bw / 3072 ) % current_line_width];
buffer.p[i] = {(int8_t) data.real(), (int8_t) data.imag()};
}
else
buffer.p[i] = {0, 0};
}
// collect new data
if (next_line_data != nullptr) {
if (current_line_data != nullptr)
delete current_line_data;
current_line_data = next_line_data;
next_line_data = nullptr;
}
}
WORKING_AREA(thread_wa, 4096);
void SpectrumPainterProcessor::run() {
int ui = 0;
init_genrand(22267);
while (true) {
if (fifo.is_empty() == false && next_line_data == nullptr) {
std::vector<uint8_t> data;
fifo.out(data);
auto picture_width = data.size();
auto fft_width = picture_width * 2;
auto qu = fft_width/4;
complex16_t *v = new complex16_t[fft_width];
complex16_t *tmp = new complex16_t[fft_width];
for (uint32_t fft_index = 0; fft_index < fft_width; fft_index++) {
if (fft_index < qu) {
}
else if (fft_index < qu*3) {
//TODO: Improve index handling
auto image_index = fft_index-qu;
auto bin_power = data[image_index]; // 0 to 255
auto bin_phase = genrand_int31(); // 0 to 255
// rotate by random angle
auto phase_cos = (sine_table_i8[((int)(bin_phase + 0x40)) & 0xFF]); // -127 to 127
auto phase_sin = (sine_table_i8[((int)(bin_phase)) & 0xFF]);
auto real = (int16_t)((int16_t)phase_cos * bin_power / 255); // -127 to 127
auto imag = (int16_t)((int16_t)phase_sin * bin_power / 255); // -127 to 127
auto fftshift_index = 0;
if (fft_index < qu*2) // first half (fft_index = qu; fft_index < qu*2)
fftshift_index = fft_index + 2*qu; // goes to back
else // 2nd half (fft_index = qu*2; fft_index < qu*3)
fftshift_index = fft_index - 2*qu; // goes to front
v[fftshift_index] = {real, imag};
}
}
ifft<complex16_t>(v, fft_width, tmp);
// normalize
volatile int32_t maximum = 1;
for (uint32_t i = 0; i < fft_width; i++) {
if (v[i].real() > maximum)
maximum = v[i].real();
if (v[i].real() < -maximum)
maximum = -v[i].real();
if (v[i].imag() > maximum)
maximum = v[i].imag();
if (v[i].imag() < -maximum)
maximum = -v[i].imag();
}
if (maximum == 1) { // a black line
for (uint32_t i = 0; i < fft_width; i++)
v[i] = {0, 0};
}
else {
for (uint32_t i = 0; i < fft_width; i++) {
v[i] = { (int8_t)((int32_t)v[i].real() * 120 / maximum), (int8_t)((int32_t)v[i].imag() * 120 / maximum)};
}
}
delete tmp;
next_line_data = v;
ui++;
}
else {
chThdSleepMilliseconds(1);
}
}
}
void SpectrumPainterProcessor::on_message(const Message* const msg) {
switch(msg->id) {
case Message::ID::SpectrumPainterBufferRequestConfigure:
{
const auto message = *reinterpret_cast<const SpectrumPainterBufferConfigureRequestMessage*>(msg);
current_line_width = message.width;
current_bw = message.bw / 500;
if (message.update == false) {
SpectrumPainterBufferConfigureResponseMessage response { &fifo };
shared_memory.application_queue.push(response);
if (configured == false) {
thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
NORMALPRIO, SpectrumPainterProcessor::fn,
this
);
configured = true;
}
}
break;
}
default:
break;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<SpectrumPainterProcessor>() };
event_dispatcher.run();
return 0;
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "portapack_shared_memory.hpp"
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
class SpectrumPainterProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const p) override;
void run();
private:
bool configured { false };
BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
Thread* thread {nullptr};
protected:
static msg_t fn(void* arg) {
auto obj = static_cast<SpectrumPainterProcessor*>(arg);
obj->run();
return 0;
}
};
+4 -4
View File
@@ -33,7 +33,7 @@
namespace portapack {
namespace cpld {
bool update_if_necessary(
CpldUpdateStatus update_if_necessary(
const Config config
) {
jtag::GPIOTarget target {
@@ -51,7 +51,7 @@ bool update_if_necessary(
/* Run-Test/Idle */
if( !cpld.idcode_ok() ) {
return false;
return CpldUpdateStatus::Idcode_check_failed;
}
cpld.sample();
@@ -62,7 +62,7 @@ bool update_if_necessary(
* in passive state.
*/
if( !cpld.silicon_id_ok() ) {
return false;
return CpldUpdateStatus::Silicon_id_check_failed;
}
/* Verify CPLD contents against current bitstream. */
@@ -86,7 +86,7 @@ bool update_if_necessary(
cpld.disable();
}
return ok;
return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed;
}
} /* namespace cpld */
+8 -1
View File
@@ -27,7 +27,14 @@
namespace portapack {
namespace cpld {
bool update_if_necessary(
enum class CpldUpdateStatus {
Success = 0,
Idcode_check_failed = 1,
Silicon_id_check_failed = 2,
Program_failed = 3
};
CpldUpdateStatus update_if_necessary(
const Config config
);
+1
View File
@@ -20,3 +20,4 @@
*/
#include "dsp_fft.hpp"
#include "complex.hpp"
+42
View File
@@ -33,6 +33,7 @@
#include "complex.hpp"
#include "hal.h"
#include "utility.hpp"
#include "sine_table_int8.hpp"
namespace std {
/* https://github.com/AE9RB/fftbench/blob/master/cxlr.hpp
@@ -135,4 +136,45 @@ void fft_c_preswapped(std::array<T, N>& data, const size_t from, const size_t to
}
}
/*
ifft(v,N):
[0] If N==1 then return.
[1] For k = 0 to N/2-1, let ve[k] = v[2*k]
[2] Compute ifft(ve, N/2);
[3] For k = 0 to N/2-1, let vo[k] = v[2*k+1]
[4] Compute ifft(vo, N/2);
[5] For m = 0 to N/2-1, do [6] through [9]
[6] Let w.real() = cos(2*PI*m/N)
[7] Let w.imag() = sin(2*PI*m/N)
[8] Let v[m] = ve[m] + w*vo[m]
[9] Let v[m+N/2] = ve[m] - w*vo[m]
*/
template<typename T>
void ifft( T *v, int n, T *tmp )
{
if(n>1) {
int k,m;
T z, w, *vo, *ve;
ve = tmp; vo = tmp+n/2;
for(k=0; k<n/2; k++) {
ve[k] = v[2*k];
vo[k] = v[2*k+1];
}
ifft( ve, n/2, v ); /* FFT on even-indexed elements of v[] */
ifft( vo, n/2, v ); /* FFT on odd-indexed elements of v[] */
for(m=0; m<n/2; m++) {
w.real(sine_table_i8[((int)(m/(double)n * 0x100 + 0x40)) & 0xFF]);
w.imag(sine_table_i8[((int)(m/(double)n * 0x100)) & 0xFF]);
z.real((w.real()*vo[m].real() - w.imag()*vo[m].imag())/127); /* Re(w*vo[m]) */
z.imag((w.real()*vo[m].imag() + w.imag()*vo[m].real())/127); /* Im(w*vo[m]) */
v[ m ].real(ve[m].real() + z.real());
v[ m ].imag(ve[m].imag() + z.imag());
v[m+n/2].real(ve[m].real() - z.real());
v[m+n/2].imag(ve[m].imag() - z.imag());
}
}
return;
}
#endif/*__DSP_FFT_H__*/
+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
+4 -4
View File
@@ -489,8 +489,8 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::string file) {
if(pointer + 4 > 256)
break;
switch(type) {
case 0:
case 3:
case 0: // R5G6B5
case 3: // A1R5G5B5
if(!type)
line_buffer[px] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
else
@@ -498,13 +498,13 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::string file) {
pointer += 2;
file_pos += 2;
break;
case 1:
case 1: // 24
default:
line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 3;
file_pos += 3;
break;
case 2:
case 2: // 32
line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 4;
file_pos += 4;
+38
View File
@@ -109,6 +109,8 @@ public:
AudioSpectrum = 52,
APRSPacket = 53,
APRSRxConfigure = 54,
SpectrumPainterBufferRequestConfigure = 55,
SpectrumPainterBufferResponseConfigure = 56,
MAX
};
@@ -1143,4 +1145,40 @@ public:
uint32_t return_code;
};
class SpectrumPainterBufferConfigureRequestMessage : public Message {
public:
constexpr SpectrumPainterBufferConfigureRequestMessage(
uint16_t width,
uint16_t height,
bool update,
int32_t bw
) : Message { ID::SpectrumPainterBufferRequestConfigure },
width { width },
height { height },
update { update },
bw { bw }
{
}
uint16_t width;
uint16_t height;
bool update;
int32_t bw;
};
using SpectrumPainterFIFO = FIFO<std::vector<uint8_t>>;
class SpectrumPainterBufferConfigureResponseMessage : public Message {
public:
static constexpr size_t fifo_k = 2;
constexpr SpectrumPainterBufferConfigureResponseMessage(
SpectrumPainterFIFO* fifo
) : Message { ID::SpectrumPainterBufferResponseConfigure },
fifo { fifo }
{
}
SpectrumPainterFIFO* fifo { nullptr };
};
#endif/*__MESSAGE_H__*/
+2 -1
View File
@@ -68,7 +68,8 @@ struct SharedMemory {
uint8_t volatile request_m4_performance_counter{ 0 };
uint8_t volatile m4_cpu_usage{ 0 };
uint16_t volatile m4_stack_usage{ 0 };
uint16_t volatile m4_heap_usage{ 0 };
uint32_t volatile m4_heap_usage{ 0 };
uint16_t volatile m4_buffer_missed{ 0 };
};
extern SharedMemory& shared_memory;
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "random.hpp"
static unsigned long state[N]; /* the array for the state vector */
static int rnd_left = 1;
static int rnd_init = 0;
static unsigned long *rnd_next;
/* initializes state[N] with a seed */
void init_genrand(unsigned long s)
{
int j;
state[0] = s & 0xffffffffUL;
for (j = 1; j < N; j++)
{
state[j] = (1812433253UL * (state[j - 1] ^ (state[j - 1] >> 30)) + j);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array state[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
state[j] &= 0xffffffffUL; /* for >32 bit machines */
}
rnd_left = 1;
rnd_init = 1;
}
static void next_state(void)
{
unsigned long *p = state;
int j;
/* if init_genrand() has not been called, */
/* a default initial seed is used */
if (rnd_init == 0)
init_genrand(5489UL);
rnd_left = N;
rnd_next = state;
for (j = N - M + 1; --j; p++)
*p = p[M] ^ TWIST(p[0], p[1]);
for (j = M; --j; p++)
*p = p[M - N] ^ TWIST(p[0], p[1]);
*p = p[M - N] ^ TWIST(p[0], state[0]);
}
long genrand_int31(void)
{
unsigned long y;
if (--rnd_left == 0)
next_state();
y = *rnd_next++;
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return (long)(y >> 1);
}

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