Compare commits

...

312 Commits

Author SHA1 Message Date
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
jLynx 9385be4f1e Merge pull request #920 from zxkmm/ready_to_pr_2_20230426_imp_issue_917
implemented issue#917
2023-04-27 07:01:02 +12:00
zxkmmOnHaseeWSL 9cddab9a5e fix mistakenly remane dir as file -- credit:Gull 2023-04-26 19:09:38 +08:00
zxkmmOnHaseeWSL 6e01a1d0dc change ?: to if else 2023-04-26 16:20:05 +08:00
gullradriel f73cd39d2d Merge pull request #922 from NotherNgineer/next
#910 Brighter color for .C8/.C16 files in fileman
2023-04-26 09:29:57 +02:00
Mark Thompson 0303c658ea Brighter color for .C8/.C16 files in fileman
See https://github.com/eried/portapack-mayhem/issues/910
2023-04-26 02:04:37 -05:00
gullradriel 94ddb63b1d Merge pull request #921 from zxkmm/ready_to_pr_20230426_3_textual_change_for_flash_unity
textual change for  the flash unity
2023-04-26 08:29:51 +02:00
gullradriel b1aa607a7c Merge pull request #907 from NotherNgineer/next
Support for ERT SCM+ meter protocol
2023-04-26 08:28:39 +02:00
Mark Thompson 509f86c1f7 Merge branch 'eried:next' into next 2023-04-26 01:25:46 -05:00
zxkmmOnHaseeWSL b45ca43e27 textual change for the flash unity 2023-04-26 13:29:11 +08:00
zxkmmOnHaseeWSL 47482d1e58 fixed var.clear 2023-04-26 13:22:50 +08:00
zxkmmOnHaseeWSL 59dfe5b50b implemented issue#917 2023-04-26 13:01:51 +08:00
Mark Thompson b1f5023fe1 Removed unneeded space characters 2023-04-24 15:29:51 -05:00
gullradriel a54b94fe53 Merge pull request #915 from gullradriel/recon-update
Recon description flashing on range fix
2023-04-24 21:15:56 +02:00
GullCode 45bdabcef9 Fix for description being updated at each step on a range 2023-04-24 21:14:34 +02:00
Mark Thompson 6cdada1118 Include ERT packet type in ert.txt log file 2023-04-24 09:59:43 -05:00
gullradriel cffc72c516 Merge pull request #913 from gullradriel/looking-glass-zoom
Looking glass zoom update
2023-04-24 13:56:04 +02:00
GullCode e53514aa12 tweaking scan start values and ranges, allowing <24MHz range 2023-04-24 13:51:31 +02:00
Bernd Herzog 775de5ce6f Merge pull request #912 from bernd-herzog/dfu_menu
improved stability of cpu usage calculation
2023-04-24 13:05:36 +02:00
Bernd Herzog 4ff92be23b refactoring 2023-04-24 13:04:59 +02:00
Bernd Herzog 948d8d947e improved stability of cpu usage calculation 2023-04-24 13:00:34 +02:00
jLynx 55c300ac4b Merge pull request #909 from bernd-herzog/dfu_menu
Dfu menu
2023-04-24 10:26:11 +12:00
Bernd Herzog 29b7a5ee56 improved m4 m0 communication 2023-04-23 23:48:20 +02:00
Bernd Herzog 2ef9ebd7bd implemented M4 stats 2023-04-23 21:48:45 +02:00
Bernd Herzog 850a79c9bb added m0 stats to dfu screen 2023-04-23 19:52:38 +02:00
Bernd Herzog 812f0f8211 added overlay to system view 2023-04-23 16:21:33 +02:00
jLynx d00c21adb7 Merge pull request #908 from zxkmm/ready_to_pr_20230423_fix_stealth_in_replay
fix stealth doesn't work
2023-04-23 21:11:45 +12:00
zxkmmOnHaseeWSL 925c1548a8 fix stealth doesn't work 2023-04-23 16:47:06 +08:00
jLynx cd1f1bd388 Merge pull request #906 from zxkmm/fix_fileman_refactor
fix last commit aka fileman refactor exception
2023-04-23 19:12:40 +12:00
zxkmmOnHaseeWSL 474fe00146 formatting fix 2023-04-23 14:53:06 +08:00
Mark Thompson 44b2e5ea61 Support for ERT SCM+ meter protocol 2023-04-23 01:34:56 -05:00
Mark Thompson 48ed7b1b1a Support for ERT SCM+ meter protocol 2023-04-23 01:33:42 -05:00
Mark Thompson 6201be82ea Support for ERT SCM+ meter protocol 2023-04-23 01:31:45 -05:00
Mark Thompson fe1d296b48 Support for ERT SCM+ meter protocol 2023-04-23 01:29:51 -05:00
Mark Thompson 697876d86a Support for ERT SCM+ meter protocol 2023-04-23 01:26:37 -05:00
Brumi-2021 b72c765911 Merge pull request #904 from zxkmm/ready_to_pr_20230421
make the title touchable, and back/return when touch it
2023-04-22 21:39:10 +02:00
zxkmmOnHaseeWSL 2a14888b80 fix last commit aka fileman refactor exception 2023-04-22 22:58:10 +08:00
jLynx 99f1bded4d Merge pull request #905 from zxkmm/ready_to_pr_20230422
add Refactor in fileman app
2023-04-22 21:52:53 +12:00
zxkmmOnHaseeWSL d8e9fad205 fix indentation for pr 20230422-2 2023-04-22 17:51:31 +08:00
zxkmmOnHaseeWSL 0e8dc1567c fix indentation for pr 20230422 2023-04-22 17:50:21 +08:00
zxkmmOnHaseeWSL 4ff71dcfca add Refactor in fileman app 2023-04-22 17:21:51 +08:00
zxkmmOnHaseeWSL ed0f0eb15e touchable title, little width adjust again... sorry 2023-04-22 13:31:27 +08:00
zxkmmOnHaseeWSL 193a80edea smaller touchable title 2023-04-22 13:02:41 +08:00
zxkmmOnHaseeWSL df0c85db8f touchable title 2023-04-21 21:34:18 +08:00
Bernd Herzog c5738c46d5 Merge pull request #899 from bernd-herzog/dfu_menu
Dfu button
2023-04-17 08:17:21 +02:00
Bernd Herzog 0acf7ec2c2 refactoring 2023-04-16 20:33:02 +02:00
Bernd Herzog dc560ba01f improved debug app layout 2023-04-16 20:31:14 +02:00
Bernd Herzog d74fd92451 added dfu button to debug app 2023-04-16 19:51:06 +02:00
Bernd Herzog bcefa774cd added dfu button to debug app 2023-04-16 18:23:54 +02:00
gullradriel 8382eaf45f Merge pull request #896 from gullradriel/looking-fast-slow
Looking fast slow
2023-04-13 11:11:45 +02:00
GullCode 9f700a30c0 Changed text to MAX HOLD to make a difference with existing 'MAX' 2023-04-13 11:02:32 +02:00
GullCode d815e6c952 Merge branch 'next' of github.com:gullradriel/portapack-mayhem into looking-fast-slow 2023-04-13 10:38:13 +02:00
GullCode e60ec953ab move text and button a bit so it's fitting when big 2023-04-13 10:36:15 +02:00
GullCode 72d1e21fc6 added adjust_range, fast scan and slow scan, lock/unlock range, integer division results when possible, default step of 1 2023-04-13 10:18:16 +02:00
jLynx e4d42817ee Merge pull request #895 from JimiHFord/patch-1
fix typo
2023-04-13 16:16:19 +12:00
Jimi Ford 5729bde214 fix typo 2023-04-13 00:11:30 -04:00
gullradriel ec7a77896a Update ui_looking_glass_app.hpp
size of db is uint8_t, adjusting live-v and peak-v array to uint8_t
2023-04-11 20:41:42 +02:00
eried 99c26c3b7b Adding step files of the enclosure 2023-04-11 09:55:21 +02:00
Brumi-2021 aa20bff2b9 Merge pull request #887 from gullradriel/looking-glass-fix
Peak view entry menu and functionnalities
2023-04-10 23:10:19 +02:00
GullCode f5e949047d correct clear size in peak-v and live-v 2023-04-10 21:39:50 +02:00
GullCode afb75af5b1 removed redundant set_dirty 2023-04-09 22:32:35 +02:00
GullCode 7f99b1181e Added MAX:FREQ , RST , JMP on LIVE-V and PEAK-V. Ploter is having a y shift in these modes 2023-04-09 22:07:50 +02:00
GullCode cec0fe3288 Peak view entry menu and functionnalities 2023-04-09 14:45:07 +02:00
gullradriel ba3114c0b6 Update ui_looking_glass_app.cpp
Restoring accidentally deleted filter_config.on_change
2023-04-09 08:26:09 +02:00
gullradriel cc2bb772e5 Merge pull request #884 from gullradriel/looking-glass-level-view-controls
Looking glass level view controls
2023-04-07 17:02:32 +02:00
GullCode fbf9138410 live frequency view integration controls 2023-04-07 17:01:09 +02:00
GullCode 0559aa2037 Merge branch 'next' of github.com:gullradriel/portapack-mayhem into next 2023-04-07 15:30:51 +02:00
gullradriel 0d7072ec5d Merge pull request #882 from bernd-herzog/fix_hackrf_mode_2
fixed hackrf mode for portapacks with TQFP100 CPLD
2023-04-07 14:54:33 +02:00
GullCode c1f55c0dec Merge branch 'next' of github.com:gullradriel/portapack-mayhem into next 2023-04-07 13:45:18 +02:00
gullradriel 464b7dd98d Update analog_audio_app.hpp
Added 100k bandwidth choice for SPEC
2023-04-07 13:44:48 +02:00
GullCode 4d429437b5 Revert "Update analog_audio_app.hpp"
This reverts commit bc39292e72.
2023-04-07 13:43:43 +02:00
gullradriel bc39292e72 Update analog_audio_app.hpp
Added 100k bandwidth choice for SPEC
2023-04-07 13:40:16 +02:00
gullradriel 27f70cb9fe Merge pull request #880 from gullradriel/looking-test
Looking glass minor upgrade
2023-04-07 13:10:13 +02:00
Bernd Herzog e5f80adeb7 Merge pull request #881 from eried/revert-878-fix_hackrf_mode
Revert "fixed hackrf mode for portapacks with TQFP100 CPLD"
2023-04-07 12:54:47 +02:00
Bernd Herzog 5a6f6e8db0 Revert "fixed hackrf mode for portapacks with TQFP100 CPLD" 2023-04-07 12:53:26 +02:00
Bernd Herzog 21547e6c55 fixed hackrf mode for portapacks with TQFP100 CPLD 2023-04-07 12:52:41 +02:00
GullCode 12cfbf70f0 added option button for view_config, default to SPCTR (spectrum), clear screen between changes, color gradient 2023-04-07 11:30:28 +02:00
GullCode c3971cae32 Added live frequency view mode 2023-04-07 01:57:42 +02:00
gullradriel f7c2dde79f Merge pull request #878 from bernd-herzog/fix_hackrf_mode
fixed hackrf mode for portapacks with TQFP100 CPLD
2023-04-06 17:29:28 +02:00
Bernd Herzog cd2d810c31 fixed hackrf mode for portapacks with TQFP100 CPLD 2023-04-06 14:09:50 +02:00
jLynx ba6202e651 Merge pull request #874 from NotherNgineer/next
TPMS app enhancement to support Celsius/Fahrenheit toggling #873
2023-04-06 08:25:32 +12:00
Mark Thompson 40d64030c8 Enhance TPMS app to support Fahrenheit temperature
Modified TPMS app to allow switching between Celsius & Fahrenheit temperatures, which also simplified some of the code by sticking with a constant heading row
2023-04-05 14:16:44 -05:00
Mark Thompson 049a20b878 Enhance TPMS app to show Fahrenheit temps
Modified TPMS app to allow switching between Celsius & Fahrenheit temperatures, which also simplifies some of the code
2023-04-05 14:13:16 -05:00
gullradriel 0e5ae7a6a4 Merge pull request #871 from Brumi-2021/Add_tick_box_sel_common_RX_TX_freq_to_MIC_APP
Add Separated/common freq control in Mic App
2023-04-04 18:53:43 +02:00
Brumi-2021 c867f98117 Add Separated/common freq control in Mic App 2023-04-04 17:26:45 +02:00
gullradriel 54eebedc1f Merge pull request #869 from NotherNgineer/next
#868 TPMS app displays incorrect readings for Type 5 sensors
2023-04-04 07:37:05 +02:00
Mark Thompson 35db368527 Update tpms_packet.cpp 2023-04-03 16:40:07 -05:00
Mark Thompson be691fd278 Fixed TPMS type 5 sensor readings
Fixed issue with temperature & pressure values for OOK-8400-Schrader TPMS sensor type 5 (temperature & pressure were swapped and using wrong conversion ratio).  Tested fix against 14 sensors of this type.
2023-04-03 15:36:10 -05:00
gullradriel 3db3e78b44 Merge pull request #866 from gullradriel/recon-gui-update
Recon gui update
2023-04-03 12:49:22 +02:00
GullCode a48ecccd7d widget size and position adjustement to avoid overlap, changed OPT in favor of CONFIG and gave all the side buttons the same size 2023-04-03 12:46:59 +02:00
GullCode 570775552c fixed peak that was drawing after the rssi widget 2023-04-03 12:45:35 +02:00
gullradriel ecbb232d9c Merge pull request #864 from bernd-herzog/ssd_to_usb
Ssd to usb
2023-04-01 19:58:02 +02:00
gullradriel 0e59e61461 Merge pull request #865 from gullradriel/recon-fix
fixing range frequencies and index skipping
2023-04-01 19:57:31 +02:00
GullCode 7da322f926 fixing range frequencies and index skipping 2023-04-01 19:56:44 +02:00
Bernd Herzog 04392752d1 fixed spelling error 2023-04-01 19:38:38 +02:00
Bernd Herzog db5fcaba44 Merge remote-tracking branch 'origin/next' into ssd_to_usb 2023-04-01 19:24:53 +02:00
Bernd Herzog a8f0c8eac9 refactoring 2023-04-01 19:24:13 +02:00
Bernd Herzog 6fadd70c1d updated ui 2023-04-01 19:16:01 +02:00
Bernd Herzog 8d8d457da2 refactoring 2023-04-01 18:57:53 +02:00
Bernd Herzog 893df7272f refactoring 2023-04-01 18:36:57 +02:00
Bernd Herzog bb90fdc5fc Implemented write 2023-04-01 17:39:08 +02:00
Bernd Herzog 50859171f4 implemented msd read 2023-04-01 17:21:57 +02:00
Bernd Herzog fc1b676ce6 implemented rest of scsi stack 2023-03-31 22:34:55 +02:00
Bernd Herzog 776ac652a8 implemented scsi inquiry 2023-03-31 19:18:39 +02:00
gullradriel c9c438824d Merge pull request #860 from gullradriel/load-save-persistent-memory
Load save persistent memory
2023-03-31 18:25:10 +02:00
GullCode 95eb1e6d59 Loading pmem if enable before displaying splashscreen 2023-03-31 14:24:54 +02:00
GullCode 1f32367495 reset to original since loading is now taking place in navigation 2023-03-31 14:24:29 +02:00
GullCode 0ed5e6542e P.Mem settings menu entry 2023-03-31 14:06:22 +02:00
GullCode 7240d45a4f added on green sdcard load persistent settings 2023-03-31 14:05:49 +02:00
GullCode 17250304e0 indentation & added load & save persistent settings to sdcard 2023-03-31 14:05:33 +02:00
GullCode 835bc49999 Working pmem load flag checkbox 2023-03-30 17:40:17 +02:00
Bernd Herzog a6eb430830 integrated usb stack 2023-03-30 11:46:36 +02:00
Bernd Herzog 8dd68a0811 added copy of hackrf usb stack 2023-03-29 17:05:11 +02:00
Erwin Ried 9540776afd Merge pull request #855 from bernd-herzog/flash_utility
Add new App: flash utility
2023-03-28 15:41:43 +02:00
Bernd Herzog f5d4fce945 added flash utility 2023-03-28 14:39:00 +02:00
gullradriel 8840a6e894 Merge pull request #844 from bernd-herzog/reduce_image_size_lz4
Reduce image size lz4
2023-03-28 07:35:51 +02:00
gullradriel 7ef98f7741 Merge pull request #854 from gullradriel/converter-fix-disable
Fix to force converter icon to be displayed if enabled
2023-03-27 23:06:32 +02:00
GullCode b41be47c9d pesky and non consistent indentation is boring 2023-03-27 23:05:46 +02:00
GullCode 1e2bc703b8 Fix to force converter icon to be displayed if enabled 2023-03-27 23:03:09 +02:00
Bernd Herzog 9115eadd17 added lz4 to dockerfile 2023-03-27 16:22:20 +02:00
gullradriel 05429d3d15 Merge pull request #853 from gullradriel/up-down-convert
Up Down Converter (replacement/update of previous HamItUp function)
2023-03-27 15:39:04 +02:00
GullCode 30c1f9f594 Changed hamitup to converter icons and functions 2023-03-27 14:22:35 +02:00
GullCode f994f8d697 changing logic to take 'up' or 'down' in account 2023-03-27 14:22:07 +02:00
GullCode e045628dab Menu entry for Converter, cleanings of Radio menu 2023-03-27 14:16:03 +02:00
GullCode 17cdb34fea added set of functions for up/down/enable/disable converter 2023-03-27 14:15:28 +02:00
GullCode 9df0f6e9df updated bitmap icons for converter 2023-03-27 14:14:53 +02:00
GullCode 54e4b535cc converters icons 2023-03-27 14:14:11 +02:00
gullradriel d6555a7af0 Merge pull request #852 from gullradriel/recon-minor-update
Recon minor update
2023-03-26 21:06:16 +02:00
GullCode 30004a1c3c cosmetic, ui moves 2023-03-26 21:04:43 +02:00
GullCode 8d60da0a5a updated description of entry / input output file 2023-03-26 15:30:54 +02:00
GullCode 17f30b6f26 Added and fixed go to index 2023-03-26 15:11:27 +02:00
GullCode 3d13ee828e changed text index to button, adding set to index and on_encoder, global indentation 2023-03-24 23:44:16 +01:00
Brumi-2021 9398b12093 Merge pull request #850 from KillerTurtleSoftware/patch-1
Corrected tone_key.cpp Motorola Code
2023-03-24 16:53:37 +01:00
Erwin Ried 61b82645f7 Update problem-upgrading-the-firmware.md 2023-03-24 12:50:54 +01:00
Erwin Ried c285084dcd Update bug_report.md 2023-03-24 12:49:18 +01:00
KillerTurtleSoftware e034c6e66e Corrected tone_key.cpp Motorola Code
Corrected Motorola Code on a few freqs.
2023-03-23 08:46:01 -05:00
gullradriel bb3ba29d22 Merge pull request #849 from gullradriel/level-rssi-minor-fix
Level rssi minor fix
2023-03-23 13:35:04 +01:00
GullCode ad11ab649e Changed default RSSI peak value 2023-03-23 13:34:15 +01:00
GullCode c76c6bd857 Changed default RSSI peak value, position and size of RSSI graph 2023-03-23 13:33:59 +01:00
GullCode 47b770bc3a Changed RSSIGraph drawing system 2023-03-23 13:33:22 +01:00
gullradriel dd8538b8bf Merge pull request #848 from gullradriel/rssi-peak-level-tune
Rssi peak level tune
2023-03-22 14:40:21 +01:00
GullCode 03c9b4aba7 fixed horizontal peak hold, adjusted horizontal and vertical size 2023-03-22 14:38:47 +01:00
GullCode 56526a5710 enabled 1s peak hold on rssi view 2023-03-22 14:38:00 +01:00
GullCode 2d9fe6cf8a Set 1s peak as default, set 32x resolution as default 2023-03-22 14:37:31 +01:00
gullradriel 4ec444688f Merge pull request #846 from gullradriel/audio_app_freq_fix
forcing input freq to be set in all cases
2023-03-22 11:42:15 +01:00
GullCode 2e4a86d7ec forcing input freq to be set in all cases 2023-03-22 11:39:00 +01:00
gullradriel 92b622deac Added HamItUp option (#840)
* Added HamItUp option to allow quick offset of the tuned frequency
* New HamItUp icon for top bar
* HamItUp checkbox status and frequency persistent settings in Settings/Radio
2023-03-22 08:46:58 +01:00
gullradriel 2457ba016f Merge pull request #845 from gullradriel/app-settings-update
Quoted restore frequency at start for audio app
2023-03-22 08:44:01 +01:00
GullCode 4a6c76cc47 Quoted restore frequency at start for a good interactions with other apps calling audio app 2023-03-21 22:45:49 +01:00
Bernd Herzog 7b4662fe4a fixed packing 2023-03-21 22:01:09 +01:00
Bernd Herzog 8fcedb017c fixed script 2023-03-21 21:31:59 +01:00
Bernd Herzog 7b3ab64930 fixed data packing 2023-03-21 21:27:32 +01:00
Bernd Herzog b856b047cc added compression of baseband images 2023-03-21 19:18:38 +01:00
gullradriel ce23c7d882 Merge pull request #841 from gullradriel/looking-glass-update
fixing width too tiny for calculation. It has to be a multiple of 240
2023-03-20 16:45:33 +01:00
GullCode 7eba8fd49b fixing width too tiny for calculation. It has to be a multiple of 240 2023-03-20 16:43:31 +01:00
gullradriel bfdf7660b6 Merge pull request #838 from bernd-herzog/error_handling
improved runtime error details in guru meditation
2023-03-19 08:40:25 +01:00
Bernd Herzog b83f43793c improved runtime error handling 2023-03-18 23:19:23 +01:00
Bernd Herzog ed1c2e1c03 Merge remote-tracking branch 'origin/next' into error_handling 2023-03-18 19:35:22 +01:00
Bernd Herzog caba16a549 Merge remote-tracking branch 'origin/next' into error_handling 2023-03-18 16:39:56 +01:00
gullradriel 5318542021 Merge pull request #836 from gullradriel/looking-glass-update
Looking glass update
2023-03-16 17:41:22 +01:00
GullCode 93f966bdcd Fixing 20MHz boudaries 2023-03-16 12:09:13 +01:00
GullCode 7b5f138dfb Fixing max<min case 2023-03-16 12:03:00 +01:00
GullCode 149cd755ca Restoring min step of 1, forcing 20Mhz minimum wide view 2023-03-16 11:56:24 +01:00
GullCode 63d0201b71 Fixing set freq and min step value 2023-03-16 11:32:26 +01:00
GullCode cf19408784 Merge branch 'next' of github.com:gullradriel/portapack-mayhem into looking-glass-update 2023-03-16 10:40:11 +01:00
gullradriel f882de5eff Merge pull request #835 from gullradriel/recon-update
Recon update
2023-03-16 10:36:24 +01:00
GullCode 49bda24079 added steps config and thus possibility to adjust config / see under 240 MHz 2023-03-16 10:33:36 +01:00
GullCode a1705dd9d4 reoganised things a little, added nb_columns management in ui 2023-03-14 22:23:16 +01:00
GullCode 155543e5d5 Added nb_columns, changed drawing due to column system 2023-03-14 22:22:49 +01:00
gullradriel 62b9fa2b6a Merge pull request #834 from gullradriel/recon-update
Recon minor update, RSSI widget get 'on touch'
2023-03-14 00:28:22 +01:00
GullCode f5427d7fdc UI adjustements 2023-03-14 00:26:25 +01:00
GullCode b8c1e8c4a6 Added on_touch on RSSI widget 2023-03-14 00:26:12 +01:00
gullradriel 7a0d02cb83 Merge pull request #833 from gullradriel/recon-update
Recon minor update
2023-03-13 23:29:37 +01:00
gullradriel c23f03c074 Merge branch 'eried:next' into recon-update 2023-03-13 23:27:44 +01:00
GullCode cfdd12d8a6 aligned OPT to the right 2023-03-13 23:27:04 +01:00
gullradriel d1ab93f644 Merge pull request #832 from gullradriel/recon-update
Recon update
2023-03-13 23:18:26 +01:00
GullCode f7df4f3575 Use rssi focusable, added shortcut to level app, glass app, cut buttons a bit better in the ui, better pause/resume/skiplock/unlock button 2023-03-13 23:14:06 +01:00
GullCode ad4cfe5a44 adding header guard 2023-03-13 23:12:46 +01:00
GullCode 8db44d1a92 fixed a text size, used new rssi focusable property so you can launch the level app by touching or selecting and clicking the rssi bars 2023-03-13 22:56:47 +01:00
GullCode 37cfcb0d5c added focusable ability to rssi field 2023-03-13 22:55:48 +01:00
gullradriel 6a61b9528e Merge pull request #831 from gullradriel/level-app-updates
Level app updates
2023-03-13 15:59:24 +01:00
gullradriel ab58f368b5 Merge branch 'eried:next' into level-app-updates 2023-03-13 15:56:41 +01:00
gullradriel 023ee2a6f5 Merge pull request #830 from bernd-herzog/error_handling
added runtime error details to guru meditation
2023-03-13 15:50:27 +01:00
Bernd Herzog b6011a777b added runtime error details to guru meditation 2023-03-13 15:04:00 +01:00
GullCode 37494500b2 Added display of rssi get delta, swapped audio and steps button for convenience 2023-03-13 14:04:23 +01:00
GullCode f42812c6b4 Added rssi get_delta, better rssi min graph view 2023-03-13 14:03:37 +01:00
GullCode a7acb37a56 added 0.1 and 1Khz steps 2023-03-13 14:02:29 +01:00
gullradriel 69df16d6e2 Level meter (#827)
* added possibility to scale vertically from bottom to top

* Squeleton of Level app from Recon App

* Working LevelApp

* Tweaking peak and display or RSSI chart

* Moved widgets, prepared audio decode, added working ctcss display and peak hold max rssi val

* Added RSSIGraph

* Updated Level to use RSSIGraph

* Graph as lines instead as bars

* correct CTCSS hiding if not in NFM mode

* added back db value and drawing for it. clamped to [-100,20] db

* added back audio, volume, better placement for buttons, db graph

* Using different icon for Level app, unless someone provide a better one

* fixed CTCSS position

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-03-10 14:25:38 +01:00
gullradriel e2620f1a33 Merge pull request #822 from heurist1/fix-tuning-radio-when-opening-app-with-settings
Fix  the initialisation of frquency when apps are launched
2023-03-05 20:57:54 +01:00
heurist1 f6a4cc26ee Move where on_change for the frequency field is setup, so that the frequency is correctly initialised when the app is launched and settings are being used 2023-03-05 19:50:56 +00:00
gullradriel 3d248647c9 Set usable persistent memory defaults for Recon (#820)
* Set usable defaults for Recon when PortaPack is not having a valid button battery installed

* fixed indentation

* fixed indentation

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-03-04 22:04:53 +01:00
gullradriel 3c13161273 Merge pull request #818 from Brumi-2021/ADSB_squawk_investigation
Correct squawk id in ADSB TX
2023-03-04 08:10:43 +01:00
gullradriel f0b4914186 Merge pull request #819 from Brumi-2021/revert_new_spectrum_colour_scheme
Revert "New spectrum color scheme"
2023-03-04 08:09:44 +01:00
Brumi-2021 037fc098ef Revert "New spectrum color scheme"
This reverts commit f59437cf71.
2023-03-04 00:47:40 +01:00
Brumi-2021 988404df9c Correct squawk id in ADSB TX 2023-03-03 23:10:58 +01:00
jLynx f5deae56cf Merge pull request #806 from nnesetto/next
New Docker and sounds
2023-03-02 08:12:13 +13:00
gullradriel e526061d13 Merge pull request #815 from heurist1/adsb-multiple-aircraft-on-map2
ADSB RX multiple aircraft on map - 2
2023-03-01 18:41:25 +01:00
heurist1 daf63de55f Final corrections to make merged code compile, and reduce the adding and removing of other markers 2023-02-28 23:00:51 +00:00
heurist1 778111d466 Updates to put the multiple aircraft on the map
Also manage the CPU in on the one second tick to keep GUI responsive
Some other small edits that fix minor problems from my previous checkins
2023-02-28 20:19:23 +00:00
heurist1 00b75eacda Update map to allow multiple markers 2023-02-28 20:14:15 +00:00
heurist1 ef151e243b Imprment string trimr to remove spaces at the end of aircraft name.
(cherry picked from commit 70fd4039938cdd45f03d541e0aff1238de85fb3e)
2023-02-28 20:00:56 +00:00
heurist1 567fee1d98 Create optimised integer functions to reduce CPU usage
(cherry picked from commit a47bfe1da7bffe9f752e4c522e11593cce6dffd0)
2023-02-28 20:00:39 +00:00
heurist1 b549d3a4f1 Add ability to draw text with a transparent background
(cherry picked from commit a15da2e136147b31ab53058871815c8eb759576a)
2023-02-28 20:00:05 +00:00
gullradriel 86203f090f Merge pull request #812 from euquiq/mayhem-version
Looking Glass optimizations
2023-02-28 08:03:06 +01:00
euquiq 19fed78b7f Looking Glass optimizations 2023-02-27 23:29:13 -03:00
ittercho c0532aeafa Merge branch 'eried:next' into next 2023-02-22 23:24:38 +00:00
Erwin Ried c8774e82d9 Merge pull request #808 from zxkmm/next
Add config files for FREQMAN
2023-02-22 14:07:27 +01:00
zxkmm 62ee23a314 Add config files for FREQMAN 2023-02-22 21:00:15 +08:00
ittercho 70551ef155 Format 2023-02-19 08:35:25 +01:00
ittercho 91074e5b4e Add file 2023-02-19 08:35:19 +01:00
gullradriel 5f95cec5f4 Merge pull request #805 from phil-stumpy/next
Adding support for HackRF One R9
2023-02-16 13:48:01 +01:00
phil-stumpy 66ba6442b1 Adding support for HackRF One R9, as per https://github.com/sharebrained/portapack-hackrf/pull/187 2023-02-16 12:10:26 +00:00
gullradriel 7e20cea6d7 Merge pull request #800 from gullradriel/next
Updated hackRF submodule
2023-02-07 14:00:54 +01:00
GullCode 65217856c0 Updated hackRF submodule 2023-02-07 13:59:00 +01:00
gullradriel 5117538b49 Merge pull request #799 from lujji/fix-ook-app
Fix OOK app
2023-02-04 16:41:06 +01:00
lujji 362c208078 fix ook app and 1527 encoder; add scan encoding 2023-02-02 17:54:36 +02:00
Erwin Ried 4f9e8e9642 Update README.md 2023-01-25 15:34:09 +01:00
Erwin Ried de53626a46 Update README.md 2023-01-25 15:33:00 +01:00
Erwin Ried 2cf6849985 Update README.md 2023-01-25 15:16:03 +01:00
Erwin Ried 1cde3ab189 Update README.md 2023-01-25 15:12:21 +01:00
Erwin Ried bdb70a3231 Update README.md 2023-01-25 15:04:43 +01:00
Erwin Ried 49e63e2728 Update README.md 2023-01-25 14:36:18 +01:00
Erwin Ried 1beca57e56 Update README.md 2023-01-25 14:33:27 +01:00
Erwin Ried 27632825a3 Update contributors list 2023-01-25 14:14:12 +01:00
gullradriel e02e58b366 Merge pull request #789 from formtapez/settings
Save and recall all receiver settings of the Audio RX app
2023-01-21 13:04:52 +01:00
formtapez 37912df085 tabs vs spaces 2023-01-21 11:46:43 +01:00
formtapez a20a08c630 save/recall all receiver settings 2023-01-21 11:36:06 +01:00
formtapez a8885282cd save and recall stepsize 2023-01-20 22:21:59 +01:00
gullradriel 2d9090862b Merge pull request #782 from Brumi-2021/adding_Vertical_rate_field_ADSB_TX
Added Vertical rate field ADSB-TX
2023-01-05 09:59:00 +01:00
Brumi-2021 6e5bb7911c Added Vertical rate field ADSB-TX 2023-01-05 00:14:11 +01:00
Brumi-2021 46f98ddc8b Merge pull request #780 from Brumi-2021/Correcting_complementary_Bearing_Compas_Hdg_angle_ADSB-TX_App_final
Solving correct  Bearing Angle in ADSB TX app
2023-01-01 23:15:52 +01:00
Brumi-2021 2902365b80 Solving correct Bearing Angle in ADSB TX app 2023-01-01 23:23:06 +01:00
Brumi-2021 c0f368c978 Merge pull request #775 from Brumi-2021/Small_BW_Label_error_correction_Scanner_App
Scanner BW label corrections
2022-12-29 23:23:41 +01:00
Brumi-2021 75c2b69f19 Scanner BW label corrections 2022-12-30 00:38:54 +01:00
Brumi-2021 3291c7e2a0 Merge pull request #774 from Brumi-2021/Minor_ADSB_Compas_sinf32_array_value_correction_and_comments
Minor sin_f32_table correction and comments
2022-12-29 22:24:28 +01:00
Brumi-2021 64ca9f6a68 Minor sin_f32_table correction and comments 2022-12-29 23:27:15 +01:00
gullradriel 297063873f Merge pull request #767 from rusty-labs/scanner-manual-mode-unlimited-range
#765 Scanner app, unlimited range for manual mode
2022-12-29 17:41:00 +01:00
rusty.labs 1f7b800c2a #765 unlimited range for manual mode
* fix for #765, manual mode has unlimited range now
* speedup for button_add.on_select (roughly x70 times faster)
* fix for random freezes while switching to Audio
2022-12-26 17:18:30 -05:00
218 changed files with 50232 additions and 8032 deletions
+1
View File
@@ -0,0 +1 @@
build
+1 -1
View File
@@ -9,7 +9,7 @@ assignees: ''
----
(Please try the latest nightly release before submitting this. You can find the latest nightly verison here: https://github.com/eried/portapack-mayhem/releases)
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/eried/portapack-mayhem/releases)
----
@@ -31,7 +31,7 @@ You can find the list of versions here: https://github.com/eried/portapack-mayhe
**Affected versions**
Please tell us what version you are running.
Also please try the latest nightly release before submitting this.
If your device is still functional, try the latest nightly release before submitting this.
You can find the latest nightly version here https://github.com/eried/portapack-mayhem/releases
**Were you able to update the firmware before?**
+1 -1
View File
@@ -1 +1 @@
v1.5.4
v1.6.0
+1 -1
View File
@@ -1 +1 @@
v1.6.0
v1.7.0
-1
View File
@@ -53,4 +53,3 @@ addons:
- sed
- cmake
- dfu-util
+1 -1
View File
@@ -45,7 +45,7 @@ endif()
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
add_subdirectory(hackrf/firmware/hackrf_usb)
add_subdirectory(hackrf-portapack)
set(HACKRF_FIRMWARE_DFU_FILENAME hackrf_usb.dfu)
set(HACKRF_FIRMWARE_BIN_FILENAME hackrf_usb_ram.bin)
+16 -23
View File
@@ -6,7 +6,7 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
[<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
# Quick overview
@@ -18,44 +18,40 @@ If you are new to *HackRF+PortaPack+Mayhem*, there is an awesome introductory vi
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) is always welcomed and appreciated.
## Does it work on H1/H2 PortaPack?
## What to buy?
Yes, both devices are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk), which is a kit that includes everything you need. Sadly, the people making the H2 never made the updated schematics available, which is not ideal (and goes against the terms of the license). Most members of the community are using a clone of the [PortaPack H1+HackRF+metal case](https://s.click.aliexpress.com/e/_dS6liw4), which does not include any battery functionality, but it is a cheaper alternative.
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX) pack, that includes everything you need. Sadly, the people making the H2 never made the updated schematics available (against the terms of the license).
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
## Where is the latest firmware?
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
![image](https://user-images.githubusercontent.com/1091420/214579017-9ad970b9-0917-48f6-a550-588226d3f89b.png)
The current **stable release** is on the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/eried/portapack-mayhem?label=Releases&style=social)](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
:warning: If it looks **too different**, this might mean that they are using their own recipe, check the [different models](https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions) in our wiki. For example all the H3 and clones of that version use their own version of the firmware. They do not contribute the changes back and eventually you will be left with a device that nobody maintains:
## Is this the newest firmware for my PortaPack?
Most probably: **YES**. *If you find new features somewhere else, please [suggest](https://github.com/eried/portapack-mayhem/issues/new/choose) them*.
![image](https://user-images.githubusercontent.com/1091420/214581333-424900ee-26f8-4e96-be2f-69d8dc995ba9.png)
## Which one is actually the newest?
There is a lot of confusion of which is the latest version because no old version used any actual "version number". Additionally, since the files were distributed on facebook groups, github issue links and similar temporal sources, then there was no central location for them.
## Where is the latest version?
This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://en.wikipedia.org/wiki/Software_versioning), so you can always compare your current version with the latest from [Releases](https://github.com/eried/portapack-mayhem/releases/latest).
## What about Havoc/GridRF/jamesshao8/jboone's?
* jboone's PortaPack: the [vanilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
* Havoc: It was the most popular fork of jboone's PortaPack, currrently, it is not being maintained nor updated
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything. Latest functions do not follow the license and are not being published with source code, so keep this in mind
* GridRF: They sell PortaPack clones with their own firmware based on a old version, which has no sourcecode available
The current **stable release** is on the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/eried/portapack-mayhem?label=Releases&style=social)](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description. The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
## How can I collaborate
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) with details.
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try colaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try collaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
[![Contributors](https://contrib.rocks/image?repo=eried/portapack-mayhem)](https://github.com/eried/portapack-mayhem/graphs/contributors)
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
As a last option, if you want to send money directly to me for getting more boards, antennas and such:
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CBPQA4HRRPJQ6&source=url)
## What if I really want to pay for something?
## What if I really want something specific?
If what you need can be relevant in general, you can [request a feature](https://github.com/eried/portapack-mayhem/issues/new?labels=enhancement&template=feature_request.md).
You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).
Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).
@@ -64,6 +60,3 @@ Promote your bounty over our Discord by clicking the chat badge on [top](#portap
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook, and our Discord by clicking the chat badge on [top](#portapack-mayhem).
## What if I find incongruencies, or grammatical errors in the text?
If is on the [Wiki](https://github.com/eried/portapack-mayhem/wiki), you can modify it directly. If is on files of the repository, you can send corrections as [pull requests](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate#coding-new-stuff-or-fixing-bugs). As a last resource, open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
+9
View File
@@ -0,0 +1,9 @@
version: '3.9'
services:
portapack-mayhem-dev:
build:
context: .
dockerfile: dockerfile-nogit-alpine
volumes:
- ".:/havoc"
+9
View File
@@ -0,0 +1,9 @@
version: '3.9'
services:
portapack-mayhem-dev:
build:
context: .
dockerfile: dockerfile-alpine
volumes:
- "./bin:/havocbin"
+8 -8
View File
@@ -14,7 +14,7 @@ COPY ./ /havocsrc
#Fetch dependencies from APT
RUN apt-get update && \
apt-get install -y tar wget dfu-util cmake python bzip2 curl python3 python3-yaml && \
apt-get install -y tar wget dfu-util cmake python bzip2 lz4 curl python3 python3-yaml && \
apt-get -qy autoremove
#Install current pip from PyPa
@@ -24,9 +24,9 @@ RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
#Fetch additional dependencies from Python 2.x pip
RUN pip install pyyaml
RUN rm -rf /usr/bin/python && \
rm -rf /usr/bin/pip && \
ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
rm -rf /usr/bin/pip && \
ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
@@ -37,12 +37,12 @@ RUN mkdir /opt/build && cd /opt/build && \
wget -O gcc-arm-none-eabi $ARMBINURL && \
mkdir armbin && \
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
#Set environment variable so compiler knows where the toolchain lives
ENV PATH=$PATH:/opt/build/armbin/bin
CMD cd /havocsrc && \
mkdir build && cd build && \
cmake .. && make firmware && \
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
mkdir build && cd build && \
cmake .. && make firmware && \
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
+29
View File
@@ -0,0 +1,29 @@
# Download base image.
FROM alpine:edge
# Download ARM toolkit.
RUN apk add --no-cache gcc-arm-none-eabi gcc
# Create volume /havocbin for compiled firmware binaries
VOLUME /havocbin
# Copy build context (repository root) to /havocsrc
COPY ./ /havocsrc
# Fetch dependencies from APK
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk update -U
RUN apk add --no-cache git tar wget cmake curl bzip2 lz4 make
RUN apk add --no-cache dfu-util ccache icu-data-full
RUN apk add --no-cache python3 py3-pip py3-yaml
RUN apk add --no-cache py3-pyyaml-env-tag
RUN apk add --no-cache g++ gcc clang clang-static clang-dev llvm-dev llvm-static
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
CMD cd /havocsrc && \
mkdir build && cd build && \
cmake .. && make firmware && \
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
+3 -3
View File
@@ -11,7 +11,7 @@ WORKDIR /havoc/firmware
# Fetch dependencies from APT
RUN apt-get update && \
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 curl && \
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
apt-get -qy autoremove
#Install current pip from PyPa
@@ -21,7 +21,7 @@ RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
#Fetch additional dependencies from Python 3.x pip
RUN pip install pyyaml
RUN ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
ln -s /usr/bin/pip3 /usr/bin/pip
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
@@ -38,4 +38,4 @@ RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
CMD cd .. && cd build && \
cmake .. && make firmware
cmake .. && make firmware
+27
View File
@@ -0,0 +1,27 @@
FROM alpine:edge
# Download ARM toolkit.
RUN apk add --no-cache gcc-arm-none-eabi gcc
# Create volume /havoc/bin for compiled firmware binaries
VOLUME /havoc
WORKDIR /havoc/firmware
# Fetch dependencies from APK
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk update -U
RUN apk add --no-cache git tar wget cmake curl bzip2 lz4 make
RUN apk add --no-cache dfu-util ccache icu-data-full
RUN apk add --no-cache python3 py3-pip py3-yaml
RUN apk add --no-cache py3-pyyaml-env-tag
RUN apk add --no-cache g++ gcc clang clang-static clang-dev llvm-dev llvm-static
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
# Configure CCACHE
RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
CMD cd .. && cd build && \
cmake .. && make firmware
+1
View File
@@ -28,6 +28,7 @@ set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
set(LZ4 lz4)
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
+12 -2
View File
@@ -103,6 +103,7 @@ set(CSRC
${PLATFORMSRC}
${BOARDSRC}
${FATFSSRC}
firmware_info.c
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
@@ -124,7 +125,7 @@ set(CPPSRC
${COMMON}/cpld_max5.cpp
${COMMON}/cpld_update.cpp
${COMMON}/cpld_xilinx.cpp
${COMMON}/debug.cpp
debug.cpp
${COMMON}/ert_packet.cpp
${COMMON}/event.cpp
${COMMON}/gcc.cpp
@@ -154,6 +155,7 @@ set(CPPSRC
${COMMON}/ui_widget.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/performance_counter.cpp
app_settings.cpp
audio.cpp
baseband_api.cpp
@@ -193,6 +195,7 @@ set(CPPSRC
hw/debounce.cpp
hw/encoder.cpp
hw/max2837.cpp
hw/max2839.cpp
hw/max5864.cpp
hw/rffc507x.cpp
hw/rffc507x_spi.cpp
@@ -228,10 +231,13 @@ set(CPPSRC
apps/ui_nrf_rx.cpp
apps/ui_aprs_tx.cpp
apps/ui_bht_tx.cpp
apps/ui_dfu_menu.cpp
apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_encoders.cpp
apps/ui_fileman.cpp
apps/ui_flash_utility.cpp
apps/ui_sd_over_usb.cpp
apps/ui_freqman.cpp
apps/ui_jammer.cpp
apps/ui_keyfob.cpp
@@ -272,6 +278,7 @@ set(CPPSRC
apps/soundboard_app.cpp
apps/ui_recon.cpp
apps/ui_recon_settings.cpp
apps/ui_level.cpp
apps/tpms_app.cpp
apps/tpms_app.cpp
protocols/aprs.cpp
@@ -314,7 +321,10 @@ set(TCSRC)
set(TCPPSRC)
# List ASM source files here
set(ASMSRC ${PORTASM})
set(ASMSRC
${PORTASM}
lz4.S
)
set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC}
${HALINC} ${PLATFORMINC} ${BOARDINC}
+14 -1
View File
@@ -51,6 +51,13 @@ int app_settings::load(std::string application, AppSettings* settings) {
settings->tx_amp=std::app_settings::read_long_long(file_content, "tx_amp=");
settings->tx_frequency=std::app_settings::read_long_long(file_content, "tx_frequency=");
settings->tx_gain=std::app_settings::read_long_long(file_content, "tx_gain=");
settings->step=std::app_settings::read_long_long(file_content, "step=");
settings->modulation=std::app_settings::read_long_long(file_content, "modulation=");
settings->am_config_index=std::app_settings::read_long_long(file_content, "am_config_index=");
settings->nbfm_config_index=std::app_settings::read_long_long(file_content, "nbfm_config_index=");
settings->wfm_config_index=std::app_settings::read_long_long(file_content, "wfm_config_index=");
settings->squelch=std::app_settings::read_long_long(file_content, "squelch=");
rc = SETTINGS_OK;
}
else rc = SETTINGS_UNABLE_TO_LOAD;
@@ -61,7 +68,7 @@ int app_settings::load(std::string application, AppSettings* settings) {
int app_settings::save(std::string application, AppSettings* settings) {
if (portapack::persistent_memory::save_app_settings()) {
if (portapack::persistent_memory::save_app_settings()) {
file_path = folder+"/"+application+".ini";
make_new_directory(folder);
@@ -79,6 +86,12 @@ int app_settings::save(std::string application, AppSettings* settings) {
// Save other settings from struct
settings_file.write_line("rx_frequency="+to_string_dec_uint(settings->rx_frequency));
settings_file.write_line("tx_frequency="+to_string_dec_uint(settings->tx_frequency));
settings_file.write_line("step="+to_string_dec_uint(settings->step));
settings_file.write_line("modulation="+to_string_dec_uint(settings->modulation));
settings_file.write_line("am_config_index="+to_string_dec_uint(settings->am_config_index));
settings_file.write_line("nbfm_config_index="+to_string_dec_uint(settings->nbfm_config_index));
settings_file.write_line("wfm_config_index="+to_string_dec_uint(settings->wfm_config_index));
settings_file.write_line("squelch="+to_string_dec_uint(settings->squelch));
rc = SETTINGS_OK;
}
+5
View File
@@ -59,6 +59,11 @@ public:
uint32_t tx_frequency;
uint8_t tx_gain;
uint8_t vga;
uint32_t step;
uint8_t am_config_index;
uint8_t nbfm_config_index;
uint8_t wfm_config_index;
uint8_t squelch;
};
int load(std::string application, AppSettings* settings);
+1 -1
View File
@@ -166,7 +166,7 @@ public:
void focus() override;
std::string title() const override { return "AIS Boats RX"; };
std::string title() const override { return "AIS RX"; };
private:
static constexpr uint32_t initial_target_frequency = 162025000;
+27 -15
View File
@@ -129,23 +129,26 @@ AnalogAudioView::AnalogAudioView(
&waterfall
});
// Set on_change before initialising the field
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
// load app settings
auto rc = settings.load("rx_audio", &app_settings);
if(rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
receiver_model.set_rf_amp(app_settings.rx_amp);
field_frequency.set_value(app_settings.rx_frequency);
// field_frequency.set_value(app_settings.rx_frequency);
receiver_model.set_configuration_without_init(static_cast<ReceiverModel::Mode>(app_settings.modulation), app_settings.step, app_settings.am_config_index, app_settings.nbfm_config_index, app_settings.wfm_config_index, app_settings.squelch);
}
else field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_value(receiver_model.tuning_frequency());
//Filename Datetime and Frequency
record_view.set_filename_date_frequency(true);
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
@@ -193,36 +196,45 @@ AnalogAudioView::AnalogAudioView(
audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
}
size_t AnalogAudioView::get_spec_bw_index() {
return spec_bw_index;
return spec_bw_index;
}
void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
spec_bw_index = index;
spec_bw = bw;
spec_bw_index = index;
spec_bw = bw;
baseband::set_spectrum(bw, spec_trigger);
receiver_model.set_sampling_rate(bw);
receiver_model.set_baseband_bandwidth(bw/2);
baseband::set_spectrum(bw, spec_trigger);
receiver_model.set_sampling_rate(bw);
receiver_model.set_baseband_bandwidth(bw/2);
}
uint16_t AnalogAudioView::get_spec_trigger() {
return spec_trigger;
return spec_trigger;
}
void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
spec_trigger = trigger;
spec_trigger = trigger;
baseband::set_spectrum(spec_bw, spec_trigger);
baseband::set_spectrum(spec_bw, spec_trigger);
}
AnalogAudioView::~AnalogAudioView() {
// save app settings
app_settings.rx_frequency = field_frequency.value();
app_settings.lna = receiver_model.lna();
app_settings.vga = receiver_model.vga();
app_settings.rx_amp = receiver_model.rf_amp();
app_settings.step = receiver_model.frequency_step();
app_settings.modulation = (uint8_t)receiver_model.modulation();
app_settings.am_config_index = receiver_model.am_configuration();
app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
app_settings.wfm_config_index = receiver_model.wfm_configuration();
app_settings.squelch = receiver_model.squelch_level();
settings.save("rx_audio", &app_settings);
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
@@ -117,6 +117,7 @@ private:
{ " 2m ", 2000000 },
{ " 1m ", 1000000 },
{ "500k", 500000 },
{ "100k", 100000 },
}
};
+4 -3
View File
@@ -57,6 +57,10 @@ AnalogTvView::AnalogTvView(
&tv
});
// Set on_change before initialising the field
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
// load app settings
auto rc = settings.load("rx_tv", &app_settings);
@@ -70,9 +74,6 @@ AnalogTvView::AnalogTvView(
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
+1 -1
View File
@@ -53,7 +53,7 @@ public:
void focus() override;
std::string title() const override { return "Analog TV RX"; };
std::string title() const override { return "AnalogTV RX"; };
private:
static constexpr ui::Dim header_height = 3 * 16;
+3 -1
View File
@@ -41,6 +41,7 @@ std::string type(Packet::Type value) {
case Packet::Type::Unknown: return "???";
case Packet::Type::IDM: return "IDM";
case Packet::Type::SCM: return "SCM";
case Packet::Type::SCMPLUS: return "SCM+";
}
}
@@ -62,7 +63,8 @@ std::string commodity_type(CommodityType value) {
void ERTLogger::on_packet(const ert::Packet& packet) {
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
std::string entry = ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
+1 -1
View File
@@ -125,7 +125,7 @@ public:
void focus() override;
std::string title() const override { return "ERT Meter RX"; };
std::string title() const override { return "ERT RX"; };
private:
ERTRecentEntries recent { };
+5 -3
View File
@@ -77,6 +77,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
&console
});
// Set on_change before initialising the field
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
// load app settings
auto rc = settings.load("rx_pocsag", &app_settings);
if(rc == SETTINGS_OK) {
@@ -94,9 +99,6 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
receiver_model.enable();
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
+7 -1
View File
@@ -161,10 +161,16 @@ void ReplayAppView::start() {
sample_rate * 8,
baseband_bandwidth,
rf::Direction::Transmit,
rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps
rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga())
});
if (portapack::persistent_memory::stealth_mode()){
DisplaySleepMessage message;
EventDispatcher::send_message(message);
}
}
void ReplayAppView::stop(const bool do_loop) {
+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
@@ -46,7 +46,7 @@ public:
void focus() override;
std::string title() const override { return "Soundboard TX"; };
std::string title() const override { return "Soundbrd TX"; };
private:
NavigationView& nav_;
+27 -43
View File
@@ -35,6 +35,7 @@ namespace tpms {
namespace format {
static bool use_kpa = true;
static bool use_celsius = true;
std::string type(Reading::Type type) {
return to_string_dec_uint(toUType(type), 2);
@@ -45,15 +46,11 @@ std::string id(TransponderID id) {
}
std::string pressure(Pressure pressure) {
if(use_kpa){
return to_string_dec_int(pressure.kilopascal(), 3);
}
return to_string_dec_int(pressure.psi(), 3);
return to_string_dec_int(use_kpa? pressure.kilopascal():pressure.psi(), 3);
}
std::string temperature(Temperature temperature) {
return to_string_dec_int(temperature.celsius(), 3);
return to_string_dec_int(use_celsius? temperature.celsius():temperature.fahrenheit(), 3);
}
std::string flags(Flags flags) {
@@ -111,15 +108,15 @@ void RecentEntriesTable<TPMSRecentEntries>::draw(
std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id);
if( entry.last_pressure.is_valid() ) {
line += " " + tpms::format::pressure(entry.last_pressure.value());
line += " " + tpms::format::pressure(entry.last_pressure.value());
} else {
line += " " " ";
line += " " " ";
}
if( entry.last_temperature.is_valid() ) {
line += " " + tpms::format::temperature(entry.last_temperature.value());
line += " " + tpms::format::temperature(entry.last_temperature.value());
} else {
line += " " " ";
line += " " " ";
}
if( entry.received_count > 999 ) {
@@ -145,10 +142,12 @@ TPMSAppView::TPMSAppView(NavigationView&) {
&rssi,
&channel,
&options_band,
&options_pressure,
&options_temperature,
&field_rf_amp,
&field_lna,
&field_vga,
&options_type,
&recent_entries_view
});
// load app settings
@@ -181,16 +180,19 @@ TPMSAppView::TPMSAppView(NavigationView&) {
};
options_band.set_by_value(target_frequency());
options_type.on_change = [this](size_t, int32_t i) {
if (i == 0){
tpms::format::use_kpa = true;
} else if (i == 1){
tpms::format::use_kpa = false;
}
update_type();
options_pressure.on_change = [this](size_t, int32_t i) {
tpms::format::use_kpa = !i;
update_view();
};
options_type.set_selected_index(0, true);
options_pressure.set_selected_index(0, true);
options_temperature.on_change = [this](size_t, int32_t i) {
tpms::format::use_celsius = !i;
update_view();
};
options_temperature.set_selected_index(0, true);
logger = std::make_unique<TPMSLogger>();
if( logger ) {
@@ -214,16 +216,8 @@ void TPMSAppView::focus() {
options_band.focus();
}
void TPMSAppView::update_type() {
if (tpms::format::use_kpa){
remove_child(&recent_entries_view_psi);
add_child(&recent_entries_view_kpa);
recent_entries_view_kpa.set_parent_rect(view_normal_rect);
} else {
remove_child(&recent_entries_view_kpa);
add_child(&recent_entries_view_psi);
recent_entries_view_psi.set_parent_rect(view_normal_rect);
}
void TPMSAppView::update_view() {
recent_entries_view.set_parent_rect(view_normal_rect);
}
void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
@@ -231,7 +225,7 @@ void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
view_normal_rect = { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height };
update_type();
update_view();
}
void TPMSAppView::on_packet(const tpms::Packet& packet) {
@@ -244,23 +238,13 @@ void TPMSAppView::on_packet(const tpms::Packet& packet) {
const auto reading = reading_opt.value();
auto& entry = ::on_packet(recent, TPMSRecentEntry::Key { reading.type(), reading.id() });
entry.update(reading);
if(tpms::format::use_kpa){
recent_entries_view_kpa.set_dirty();
} else {
recent_entries_view_psi.set_dirty();
}
recent_entries_view.set_dirty();
}
}
void TPMSAppView::on_show_list() {
if(tpms::format::use_kpa){
recent_entries_view_kpa.hidden(false);
recent_entries_view_kpa.focus();
} else {
recent_entries_view_psi.hidden(false);
recent_entries_view_psi.focus();
}
recent_entries_view.hidden(false);
recent_entries_view.focus();
}
void TPMSAppView::on_band_changed(const uint32_t new_band_frequency) {
+17 -18
View File
@@ -102,8 +102,8 @@ public:
void paint(Painter&) override { };
void focus() override;
std::string title() const override { return "TPMS Cars RX"; };
std::string title() const override { return "TPMS RX"; };
private:
static constexpr uint32_t initial_target_frequency = 315000000;
@@ -144,7 +144,7 @@ private:
}
};
OptionsField options_type {
OptionsField options_pressure {
{ 5 * 8, 0 * 16 },
3,
{
@@ -153,6 +153,15 @@ private:
}
};
OptionsField options_temperature {
{ 9 * 8, 0 * 16 },
1,
{
{ "C", 0 },
{ "F", 1 }
}
};
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
@@ -168,31 +177,21 @@ private:
TPMSRecentEntries recent { };
std::unique_ptr<TPMSLogger> logger { };
const RecentEntriesColumns columns_kpa { {
const RecentEntriesColumns columns { {
{ "Tp", 2 },
{ "ID", 8 },
{ "kPa", 3 },
{ "C", 3 },
{ "Pres", 4 },
{ "Temp", 4 },
{ "Cnt", 3 },
{ "Fl", 2 },
} };
TPMSRecentEntriesView recent_entries_view_kpa { columns_kpa, recent };
const RecentEntriesColumns columns_psi { {
{ "Tp", 2 },
{ "ID", 8 },
{ "PSI", 3 },
{ "C", 3 },
{ "Cnt", 3 },
{ "Fl", 2 },
} };
TPMSRecentEntriesView recent_entries_view_psi { columns_psi, recent };
TPMSRecentEntriesView recent_entries_view { columns, recent };
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const tpms::Packet& packet);
void on_show_list();
void update_type();
void update_view();
void on_band_changed(const uint32_t new_band_frequency);
@@ -38,7 +38,7 @@ namespace ui
console.writeln("Brumi-2021,texasyojimbo");
console.writeln("heurist1,intoxsick,ckuethe");
console.writeln("notpike,jLynx,zigad");
console.writeln("MichalLeonBorsuk");
console.writeln("MichalLeonBorsuk,jimilinuxguy");
console.writeln("");
break;
+114 -58
View File
@@ -46,10 +46,10 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
auto entry_age = entry.age;
// Color decay for flights not being updated anymore
if (entry_age < ADSB_DECAY_A) {
if (entry_age < ADSB_CURRENT) {
aged_color = 0x10;
target_color = Color::green();
} else if (entry_age < ADSB_DECAY_B) {
} else if (entry_age < ADSB_RECENT) {
aged_color = 0x07;
target_color = Color::light_grey();
} else {
@@ -59,21 +59,13 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
std::string entry_string = "\x1B";
entry_string += aged_color;
#if false
entry_string += to_string_hex(entry.ICAO_address, 6) + " " +
entry.callsign + " " +
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+") + " " +
entry.time_string;
#else
// SBT
entry_string +=
(entry.callsign[0]!=' ' ? entry.callsign + " " : to_string_hex(entry.ICAO_address, 6) + " ") +
to_string_dec_uint((unsigned int)((entry.pos.altitude+50)/100),4) +
(entry.callsign[0]!=' ' ? entry.callsign + " " : entry.icaoStr + " ") +
to_string_dec_uint((unsigned int)(entry.pos.altitude/100),4) +
to_string_dec_uint((unsigned int)entry.velo.speed,4) +
to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " +
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") +
to_string_dec_uint(entry.age, 4);
#endif
painter.draw_string(
target_rect.location(),
@@ -124,8 +116,6 @@ ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView(
});
std::unique_ptr<ADSBLogger> logger { };
//update(entry_copy);
icao_code = to_string_hex(entry_copy.ICAO_address, 6);
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
@@ -225,7 +215,10 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14));
if (send_updates)
{
geomap_view->update_tag(trimr(entry.callsign[0]!=' ' ? entry.callsign : to_string_hex(entry.ICAO_address, 6)));
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude);
}
}
ADSBRxDetailsView::~ADSBRxDetailsView() {
@@ -277,19 +270,14 @@ ADSBRxDetailsView::ADSBRxDetailsView(
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
button_aircraft_details.on_select = [this, &nav](Button&) {
//detailed_entry_key = entry.key();
aircraft_details_view = nav.push<ADSBRxAircraftDetailsView>(
entry_copy,
[this]() {
send_updates = false;
});
aircraft_details_view = nav.push<ADSBRxAircraftDetailsView>(entry_copy, [this]() { send_updates = false;});
send_updates = false;
};
button_see_map.on_select = [this, &nav](Button&) {
if (!send_updates) { // Prevent recursively launching the map
geomap_view = nav.push<GeoMapView>(
entry_copy.callsign,
trimr(entry_copy.callsign[0]!=' ' ? entry_copy.callsign : entry_copy.icaoStr),
entry_copy.pos.altitude,
GeoPos::alt_unit::FEET,
entry_copy.pos.latitude,
@@ -309,6 +297,7 @@ void ADSBRxView::focus() {
}
ADSBRxView::~ADSBRxView() {
receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit
// save app settings
settings.save("rx_adsb", &app_settings);
@@ -326,8 +315,6 @@ AircraftRecentEntry ADSBRxView::find_or_create_entry(uint32_t ICAO_address) {
// If not found
if (it == std::end(recent)){
recent.emplace_front(ICAO_address); // Add it
truncate_entries(recent); // Truncate the list
sort_entries_by_state();
it = find(recent, ICAO_address); // Find it again
}
return *it;
@@ -342,6 +329,21 @@ void ADSBRxView::replace_entry(AircraftRecentEntry & entry)
entry);
}
void ADSBRxView::remove_old_entries()
{
auto it = recent.rbegin();
auto end = recent.rend();
while (it != end)
{
if (it->age_state>=4) {
std::advance(it, 1);
recent.erase( it.base() );
} else {
break; // stop looking because the list is sorted
}
}
}
void ADSBRxView::sort_entries_by_state()
{
// Sorting List pn age_state using lambda function as comparator
@@ -349,8 +351,8 @@ void ADSBRxView::sort_entries_by_state()
}
void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
logger = std::make_unique<ADSBLogger>();
rtc::RTC datetime;
std::string str_timestamp;
std::string callsign;
std::string str_info;
std::string logentry;
@@ -365,16 +367,16 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
entry.reset_age();
if (entry.hits==0)
{
entry.amp = message->amp;
entry.amp = message->amp; // Store amplitude on first hit
} else {
entry.amp = ((entry.amp*15)+message->amp)>>4;
entry.amp = ((entry.amp*15)+message->amp)>>4; // Update smoothed amplitude on updates
}
str_timestamp = to_string_datetime(datetime, HMS);
entry.set_time_string(str_timestamp);
entry.inc_hit();
logentry += to_string_hex_array(frame.get_raw_data(), 14) + " ";
logentry += "ICAO:" + to_string_hex(ICAO_address, 6) + " ";
if (logger) {
logentry += to_string_hex_array(frame.get_raw_data(), 14) + " ";
logentry += "ICAO:" + entry.icaoStr + " ";
}
if (frame.get_DF() == DF_ADSB) {
uint8_t msg_type = frame.get_msg_type();
@@ -385,7 +387,9 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) {
callsign = decode_frame_id(frame);
entry.set_callsign(callsign);
logentry+=callsign+" ";
if (logger) {
logentry+=callsign+" ";
}
}
// 9:
// 18: { // Extended squitter/non-transponder
@@ -400,30 +404,33 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
" Lat:" + to_string_decimal(entry.pos.latitude, 2) +
" Lon:" + to_string_decimal(entry.pos.longitude, 2);
// printing the coordinates in the log file with more
// resolution, as we are not constrained by screen
// real estate there:
std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_decimal(entry.pos.latitude, 7) +
" Lon:" + to_string_decimal(entry.pos.longitude, 7);
entry.set_info_string(str_info);
logentry+=log_info + " ";
if (logger) {
// printing the coordinates in the log file with more
// resolution, as we are not constrained by screen
// real estate there:
std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_decimal(entry.pos.latitude, 7) +
" Lon:" + to_string_decimal(entry.pos.longitude, 7);
logentry+=log_info + " ";
}
}
} else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){
entry.set_frame_velo(frame);
logentry += "Type:" + to_string_dec_uint(msg_sub) +
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
" Spd: "+ to_string_dec_int(entry.velo.speed);
if (logger) {
logentry += "Type:" + to_string_dec_uint(msg_sub) +
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
" Spd: "+ to_string_dec_int(entry.velo.speed);
}
}
}
replace_entry(entry);
} // frame.get_DF() == DF_ADSB
replace_entry(entry);
logger = std::make_unique<ADSBLogger>();
if (logger) {
logger->append(u"adsb.txt");
// will log each frame in format:
@@ -433,20 +440,69 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
}
}
void ADSBRxView::on_tick_second() {
// Decay and refresh if needed
for (auto& entry : recent) {
entry.inc_age();
if (details_view) {
if (send_updates && (entry.key() == detailed_entry_key)) // Check if the ICAO address match
details_view->update(entry);
void ADSBRxView::on_tick_second() {
if (recent.size() <= 16){ // Not many entries update everything (16 is one screen full)
updateDetailsAndMap(1);
updateRecentEntries();
} else if (updateState==0) { // Even second
updateState = 1;
updateDetailsAndMap(2);
} else { // Odd second only performed when there are many entries
updateState = 0;
updateRecentEntries();
}
}
void ADSBRxView::updateDetailsAndMap(int ageStep) {
ui::GeoMarker marker;
bool storeNewMarkers = false;
// Sort and truncate the entries, grouped, newest group first
sort_entries_by_state();
truncate_entries(recent);
remove_old_entries();
// Calculate if it is time to update markers
if (send_updates && details_view && details_view->geomap_view) {
ticksSinceMarkerRefresh += ageStep;
if (ticksSinceMarkerRefresh >= MARKER_UPDATE_SECONDS) { // Update other aircraft every few seconds
storeNewMarkers = true;
ticksSinceMarkerRefresh=0;
}
} else {
ticksSinceMarkerRefresh = MARKER_UPDATE_SECONDS; // Send the markers as soon as the geoview exists
}
// Sort the list if it is being displayed
if (!send_updates) {
sort_entries_by_state();
// Increment age, and pass updates to the details and map
const bool otherMarkersCanBeSent = send_updates && storeNewMarkers && details_view && details_view->geomap_view; // Save retesting all of this
MapMarkerStored markerStored = MARKER_NOT_STORED;
if (otherMarkersCanBeSent) {details_view->geomap_view->clear_markers();}
// Loop through all entries
for (auto& entry : recent) {
entry.inc_age(ageStep);
// Only if there is a details view
if (send_updates && details_view) {
if (entry.key() == detailed_entry_key) // Check if the ICAO address match
{
details_view->update(entry);
}
// Store if the view is present and the list isn't full
else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state<=2))
{
marker.lon = entry.pos.longitude;
marker.lat = entry.pos.latitude;
marker.angle = entry.velo.heading;
marker.tag = trimr(entry.callsign[0]!=' ' ? entry.callsign : entry.icaoStr);
markerStored = details_view->geomap_view->store_marker(marker);
}
}
} // Loop through all entries, if only to update the age
}
void ADSBRxView::updateRecentEntries() {
// Redraw the list of aircraft
if (!send_updates){
recent_entries_view.set_dirty();
}
}
@@ -492,7 +548,7 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
on_tick_second();
};
prevFreq = receiver_model.tuning_frequency();
prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation
baseband::set_adsb();
+23 -24
View File
@@ -25,6 +25,7 @@
#include "ui_receiver.hpp"
#include "ui_geomap.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "string_format.hpp"
#include "file.hpp"
#include "database.hpp"
@@ -39,9 +40,9 @@ using namespace adsb;
namespace ui {
#define ADSB_DECAY_A 10 // In seconds
#define ADSB_DECAY_B 30
#define ADSB_DECAY_C 60 // Can be used for removing old entries, RecentEntries already caps to 64
#define ADSB_CURRENT 10 // Seconds
#define ADSB_RECENT 30 // Seconds
#define ADSB_REMOVE 300 // Used for removing old entries
#define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id)
#define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id)
@@ -83,14 +84,15 @@ struct AircraftRecentEntry {
ADSBFrame frame_pos_even { };
ADSBFrame frame_pos_odd { };
std::string icaoStr {" "};
std::string callsign { " " };
std::string time_string { "" };
std::string info_string { "" };
AircraftRecentEntry(
const uint32_t ICAO_address
) : ICAO_address { ICAO_address }
{
this->icaoStr = to_string_hex(ICAO_address, 6);
}
Key key() const {
@@ -125,23 +127,20 @@ struct AircraftRecentEntry {
info_string = new_info_string;
}
void set_time_string(std::string& new_time_string) {
time_string = new_time_string;
}
void reset_age() {
age = 0;
}
void inc_age() {
age++;
if (age < ADSB_DECAY_A)
{
void inc_age(int delta) {
age+=delta;
if (age < ADSB_CURRENT){
age_state = pos.valid ? 0 : 1;
}
else
{
age_state = (age < ADSB_DECAY_B) ? 2 : 3;
} else if(age < ADSB_RECENT){
age_state = 2;
} else if(age < ADSB_REMOVE){
age_state = 3;
} else{
age_state = 4;
}
}
};
@@ -273,10 +272,10 @@ public:
std::database::AirlinesDBRecord airline_record = {};
GeoMapView* geomap_view { nullptr };
private:
AircraftRecentEntry entry_copy { 0 };
std::function<void(void)> on_close_ { };
GeoMapView* geomap_view { nullptr };
ADSBRxAircraftDetailsView* aircraft_details_view { nullptr };
bool send_updates { false };
std::database db = { };
@@ -364,6 +363,7 @@ public:
std::string title() const override { return "ADS-B RX"; };
void replace_entry(AircraftRecentEntry & entry);
void remove_old_entries();
AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address);
void sort_entries_by_state();
@@ -372,24 +372,23 @@ private:
std::unique_ptr<ADSBLogger> logger { };
void on_frame(const ADSBFrameMessage * message);
void on_tick_second();
int updateState = { 0 };
void updateRecentEntries();
void updateDetailsAndMap(int ageStep);
#define MARKER_UPDATE_SECONDS (5)
int ticksSinceMarkerRefresh { MARKER_UPDATE_SECONDS-1 };
// app save settings
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
const RecentEntriesColumns columns { {
#if false
{ "ICAO", 6 },
{ "Callsign", 9 },
{ "Hits", 4 },
{ "Time", 8 }
#else
{ "ICAO/Call", 9 },
{ "Lvl", 3 },
{ "Spd", 3 },
{ "Amp", 3 },
{ "Hit", 3 },
{ "Age", 4 }
#endif
} };
AircraftRecentEntries recent { };
RecentEntriesView<RecentEntries<AircraftRecentEntry>> recent_entries_view { columns, recent };
+6 -3
View File
@@ -162,13 +162,16 @@ ADSBSpeedView::ADSBSpeedView(
add_children({
&labels_speed,
&labels_vert_rate,
&compass,
&field_angle,
&field_speed
&field_speed,
&field_vert_rate
});
field_angle.set_value(0);
field_speed.set_value(400);
field_vert_rate.set_value(0);
field_angle.on_change = [this](int32_t v) {
compass.set_value(v);
@@ -181,7 +184,7 @@ void ADSBSpeedView::collect_frames(const uint32_t ICAO_address, std::vector<ADSB
ADSBFrame temp_frame;
encode_frame_velo(temp_frame, ICAO_address, field_speed.value(),
field_angle.value(), 0); // TODO: v_rate
field_angle.value(), field_vert_rate.value()); // Added v_rate , ft/min , (+) climb , (-) descend .
frame_list.emplace_back(temp_frame);
}
@@ -204,7 +207,7 @@ void ADSBSquawkView::collect_frames(const uint32_t ICAO_address, std::vector<ADS
ADSBFrame temp_frame;
(void)ICAO_address;
encode_frame_squawk(temp_frame, field_squawk.value_dec_u32());
encode_frame_squawk(temp_frame, field_squawk.concatenate_4_octal_u16());
frame_list.emplace_back(temp_frame);
}
+8
View File
@@ -96,6 +96,10 @@ private:
{ { 1 * 8, 6 * 16 }, "Speed: kn Bearing: *", Color::light_grey() }
};
Labels labels_vert_rate {
{ { 1 * 8, 8 * 16 }, "Vert. rate: ft/min, (+/-)", Color::light_grey() }
};
Compass compass {
{ 21 * 8, 2 * 16 }
};
@@ -107,6 +111,10 @@ private:
NumberField field_speed {
{ 8 * 8, 6 * 16 }, 3, { 0, 999 }, 5, ' '
};
NumberField field_vert_rate {
{ 11 * 8, 8 * 16 }, 5, { -4096, 4096 }, 64, ' ' // Let's limit to +/-5k aprox , Ex. max safe descent vertical rate aprox -1000 ft/min on an instrument approach. , std step is 64
};
};
class ADSBSquawkView : public OptionTabView {
+1 -1
View File
@@ -52,7 +52,7 @@ public:
void focus() override;
std::string title() const override { return "AFSK RX (beta)"; };
std::string title() const override { return "AFSK RX"; };
private:
void on_data(uint32_t value, bool is_data);
+1 -1
View File
@@ -179,7 +179,7 @@ public:
void focus() override;
std::string title() const override { return "BHT Xy/EP TX"; };
std::string title() const override { return "BHT TX"; };
private:
// app save settings
+1 -1
View File
@@ -40,7 +40,7 @@ public:
void focus() override;
std::string title() const override { return "BurgerPgr TX"; };
std::string title() const override { return "BurgerPgrTX"; };
private:
enum tx_modes {
+61 -21
View File
@@ -266,7 +266,9 @@ bool ControlsSwitchesWidget::on_key(const KeyEvent key) {
}
void ControlsSwitchesWidget::paint(Painter& painter) {
const std::array<Rect, 7> button_rects { {
const auto pos = screen_pos();
const std::array<Rect, 8> button_rects { {
{ 64, 32, 16, 16 }, // Right
{ 0, 32, 16, 16 }, // Left
{ 32, 64, 16, 16 }, // Down
@@ -274,27 +276,63 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
{ 32, 32, 16, 16 }, // Select
{ 16, 96, 16, 16 }, // Encoder phase 0
{ 48, 96, 16, 16 }, // Encoder phase 1
{ 96, 0, 16, 16 }, // Dfu
} };
const auto pos = screen_pos();
auto switches_raw = control::debug::switches();
auto switches_debounced = get_switches_state().to_ulong();
auto switches_event = key_event_mask;
for(const auto r : button_rects) {
const auto c =
((switches_event & 1) ?
Color::red() :
((switches_debounced & 1) ?
Color::green() :
((switches_raw & 1) ?
Color::yellow() :
Color::blue()
)
)
);
painter.fill_rectangle(r + pos, c);
painter.fill_rectangle(r + pos, Color::blue());
}
const std::array<Rect, 8> raw_rects { {
{ 64 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Right
{ 0 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Left
{ 32 + 1, 64 + 1, 16 - 2, 16 - 2 }, // Down
{ 32 + 1, 0 + 1, 16 - 2, 16 - 2 }, // Up
{ 32 + 1, 32 + 1, 16 - 2, 16 - 2 }, // Select
{ 16 + 1, 96 + 1, 16 - 2, 16 - 2 }, // Encoder phase 0
{ 48 + 1, 96 + 1, 16 - 2, 16 - 2 }, // Encoder phase 1
{ 96 + 1, 0 + 1, 16 - 2, 16 - 2 }, // Dfu
} };
auto switches_raw = control::debug::switches();
for(const auto r : raw_rects) {
if (switches_raw & 1)
painter.fill_rectangle(r + pos, Color::yellow());
switches_raw >>= 1;
}
const std::array<Rect, 6> debounced_rects { {
{ 64 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Right
{ 0 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Left
{ 32 + 2, 64 + 2, 16 - 4, 16 - 4 }, // Down
{ 32 + 2, 0 + 2, 16 - 4, 16 - 4 }, // Up
{ 32 + 2, 32 + 2, 16 - 4, 16 - 4 }, // Select
{ 96 + 2, 0 + 2, 16 - 4, 16 - 4 }, // Dfu
} };
auto switches_debounced = get_switches_state().to_ulong();
for(const auto r : debounced_rects) {
if (switches_debounced & 1)
painter.fill_rectangle(r + pos, Color::green());
switches_debounced >>= 1;
}
const std::array<Rect, 6> events_rects { {
{ 64 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Right
{ 0 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Left
{ 32 + 3, 64 + 3, 16 - 6, 16 - 6 }, // Down
{ 32 + 3, 0 + 3, 16 - 6, 16 - 6 }, // Up
{ 32 + 3, 32 + 3, 16 - 6, 16 - 6 }, // Select
{ 96 + 3, 0 + 3, 16 - 6, 16 - 6 }, // Dfu
} };
auto switches_event = key_event_mask;
for(const auto r : events_rects) {
if (switches_event & 1)
painter.fill_rectangle(r + pos, Color::red());
switches_event >>= 1;
}
}
@@ -322,17 +360,19 @@ void DebugControlsView::focus() {
/* DebugPeripheralsMenuView **********************************************/
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
const char * max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
const char * si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
add_items({
{ "RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"RFFC5072", RegistersWidgetConfig { 31, 16 },
[](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
); } },
{ "MAX2837", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"MAX2837", RegistersWidgetConfig { 32, 10 },
{ max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push<RegistersView>(
max283x, RegistersWidgetConfig { 32, 10 },
[](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
); } },
{ "Si5351C", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"Si5351C", RegistersWidgetConfig { 96, 8 },
{ si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push<RegistersView>(
si5351x, RegistersWidgetConfig { 96, 8 },
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
); } },
{ audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
-1
View File
@@ -29,7 +29,6 @@
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
#include "max2837.hpp"
#include "portapack.hpp"
#include <functional>
+99
View File
@@ -0,0 +1,99 @@
/*
* 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_dfu_menu.hpp"
#include "portapack_shared_memory.hpp"
#include "performance_counter.hpp"
namespace ui {
DfuMenu::DfuMenu(NavigationView& nav) : nav_ (nav) {
add_children({
&text_head,
&labels,
&text_info_line_1,
&text_info_line_2,
&text_info_line_3,
&text_info_line_4,
&text_info_line_5,
&text_info_line_6,
&text_info_line_7,
&text_info_line_8
});
}
void DfuMenu::paint(Painter& painter) {
auto utilisation = get_cpu_utilisation_in_percent();
text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6));
text_info_line_2.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6));
text_info_line_3.set(to_string_dec_uint(utilisation, 6));
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(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, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::black()
);
painter.fill_rectangle(
{
{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::dark_cyan()
);
painter.fill_rectangle(
{
{21 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}
},
ui::Color::dark_cyan()
);
painter.fill_rectangle(
{
{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8},
{17 * CHARACTER_WIDTH + margin * 2, 8}
},
ui::Color::dark_cyan()
);
painter.fill_rectangle(
{
{5 * CHARACTER_WIDTH - margin, (lines+3) * LINE_HEIGHT + margin},
{17 * CHARACTER_WIDTH + margin * 2, 8}
},
ui::Color::dark_cyan()
);
}
} /* namespace ui */
+73
View File
@@ -0,0 +1,73 @@
/*
* 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.
*/
#ifndef __UI_DFU_MENU_H__
#define __UI_DFU_MENU_H__
#include <cstdint>
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "debug.hpp"
#include "string_format.hpp"
#define LINE_HEIGHT 16
#define CHARACTER_WIDTH 8
namespace ui {
class NavigationView;
class DfuMenu : public View {
public:
DfuMenu(NavigationView& nav);
~DfuMenu() = default;
void paint(Painter& painter) override;
private:
NavigationView& nav_;
Text text_head {{ 6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 11 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, "Performance"};
Labels labels {
{ { 6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT }, "M0 heap:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT }, "M0 stack:", Color::dark_cyan() },
{ { 6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT }, "M0 cpu %:", Color::dark_cyan() },
{ { 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 }, "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 }, ""};
Text text_info_line_2 {{ 15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
Text text_info_line_3 {{ 15 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
Text text_info_line_4 {{ 15 * CHARACTER_WIDTH, 8 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT }, ""};
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 */
#endif/*__UI_DFU_MENU_H__*/
+4 -1
View File
@@ -156,6 +156,8 @@ void EncodersConfigView::generate_frame() {
for (auto c : encoder_def->word_format) {
if (c == 'S')
frame_fragments += encoder_def->sync;
else if (!c)
break;
else
frame_fragments += encoder_def->bit_format[symfield_word.get_sym(i++)];
}
@@ -252,7 +254,8 @@ void EncodersView::start_tx(const bool scan) {
scan_width = view_scan.field_length.value();
samples_per_bit =
((view_scan.bit_length_10.value() * 10 + view_scan.bit_length.value()) * OOK_SAMPLERATE) / 1000000UL;
const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) / 2048UL;
constexpr auto sym_len = 4;
const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) * sym_len / 2048UL;
progressbar.set_max(seq_len);
repeat_min = seq_len;
} else {
+306 -148
View File
@@ -20,72 +20,173 @@
* Boston, MA 02110-1301, USA.
*/
/* TODO:
* - Paging menu items
* - Copy/Move
*/
#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) {
auto name = path.string();
return name.length() <= max_length ? name : name.substr(0, 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;
}
}
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(
@@ -105,20 +206,20 @@ FileManBaseView::FileManBaseView(
};
if (!sdcIsCardInserted(&SDCD1)) {
empty_root=true;
empty_root = true;
text_current.set("NO SD CARD!");
return;
}
load_directory_contents(current_path);
if (!entry_list.size()) {
empty_root = true;
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();
};
}
}
@@ -130,67 +231,83 @@ void FileManBaseView::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);
}
});
}
}
menu_view.set_highlighted(0); // Refresh
menu_view.set_highlighted(prev_highlight);
}
void FileManBaseView::reload_current() {
load_directory_contents(current_path);
refresh_list();
}
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const
{
size_t index = 0;
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];
}
/*void FileSaveView::on_save_name() {
@@ -215,8 +332,9 @@ FileSaveView::FileSaveView(
};
}*/
void FileLoadView::refresh_widgets(const bool v) {
(void)v; //avoid unused warning
/* FileLoadView **************************************************************/
void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
@@ -238,45 +356,92 @@ 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();
});
/* 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(current_path / 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(current_path / 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();
});
}
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_delete.hidden(v);
button_new_dir.hidden(v);
set_dirty();
}
FileManagerView::~FileManagerView() {
// Flush ?
}
FileManagerView::FileManagerView(
@@ -293,47 +458,40 @@ FileManagerView::FileManagerView(
&labels,
&text_date,
&button_rename,
&button_delete,
&button_new_dir,
&button_delete
});
menu_view.on_highlight = [this]() {
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
// TODO: enable/disable buttons.
if (selected_is_valid())
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
};
refresh_list();
on_select_entry = [this]() {
if (entry_list[menu_view.highlighted_index()].is_directory) {
load_directory_contents(get_selected_path());
refresh_list();
} else
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_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](Button&) {
if (selected_is_valid())
on_rename();
};
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);
button_delete.on_select = [this](Button&) {
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_new_dir.on_select = [this](Button&) {
on_new_dir();
};
}
}
+46 -35
View File
@@ -31,7 +31,7 @@
namespace ui {
struct fileman_entry {
std::filesystem::path entry_path { };
std::filesystem::path path { };
uint32_t size { };
bool is_directory { };
};
@@ -43,50 +43,56 @@ public:
std::string filter
);
void focus() override;
void load_directory_contents(const std::filesystem::path& dir_path);
std::filesystem::path get_selected_path();
std::string title() const override { return "File manager"; };
void focus() override;
std::string title() const override { return "Fileman"; };
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 = 50;
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::blue() },
{ ".C16", &bitmap_icon_file_iq, ui::Color::blue() },
{ ".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 push_dir(const std::filesystem::path& path);
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_;
bool empty_root { false };
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 },
"",
@@ -142,11 +148,16 @@ public:
~FileManagerView();
private:
// Passed by ref to other views needing lifetime extension.
std::string name_buffer { };
void refresh_widgets(const bool v);
void on_rename(NavigationView& nav);
void on_rename();
void on_delete();
void on_new_dir();
// True if the selected entry is a real file item.
bool selected_is_valid() const;
Labels labels {
{ { 0, 26 * 8 }, "Created ", Color::light_grey() }
@@ -161,16 +172,16 @@ private:
{ 0 * 8, 29 * 8, 14 * 8, 32 },
"Rename"
};
Button button_delete {
{ 16 * 8, 29 * 8, 14 * 8, 32 },
"Delete"
};
Button button_new_dir {
{ 0 * 8, 34 * 8, 14 * 8, 32 },
"New dir"
"New Dir"
};
};
} /* namespace ui */
@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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_flash_utility.hpp"
#include "portapack_shared_memory.hpp"
namespace ui {
Thread* FlashUtilityView::thread { nullptr };
static constexpr size_t max_filename_length = 26;
FlashUtilityView::FlashUtilityView(NavigationView& nav) : nav_ (nav) {
add_children({
&labels,
&menu_view
});
menu_view.set_parent_rect({ 0, 3 * 8, 240, 33 * 8 });
for (const auto& entry : std::filesystem::directory_iterator(u"FIRMWARE", u"*.bin")) {
auto filename = entry.path().filename();
auto path = entry.path().native();
menu_view.add_item({
filename.string().substr(0, max_filename_length),
ui::Color::red(),
&bitmap_icon_temperature,
[this, path](KeyEvent) {
this->firmware_selected(path);
}
});
}
}
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
nav_.push<ModalMessageView>(
"Warning!",
"This will replace your\ncurrent firmware.\n\nIf things go wrong you are\nrequired to flash manually\nwith dfu.",
YESNO,
[this, path](bool choice) {
if (choice) {
std::u16string full_path = std::u16string(u"FIRMWARE/") + path;
this->flash_firmware(full_path);
}
}
);
}
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
ui::Painter painter;
painter.fill_rectangle(
{ 0, 0, portapack::display.width(), portapack::display.height() },
ui::Color::black()
);
painter.draw_string({ 12, 24 }, this->nav_.style(), "This will take 15 seconds.");
painter.draw_string({ 12, 64 }, this->nav_.style(), "Please wait while LEDs RX");
painter.draw_string({ 12, 84 }, this->nav_.style(), "and TX are flashing.");
painter.draw_string({ 12, 124 }, this->nav_.style(), "Then restart the device.");
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
m0_halt();
}
void FlashUtilityView::focus() {
menu_view.focus();
}
} /* namespace ui */
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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.
*/
#ifndef __UI_FLSH_UITILTY_H__
#define __UI_FLSH_UITILTY_H__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "ff.h"
#include "baseband_api.hpp"
#include "core_control.hpp"
#include <cstdint>
namespace ui {
class FlashUtilityView : public View {
public:
FlashUtilityView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Flash Utility"; };
private:
NavigationView& nav_;
bool confirmed = false;
static Thread* thread;
Labels labels {
{ { 4, 4 }, "Select firmware to flash:", Color::white() }
};
MenuView menu_view {
{ 0, 2 * 8, 240, 26 * 8 },
true
};
void firmware_selected(std::filesystem::path::string_type path);
void flash_firmware(std::filesystem::path::string_type path);
};
} /* namespace ui */
#endif/*__UI_FLSH_UITILTY_H__*/
+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();
}
+3 -3
View File
@@ -87,7 +87,7 @@ class FrequencySaveView : public FreqManBaseView {
public:
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
std::string title() const override { return "Save frequency"; };
std::string title() const override { return "Save freq."; };
private:
std::string desc_buffer { };
@@ -126,7 +126,7 @@ public:
FrequencyLoadView(NavigationView& nav);
std::string title() const override { return "Load frequency"; };
std::string title() const override { return "Load freq."; };
private:
void refresh_widgets(const bool v);
@@ -137,7 +137,7 @@ public:
FrequencyManagerView(NavigationView& nav);
~FrequencyManagerView();
std::string title() const override { return "Freq. manager"; };
std::string title() const override { return "Freqman"; };
private:
std::string desc_buffer { };
+308
View File
@@ -0,0 +1,308 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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_level.hpp"
#include "ui_fileman.hpp"
#include "file.hpp"
using namespace portapack;
using portapack::memory::map::backup_ram;
namespace ui {
bool LevelView::check_sd_card()
{
return (sd_card::status() == sd_card::Status::Mounted) ? true : false;
}
void LevelView::focus() {
button_frequency.focus();
}
LevelView::~LevelView() {
// save app settings
app_settings.lna = field_lna.value();
app_settings.vga = field_vga.value();
app_settings.rx_amp = field_rf_amp.value();
settings.save("level", &app_settings);
receiver_model.disable();
baseband::shutdown();
}
LevelView::LevelView( NavigationView& nav) : nav_ { nav } {
add_children( {
&labels,
&field_lna,
&field_vga,
&field_rf_amp,
&field_volume,
&field_bw,
&field_mode,
&step_mode,
&rssi_resolution,
&button_frequency,
&text_ctcss,
&freq_stats_rssi,
&freq_stats_db,
&audio_mode,
&peak_mode,
&rssi,
&rssi_graph
} );
rssi.set_vertical_rssi( true );
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
// Level directory
if( check_sd_card() ) { // Check to see if SD Card is mounted
make_new_directory( u"/LEVEL" );
sd_card_mounted = true ;
}
change_mode(NFM_MODULATION); //Start on AM
field_mode.set_by_value(NFM_MODULATION); //Reflect the mode into the manual selector
//HELPER: Pre-setting a manual range, based on stored frequency
freq = persistent_memory::tuned_frequency();
receiver_model.set_tuning_frequency( freq );
button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" );
// load auto common app settings
if( sd_card_mounted )
{
auto rc = settings.load("level", &app_settings);
if(rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
receiver_model.set_rf_amp(app_settings.rx_amp);
}
}
button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) {
auto new_view = nav_.push<FrequencyKeypadView>(freq);
new_view->on_changed = [this, &button](rf::Frequency f) {
freq = f ;
receiver_model.set_tuning_frequency( f ); // Retune to actual freq
button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" );
};
};
button_frequency.on_change = [this]() {
int64_t def_step = freqman_entry_get_step_value( step_mode.selected_index() );
freq = freq + ( button_frequency.get_encoder_delta() * def_step );
if( freq < 1 )
{
freq = 1 ;
}
if( freq > ( MAX_UFREQ - def_step ) )
{
freq = MAX_UFREQ ;
}
button_frequency.set_encoder_delta( 0 );
receiver_model.set_tuning_frequency( freq ); // Retune to actual freq
button_frequency.set_text( "<" + to_string_short_freq( freq ) + " MHz>" );
};
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
if( v != -1 )
{
receiver_model.disable();
baseband::shutdown();
change_mode(v);
if( audio_mode.selected_index() != 0 )
{
audio::output::start();
}
receiver_model.enable();
}
};
rssi_resolution.on_change = [this](size_t, OptionsField::value_t v) {
if( v != -1 )
{
rssi_graph.set_nb_columns( v );
}
};
audio_mode.on_change = [this](size_t, OptionsField::value_t v) {
if( v == 0 )
{
audio::output::stop();
}
else if( v == 1 )
{
audio::output::start();
this->on_headphone_volume_changed( (receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99 );
}
else
{
}
};
peak_mode.on_change = [this](size_t, OptionsField::value_t v) {
if( v == 0 )
{
rssi.set_peak( false , 0 );
}
else
{
rssi.set_peak( true , v );
}
};
// default peak value
peak_mode.set_selected_index(2);
rssi_resolution.set_selected_index(1);
//FILL STEP OPTIONS
freqman_set_modulation_option( field_mode );
freqman_set_step_option_short( step_mode );
freq_stats_rssi.set_style(&style_white);
freq_stats_db.set_style(&style_white);
}
void LevelView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
static int last_max_db = -1000 ;
static int last_min_rssi = -1000 ;
static int last_avg_rssi = -1000 ;
static int last_max_rssi = -1000 ;
rssi_graph.add_values( rssi.get_min() , rssi.get_avg() , rssi.get_max() , statistics.max_db );
bool refresh_db = false ;
bool refresh_rssi = false ;
if( last_max_db != statistics.max_db )
{
refresh_db = true ;
}
if( last_min_rssi != rssi.get_min() || last_avg_rssi != rssi.get_avg() || last_max_rssi != rssi.get_max() )
{
refresh_rssi = true ;
}
if( refresh_db )
{
last_max_db = statistics.max_db ;
freq_stats_db.set( "Power: "+to_string_dec_int( statistics.max_db )+" db" );
}
if( refresh_rssi )
{
last_min_rssi = rssi.get_min();
last_avg_rssi = rssi.get_avg();
last_max_rssi = rssi.get_max();
freq_stats_rssi.set( "RSSI: "+to_string_dec_int( rssi.get_min() )+"/"+to_string_dec_int( rssi.get_avg() )+"/"+to_string_dec_int( rssi.get_max() )+",dt: "+to_string_dec_int( rssi.get_delta() ) );
}
} /* on_statistic_updates */
size_t LevelView::change_mode( freqman_index_t new_mod ) {
field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; };
switch( new_mod ) {
case AM_MODULATION:
freqman_set_bandwidth_option( new_mod , field_bw );
//bw DSB (0) default
field_bw.set_selected_index(0);
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
receiver_model.set_am_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); };
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
text_ctcss.set(" ");
break;
case NFM_MODULATION:
freqman_set_bandwidth_option( new_mod , field_bw );
//bw 16k (2) default
field_bw.set_selected_index(2);
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.set_nbfm_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
break;
case WFM_MODULATION:
freqman_set_bandwidth_option( new_mod , field_bw );
//bw 200k (0) only/default
field_bw.set_selected_index(0);
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
text_ctcss.set(" ");
break;
default:
break;
}
return step_mode.selected_index();
}
void LevelView::handle_coded_squelch(const uint32_t value) {
static int32_t last_idx = -1 ;
float diff, min_diff = value;
size_t min_idx { 0 };
size_t c;
if( field_mode.selected_index() != NFM_MODULATION )
{
text_ctcss.set(" ");
return ;
}
// Find nearest match
for (c = 0; c < tone_keys.size(); c++) {
diff = abs(((float)value / 100.0) - tone_keys[c].second);
if (diff < min_diff) {
min_idx = c;
min_diff = diff;
}
}
// Arbitrary confidence threshold
if( last_idx < 0 || (unsigned)last_idx != min_idx )
{
last_idx = min_idx ;
if (min_diff < 40)
text_ctcss.set("T: "+tone_keys[min_idx].first);
else
text_ctcss.set(" ");
}
}
} /* namespace ui */
+236
View File
@@ -0,0 +1,236 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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.
*/
#ifndef _UI_LEVEL
#define _UI_LEVEL
#include "ui.hpp"
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "freqman.hpp"
#include "analog_audio_app.hpp"
#include "audio.hpp"
#include "ui_mictx.hpp"
#include "portapack_persistent_memory.hpp"
#include "baseband_api.hpp"
#include "ui_spectrum.hpp"
#include "string_format.hpp"
#include "file.hpp"
#include "app_settings.hpp"
namespace ui {
class LevelView : public View {
public:
LevelView(NavigationView& nav);
~LevelView();
void focus() override;
const Style style_grey { // level
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
const Style style_white { // level
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
const Style style_yellow { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_green { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_blue { // quick level, wait == 0
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::blue(),
};
std::string title() const override { return "Level"; };
private:
NavigationView& nav_;
size_t change_mode( freqman_index_t mod_type);
void on_statistics_update(const ChannelStatistics& statistics);
void set_display_freq( int64_t freq );
bool check_sd_card();
int32_t db { 0 };
long long int MAX_UFREQ = { 7200000000 }; // maximum usable freq
bool sd_card_mounted = false ;
rf::Frequency freq = { 0 } ;
Labels labels
{
{ { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() },
{ { 0 * 8 , 1 * 16 }, "BW: MODE: S: ", Color::light_grey() },
};
LNAGainField field_lna {
{ 4 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 11 * 8, 0 * 16 }
};
RFAmpField field_rf_amp {
{ 18 * 8, 0 * 16 }
};
NumberField field_volume {
{ 24 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
OptionsField field_bw {
{ 3 * 8, 1 * 16 },
6,
{ }
};
OptionsField field_mode {
{ 15 * 8, 1 * 16 },
3,
{
}
};
OptionsField step_mode {
{ 16 * 8, 2 * 16 + 4 },
12,
{
}
};
ButtonWithEncoder button_frequency {
{ 0 * 8 , 2 * 16 + 8 , 15 * 8 , 1 * 8 },
""
};
OptionsField audio_mode {
{ 21 * 8, 1 * 16 },
9,
{
{"audio off", 0},
{"audio on",1}
//{"tone on", 2},
//{"tone off", 2},
}
};
Text text_ctcss {
{ 22 * 8, 3 * 16 + 4 , 14 * 8, 1 * 8 },
""
};
// RSSI: XX/XX/XXX,dt: XX
Text freq_stats_rssi {
{ 0 * 8 , 3 * 16 + 4 , 22 * 8, 14 },
};
// Power: -XXX db
Text freq_stats_db {
{ 0 * 8 , 4 * 16 + 4 , 14 * 8, 14 },
};
OptionsField peak_mode {
{ 40 + 10 * 8, 4 * 16 + 4 },
10,
{
{"peak:none", 0},
{"peak:0.25s",250},
{"peak:0.5s",500},
{"peak:1s",1000},
{"peak:3s",3000},
{"peak:5s",5000},
{"peak:10s",10000},
}
};
OptionsField rssi_resolution {
{ 44 + 20 * 8, 4 * 16 + 4},
4,
{
{"16x", 16},
{"32x", 32},
{"64x", 64},
{"128x", 128},
{"240x", 240},
}
};
RSSIGraph rssi_graph { // 240x320 =>
{ 0 , 5 * 16 + 4 , 240 - 5 * 8 , 320 - ( 5 * 16 + 4 ) },
};
RSSI rssi { // 240x320 =>
{ 240 - 5 * 8 , 5 * 16 + 4 , 5 * 8 , 320 - ( 5 * 16 + 4 ) },
};
void handle_coded_squelch(const uint32_t value);
void on_headphone_volume_changed(int32_t v);
MessageHandlerRegistration message_handler_coded_squelch {
Message::ID::CodedSquelch,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}
};
MessageHandlerRegistration message_handler_stats {
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
};
// app save settings
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
};
} /* namespace ui */
#endif
+639 -282
View File
@@ -22,315 +22,672 @@
#include "ui_looking_glass_app.hpp"
using namespace portapack;
namespace ui
{
void GlassView::focus() {
field_marker.focus();
}
GlassView::~GlassView() {
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
receiver_model.disable();
baseband::shutdown();
}
void GlassView::on_lna_changed(int32_t v_db) {
receiver_model.set_lna(v_db);
}
void GlassView::on_vga_changed(int32_t v_db) {
receiver_model.set_vga(v_db);
}
void GlassView::add_spectrum_pixel(Color color)
{
spectrum_row[pixel_index++] = color;
if (pixel_index == 240) //got an entire waterfall line
{
const auto draw_y = display.scroll(1); //Scroll 1 pixel down
display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top
pixel_index = 0; //Start New cascade line
}
}
//Apparently, the spectrum object returns an array of 256 bins
//Each having the radio signal power for it's corresponding frequency slot
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
{
baseband::spectrum_streaming_stop();
// 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:
for(uint8_t bin = 0; bin < 240; bin++)
void GlassView::focus()
{
if (bin < 120) {
if (spectrum.db[134 + bin] > max_power)
max_power = spectrum.db[134 + bin];
}
else
button_marker.focus();
}
GlassView::~GlassView()
{
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
receiver_model.disable();
baseband::shutdown();
}
// Returns the next multiple of num that is a multiple of multiplier
int64_t GlassView::next_mult_of(int64_t num, int64_t multiplier) {
return ((num / multiplier) + 1) * 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;
if( span % width != 0 )
{
if (spectrum.db[bin - 118] > max_power)
max_power = spectrum.db[bin - 118];
num_intervals++;
}
int64_t new_span = num_intervals * width;
int64_t delta_span = (new_span - span) / 2;
*f_min -= delta_span;
*f_max += delta_span;
}
bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz"
void GlassView::on_lna_changed(int32_t v_db)
{
receiver_model.set_lna(v_db);
}
if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled
void GlassView::on_vga_changed(int32_t v_db)
{
receiver_model.set_vga(v_db);
}
void GlassView::reset_live_view( bool clear_screen )
{
max_freq_hold = 0 ;
max_freq_power = -1000 ;
if( clear_screen )
{
if (min_color_power < max_power)
add_spectrum_pixel(spectrum_rgb3_lut[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
// only clear screen in peak mode
if( live_frequency_view == 2 )
{
bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin"
break;
} else {
bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel
display.fill_rectangle( { { 0 , 108 + 16 } , { 240 , 320 - (108 + 16) } } , { 0 , 0 , 0 } );
}
}
}
if (pixel_index)
f_center += LOOKING_GLASS_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
else
f_center = f_center_ini; //Start a new sweep
void GlassView::add_spectrum_pixel( uint8_t power )
{
static int64_t last_max_freq = 0 ;
receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
baseband::spectrum_streaming_start(); //Do the RX
}
spectrum_row[pixel_index] = spectrum_rgb3_lut[power] ; // row of colors
spectrum_data[pixel_index] = ( live_frequency_integrate * spectrum_data[pixel_index] + power ) / (live_frequency_integrate + 1); // smoothing
pixel_index ++ ;
void GlassView::on_hide()
{
baseband::spectrum_streaming_stop();
display.scroll_disable();
}
if (pixel_index == 240) // got an entire waterfall line
{
if( live_frequency_view > 0 )
{
constexpr int rssi_sample_range = 256;
constexpr float rssi_voltage_min = 0.4;
constexpr float rssi_voltage_max = 2.2;
constexpr float adc_voltage_max = 3.3;
constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
constexpr int raw_delta = raw_max - raw_min;
const range_t<int> y_max_range { 0 , 320 - ( 108 + 16 ) };
void GlassView::on_show()
{
display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates
baseband::spectrum_streaming_start();
}
//drawing and keeping track of max freq
for( uint16_t xpos = 0 ; xpos < 240 ; xpos ++ )
{
// save max powerwull freq
if( spectrum_data[ xpos ] > max_freq_power )
{
max_freq_power = spectrum_data[ xpos ];
max_freq_hold = f_min + ( (f_max - f_min) * xpos) / 240 ;
}
void GlassView::on_range_changed()
{
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
text_range.set(to_string_dec_uint(search_span));
f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm
f_max = (f_max)*MHZ_DIV;
search_span = search_span * MHZ_DIV;
marker_pixel_step = search_span / 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
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
f_center_ini += LOOKING_GLASS_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy)
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
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 fpos)
{
int64_t pos = fpos * MHZ_DIV;
pos -= f_min;
pos = pos / marker_pixel_step; //Real pixel
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw
portapack::display.fill_rectangle({(int16_t)pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({(int16_t)pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({(int16_t)pos, 106, 1, 2}, Color::red()); //Red marker middle
}
GlassView::GlassView(
NavigationView &nav) : nav_(nav)
{
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&labels,
&field_frequency_min,
&field_frequency_max,
&field_lna,
&field_vga,
&text_range,
&filter_config,
&field_rf_amp,
&range_presets,
&field_marker,
&text_marker_pm,
&field_trigger
});
load_Presets(); //Load available presets from TXT files (or default)
field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset
field_frequency_min.on_change = [this](int32_t v) {
if (v >= field_frequency_max.value())
field_frequency_max.set_value(v + 240);
this->on_range_changed();
};
field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset
field_frequency_max.on_change = [this](int32_t v) {
if (v <= field_frequency_min.value())
field_frequency_min.set_value(v - 240);
this->on_range_changed();
};
field_lna.set_value(receiver_model.lna());
field_lna.on_change = [this](int32_t v) {
this->on_lna_changed(v);
};
field_vga.set_value(receiver_model.vga());
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
filter_config.set_selected_index(0);
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
min_color_power = v;
};
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
field_frequency_min.set_value(presets_db[v].min,false);
field_frequency_max.set_value(presets_db[v].max,false);
this->on_range_changed();
};
field_marker.on_change = [this](int32_t v) {
PlotMarker(v); //Refresh marker on screen
};
field_marker.on_select = [this](NumberField&) {
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_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop();
nav_.push<AnalogAudioView>(); //Jump into audio view
};
field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution
field_trigger.on_change = [this](int32_t v) {
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
};
display.scroll_set_area( 109, 319);
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); //trigger:
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
//at which time it pushes the buffer up with channel_spectrum.feed
on_range_changed();
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); //20mhz
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
receiver_model.set_squelch_level(0);
receiver_model.enable();
}
void GlassView::load_Presets() {
File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
presets_db.clear(); //Start with fresh db
if (result.is_valid()) {
presets_Default(); //There is no txt, store a default range
} else {
std::string line; //There is a txt file
char one_char[1]; //Read it char by char
for (size_t pointer=0; pointer < presets_file.size();pointer++) {
presets_file.seek(pointer);
presets_file.read(one_char, 1);
if ((int)one_char[0] > 31) { //ascii space upwards
line += one_char[0]; //Add it to the textline
}
else if (one_char[0] == '\n') { //New Line
txtline_process(line); //make sense of this textline
line.clear(); //Ready for next textline
}
}
if (line.length() > 0) txtline_process(line); //Last line had no newline at end ?
if (!presets_db.size()) presets_Default(); //no antenna on txt, use default
int16_t point = y_max_range.clip( ( ( spectrum_data[ xpos ] - raw_min ) * ( 320 - ( 108 + 16 ) ) ) / raw_delta );
uint8_t color_gradient = (point * 255) / 212 ;
// clear if not in peak view
if( live_frequency_view != 2 )
{
display.fill_rectangle( { { xpos , 108 + 16 } , { 1 , 320 - point } } , { 0 , 0 , 0 } );
}
display.fill_rectangle( { { xpos , 320 - point } , { 1 , point } } , { color_gradient , 0 , uint8_t( 255 - color_gradient ) } );
}
if( last_max_freq != max_freq_hold )
{
last_max_freq = max_freq_hold ;
freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) );
}
PlotMarker( marker );
}
else
{
display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier
}
pixel_index = 0; // Start New cascade line
}
}
populate_Presets();
}
// Apparently, the spectrum object returns an array of 256 bins
// 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.
// 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"
}
void GlassView::txtline_process(std::string& line)
{
if (line.find("#") != std::string::npos) return; //Line is just a comment
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
size_t comma = line.find(","); //Get first comma position
if (comma == std::string::npos) return; //No comma at all
max_power = 0;
size_t previous = 0;
preset_entry new_preset;
new_preset.min = std::stoi(line.substr(0,comma));
if (!new_preset.min) return; //No frequency!
previous = comma + 1;
comma = line.find(",",previous); //Search for next delimiter
if (comma == std::string::npos) return; //No comma at all
new_preset.max = std::stoi(line.substr(previous,comma - previous));
if (!new_preset.max) return; //No frequency!
new_preset.label = line.substr(comma + 1);
if (new_preset.label.size() == 0) return; //No label ?
presets_db.push_back(new_preset); //Add this preset.
}
void GlassView::populate_Presets()
{
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t entries;
for (preset_entry preset : presets_db)
{ //go thru all available presets
entries.emplace_back(preset.label,entries.size());
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
}
range_presets.set_options(entries);
}
else //slow scan
{
offset = 32 ;
uint8_t bin_length = 80 ;
for (uint8_t bin = offset ; bin < bin_length + offset ; bin++)
{
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];
}
void GlassView::presets_Default()
{
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 += 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);
baseband::spectrum_streaming_start(); // Do the RX
}
void GlassView::on_hide()
{
baseband::spectrum_streaming_stop();
display.scroll_disable();
}
void GlassView::on_show()
{
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
baseband::spectrum_streaming_start();
}
void GlassView::on_range_changed()
{
reset_live_view( false );
f_min = field_frequency_min.value();
f_max = field_frequency_max.value();
search_span = f_max - f_min;
if( locked_range )
{
button_range.set_text(">"+to_string_dec_uint(search_span)+"<");
}
else
{
button_range.set_text(" "+to_string_dec_uint(search_span)+" ");
}
f_min = (f_min)*MHZ_DIV; // Transpose into full frequency realm
f_max = (f_max)*MHZ_DIV;
adjust_range( &f_min , &f_max , 240 );
marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz
marker = f_min + (f_max - f_min) / 2 ;
button_marker.set_text( to_string_short_freq( marker ) );
PlotMarker( marker ); // Refresh marker on screen
pixel_index = 0; // reset pixel counter
max_power = 0;
bins_Hz_size = 0; // reset amount of Hz filled up by pixels
if( (f_max - f_min) <= LOOKING_GLASS_SLICE_WIDTH_MAX )
{
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);
}
else if( LOOKING_GLASS_SLICE_WIDTH != LOOKING_GLASS_SLICE_WIDTH_MAX )
{
LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX ;
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 / 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 -= f_min;
pos = pos / marker_pixel_step; // Real pixel
uint8_t shift_y = 0 ;
if( live_frequency_view > 0 ) // plot one line down when in live view
{
shift_y = 16 ;
}
portapack::display.fill_rectangle({0, 100 + shift_y, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw
portapack::display.fill_rectangle({(int)pos - 2, 100 + shift_y, 5, 3}, Color::red()); // Red marker top
portapack::display.fill_rectangle({(int)pos - 1, 103 + shift_y, 3, 3}, Color::red()); // Red marker middle
portapack::display.fill_rectangle({(int)pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom
}
GlassView::GlassView(
NavigationView &nav) : nav_(nav)
{
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&labels,
&field_frequency_min,
&field_frequency_max,
&field_lna,
&field_vga,
&button_range,
&steps_config,
&scan_type,
&view_config,
&level_integration,
&filter_config,
&field_rf_amp,
&range_presets,
&button_marker,
&field_trigger,
&button_jump,
&button_rst,
&freq_stats});
load_Presets(); // Load available presets from TXT files (or default)
field_frequency_min.on_change = [this](int32_t v)
{
reset_live_view( true );
int32_t min_size = steps ;
if( locked_range )
min_size = search_span ;
if( min_size < 2 )
min_size = 2 ;
if( v > 7200 - min_size )
{
v = 7200 - min_size ;
field_frequency_min.set_value( v );
}
if (v > (field_frequency_max.value() - min_size ) )
field_frequency_max.set_value( v + min_size );
if( locked_range )
field_frequency_max.set_value( v + min_size );
this->on_range_changed();
};
field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset
field_frequency_min.set_step( steps );
field_frequency_min.on_select = [this, &nav](NumberField& field) {
auto new_view = nav_.push<FrequencyKeypadView>(field_frequency_min.value()*1000000);
new_view->on_changed = [this, &field](rf::Frequency f) {
int32_t freq = f / 1000000 ;
int32_t min_size = steps ;
if( locked_range )
min_size = search_span ;
if( min_size < 2 )
min_size = 2 ;
if( freq > (7200 - min_size ) )
freq = 7200 - min_size ;
field_frequency_min.set_value( freq );
if( field_frequency_max.value() < ( freq + min_size ) )
field_frequency_max.set_value( freq + min_size );
this->on_range_changed();
};
};
field_frequency_max.on_change = [this](int32_t v)
{
reset_live_view( true );
int32_t min_size = steps ;
if( locked_range )
min_size = search_span ;
if( min_size < 2 )
min_size = 2 ;
if( v < min_size )
{
v = min_size ;
field_frequency_max.set_value( v );
}
if (v < (field_frequency_min.value() + min_size) )
field_frequency_min.set_value(v - min_size);
if( locked_range )
field_frequency_min.set_value( v - min_size );
this->on_range_changed();
};
field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset
field_frequency_max.set_step( steps );
field_frequency_max.on_select = [this, &nav](NumberField& field) {
auto new_view = nav_.push<FrequencyKeypadView>(field_frequency_max.value()*1000000);
new_view->on_changed = [this, &field](rf::Frequency f) {
int32_t min_size = steps ;
if( locked_range )
min_size = search_span ;
if( min_size < 2 )
min_size = 2 ;
int32_t freq = f / 1000000 ;
if( freq < min_size )
freq = min_size ;
field_frequency_max.set_value( freq );
if( field_frequency_min.value() > ( freq - min_size) )
field_frequency_min.set_value( freq - min_size );
this->on_range_changed();
};
};
field_lna.on_change = [this](int32_t v)
{
reset_live_view( true );
this->on_lna_changed(v);
};
field_lna.set_value(receiver_model.lna());
field_vga.on_change = [this](int32_t v_db)
{
reset_live_view( true );
this->on_vga_changed(v_db);
};
field_vga.set_value(receiver_model.vga());
steps_config.on_change = [this](size_t n, OptionsField::value_t v)
{
(void)n;
field_frequency_min.set_step( v );
field_frequency_max.set_step( v );
steps = v ;
};
steps_config.set_selected_index(0); //default of 1 Mhz steps
scan_type.on_change = [this](size_t n, OptionsField::value_t v)
{
(void)n;
fast_scan = v ;
};
scan_type.set_selected_index(0); // default legacy fast scan
view_config.on_change = [this](size_t n, OptionsField::value_t v)
{
(void)n;
// clear between changes
reset_live_view( true );
if( v == 0 )
{
live_frequency_view = 0 ;
level_integration.hidden( true );
freq_stats.hidden( true );
button_jump.hidden( true );
button_rst.hidden( true );
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
}
else if( v == 1 )
{
display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } );
live_frequency_view = 1 ;
display.scroll_disable();
level_integration.hidden( false );
freq_stats.hidden( false );
button_jump.hidden( false );
button_rst.hidden( false );
}
else if( v == 2 )
{
display.fill_rectangle( { { 0 , 108 } , { 240 , 24 } } , { 0 , 0 , 0 } );
live_frequency_view = 2 ;
display.scroll_disable();
level_integration.hidden( false );
freq_stats.hidden( false );
button_jump.hidden( false );
button_rst.hidden( false );
}
set_dirty();
};
view_config.set_selected_index(0); //default spectrum
level_integration.on_change = [this](size_t n, OptionsField::value_t v)
{
(void)n;
reset_live_view( true );
live_frequency_integrate = v ;
};
level_integration.set_selected_index(3); //default integration of ( 3 * old value + new_value ) / 4
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
reset_live_view( true );
min_color_power = v;
};
filter_config.set_selected_index(0);
range_presets.on_change = [this](size_t n, OptionsField::value_t v)
{
(void)n;
field_frequency_min.set_value(presets_db[v].min, false);
field_frequency_max.set_value(presets_db[v].max, false);
this->on_range_changed();
};
button_marker.on_change = [this]()
{
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
};
button_marker.on_select = [this](ButtonWithEncoder &)
{
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
};
field_trigger.on_change = [this](int32_t v)
{
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
};
field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution
button_range.on_select = [this](Button&) {
if( locked_range )
{
locked_range = false ;
button_range.set_style(&style_white);
button_range.set_text(" "+to_string_dec_uint(search_span)+" ");
}
else
{
locked_range = true ;
button_range.set_style(&style_red);
button_range.set_text(">"+to_string_dec_uint(search_span)+"<");
}
};
button_jump.on_select = [this](Button&) {
receiver_model.set_tuning_frequency(max_freq_hold); // 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
};
button_rst.on_select = [this](Button&) {
reset_live_view( true );
};
display.scroll_set_area(109, 319);
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger:
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
// at which time it pushes the buffer up with channel_spectrum.feed
on_range_changed();
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); // 20mhz
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
receiver_model.set_squelch_level(0);
receiver_model.enable();
}
void GlassView::load_Presets()
{
File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
presets_db.clear(); // Start with fresh db
if (result.is_valid())
{
presets_Default(); // There is no txt, store a default range
}
else
{
std::string line; // There is a txt file
char one_char[1]; // Read it char by char
for (size_t pointer = 0; pointer < presets_file.size(); pointer++)
{
presets_file.seek(pointer);
presets_file.read(one_char, 1);
if ((int)one_char[0] > 31)
{ // ascii space upwards
line += one_char[0]; // Add it to the textline
}
else if (one_char[0] == '\n')
{ // New Line
txtline_process(line); // make sense of this textline
line.clear(); // Ready for next textline
}
}
if (line.length() > 0)
txtline_process(line); // Last line had no newline at end ?
if (!presets_db.size())
presets_Default(); // no antenna on txt, use default
}
populate_Presets();
}
void GlassView::txtline_process(std::string &line)
{
if (line.find("#") != std::string::npos)
return; // Line is just a comment
size_t comma = line.find(","); // Get first comma position
if (comma == std::string::npos)
return; // No comma at all
size_t previous = 0;
preset_entry new_preset;
new_preset.min = std::stoi(line.substr(0, comma));
if (!new_preset.min)
return; // No frequency!
previous = comma + 1;
comma = line.find(",", previous); // Search for next delimiter
if (comma == std::string::npos)
return; // No comma at all
new_preset.max = std::stoi(line.substr(previous, comma - previous));
if (!new_preset.max)
return; // No frequency!
new_preset.label = line.substr(comma + 1);
if (new_preset.label.size() == 0)
return; // No label ?
presets_db.push_back(new_preset); // Add this preset.
}
void GlassView::populate_Presets()
{
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t entries;
for (preset_entry preset : presets_db)
{ // go thru all available presets
entries.emplace_back(preset.label, entries.size());
}
range_presets.set_options(entries);
}
void GlassView::presets_Default()
{
presets_db.clear();
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
}
}
}
+259 -168
View File
@@ -1,187 +1,278 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 euquiq
*
* 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.
*/
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 euquiq
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
*
* 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.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "receiver_model.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
#ifndef _UI_GLASS
#define _UI_GLASS
namespace ui
{
#define LOOKING_GLASS_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz
#define MHZ_DIV 1000000
#define X2_MHZ_DIV 2000000
#include "ui.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "receiver_model.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
class GlassView : public View
{
public:
GlassView(NavigationView &nav);
namespace ui
{
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define MHZ_DIV 1000000
#define X2_MHZ_DIV 2000000
GlassView( const GlassView &);
GlassView& operator=(const GlassView &nav);
class GlassView : public View
{
public:
GlassView(NavigationView &nav);
~GlassView();
std::string title() const override { return "Looking Glass"; };
GlassView( const GlassView &);
GlassView& operator=(const GlassView &nav);
void on_show() override;
void on_hide() override;
void focus() override;
~GlassView();
std::string title() const override { return "LookingGlass"; };
private:
NavigationView& nav_;
void on_show() override;
void on_hide() override;
void focus() override;
struct preset_entry
{
rf::Frequency min{};
rf::Frequency max{};
std::string label{};
};
private:
NavigationView& nav_;
std::vector<preset_entry> presets_db{};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void do_timers();
void on_range_changed();
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void add_spectrum_pixel(Color color);
void PlotMarker(rf::Frequency pos);
void load_Presets();
void txtline_process(std::string& line);
void populate_Presets();
void presets_Default();
rf::Frequency f_min { 0 }, f_max { 0 };
rf::Frequency search_span { 0 };
rf::Frequency f_center { 0 };
rf::Frequency f_center_ini { 0 };
rf::Frequency marker_pixel_step { 0 };
rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 240 };
rf::Frequency bins_Hz_size { 0 };
uint8_t min_color_power { 0 };
uint32_t pixel_index { 0 };
std::array<Color, 240> spectrum_row = { 0 };
ChannelSpectrumFIFO* fifo { nullptr };
uint8_t max_power = 0;
Labels labels{
{{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, 4 * 16}, "RESOLUTION: (fft trigger)", Color::light_grey()}
};
NumberField field_frequency_min {
{ 4 * 8, 0 * 16 },
4,
{ 0, 6960 },
240,
' '
};
NumberField field_frequency_max {
{ 13 * 8, 0 * 16 },
4,
{ 240, 7200 },
240,
' '
};
LNAGainField field_lna {
{ 21 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 27 * 8, 0 * 16 }
};
Text text_range{
{7 * 8, 1 * 16, 4 * 8, 16},
""};
OptionsField filter_config{
{19 * 8, 1 * 16},
4,
struct preset_entry
{
{"OFF ", 0},
{"MID ", 118}, //85 +25 (110) + a bit more to kill all blue
{"HIGH", 202}, //168 + 25 (193)
}};
rf::Frequency min{};
rf::Frequency max{};
std::string label{};
};
const Style style_white { // free range
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
RFAmpField field_rf_amp{
{29 * 8, 1 * 16}};
const Style style_red { // locked range
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
OptionsField range_presets{
{7 * 8, 2 * 16},
20,
{
{" NONE (WIFI 2.4GHz)", 0},
}};
std::vector<preset_entry> presets_db{};
// 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 = 20000000;
NumberField field_marker{
{7 * 8, 3 * 16},
4,
{0, 7200},
25,
' '};
// frequency rounding helpers
int64_t next_mult_of(int64_t num, int64_t multiplier);
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
Text text_marker_pm{
{20 * 8, 3 * 16, 2 * 8, 16},
""};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void do_timers();
void on_range_changed();
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void reset_live_view( bool clear_screen );
void add_spectrum_pixel(uint8_t power);
void PlotMarker(rf::Frequency pos);
void load_Presets();
void txtline_process(std::string& line);
void populate_Presets();
void presets_Default();
NumberField field_trigger{
{11 * 8, 4 * 16},
3,
{2, 128},
2,
' '};
rf::Frequency f_min { 0 }, f_max { 0 };
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 / 256 };
rf::Frequency bins_Hz_size { 0 };
uint8_t min_color_power { 0 };
uint32_t pixel_index { 0 };
std::array<Color, 240> spectrum_row = { 0 };
std::array<uint8_t, 240> spectrum_data = { 0 };
ChannelSpectrumFIFO* fifo { nullptr };
uint8_t max_power = 0;
int32_t steps = 0 ;
uint8_t live_frequency_view = 0 ;
int16_t live_frequency_integrate = 3 ;
int64_t max_freq_hold = 0 ;
int16_t max_freq_power = -1000 ;
bool fast_scan = true ; // default to legacy fast scan
bool locked_range = false ;
Labels labels{
{{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", Color::light_grey()},
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}
};
MessageHandlerRegistration message_handler_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
}
};
NumberField field_frequency_min {
{ 4 * 8, 0 * 16 },
4,
{ 0, 7199 },
1, // number of steps by encoder delta
' '
};
};
}
NumberField field_frequency_max {
{ 13 * 8, 0 * 16 },
4,
{ 1, 7200 },
1, // number of steps by encoder delta
' '
};
LNAGainField field_lna {
{ 21 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 27 * 8, 0 * 16 }
};
Button button_range{
{7 * 8, 1 * 16, 4 * 8, 16},
""};
OptionsField filter_config{
{20 * 8, 1 * 16},
4,
{
{"OFF ", 0},
{"MID ", 118}, //85 +25 (110) + a bit more to kill all blue
{"HIGH", 202}, //168 + 25 (193)
}};
RFAmpField field_rf_amp{
{29 * 8, 1 * 16}};
OptionsField range_presets{
{7 * 8, 2 * 16},
20,
{
{" NONE (WIFI 2.4GHz)", 0},
}};
ButtonWithEncoder button_marker{
{7 * 8, 3 * 16 , 10 * 8 , 16},
" "
};
NumberField field_trigger{
{4 * 8, 4 * 16},
3,
{2, 128},
2,
' '};
OptionsField steps_config{
{ 13 * 8, 4 * 16},
3,
{
{"1", 1},
{"25", 25},
{"50", 50},
{"100", 100},
{"250", 250},
{"500", 500},
}
};
OptionsField scan_type{
{ 17 * 8, 4 * 16},
2,
{
{"F-", true },
{"S-", false },
}
};
OptionsField view_config{
{ 19 * 8, 4 * 16},
7,
{
{"SPCTR-V", 0 },
{"LEVEL-V", 1 },
{"PEAK-V" , 2 },
}
};
OptionsField level_integration{
{ 27 * 8, 4 * 16},
2,
{
{"x0", 0 },
{"x1", 1 },
{"x2", 2 },
{"x3", 3 },
{"x4", 4 },
{"x5", 5 },
{"x6", 6 },
{"x7", 7 },
{"x8", 8 },
{"x9", 9 },
}};
Button button_jump {
{ 240 - 4 * 8 , 5 * 16 , 4 * 8, 16 },
"JMP"
};
Button button_rst {
{ 240 - 9 * 8 , 5 * 16 , 4 * 8, 16 },
"RST"
};
Text freq_stats{
{0 * 8, 5 * 16 , 240 - 10 * 8 , 8 },
""
};
MessageHandlerRegistration message_handler_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
}
};
};
}
#endif
+25 -5
View File
@@ -180,7 +180,10 @@ void MicTXView::rxaudio(bool is_on) {
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
// receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out.
receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate controls!
if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return
receiver_model.set_tuning_frequency(tx_frequency); // Update freq also for RX = TX
else
receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate freq controls!
receiver_model.set_lna(rx_lna);
receiver_model.set_vga(rx_vga);
receiver_model.set_rf_amp(rx_amp);
@@ -237,6 +240,7 @@ MicTXView::MicTXView(
&field_frequency,
&options_tone_key,
&check_rogerbeep,
&check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX
&check_rxactive,
&field_volume,
&field_rxbw,
@@ -266,6 +270,7 @@ MicTXView::MicTXView(
&field_frequency,
&options_tone_key,
&check_rogerbeep,
&check_common_freq_tx_rx, // added to handle common or separate freq- TX/RX
&check_rxactive,
&field_volume,
&field_rxbw,
@@ -332,8 +337,12 @@ MicTXView::MicTXView(
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
tx_frequency = f;
if(!rx_enabled)
if(!rx_enabled) { // not activated receiver. just update freq TX
transmitter_model.set_tuning_frequency(f);
} else { // activated receiver.
if (bool_same_F_tx_rx_enabled) // user selected common freq- TX = RX
receiver_model.set_tuning_frequency(f); //Update common freq also for RX
}
};
field_frequency.on_edit = [this, &nav]() {
focused_ui = 0;
@@ -341,8 +350,12 @@ MicTXView::MicTXView(
auto new_view = nav.push<FrequencyKeypadView>(tx_frequency);
new_view->on_changed = [this](rf::Frequency f) {
tx_frequency = f;
if(!rx_enabled)
if(!rx_enabled) {
transmitter_model.set_tuning_frequency(f);
} else {
if (bool_same_F_tx_rx_enabled)
receiver_model.set_tuning_frequency(f); //Update freq also for RX
}
this->field_frequency.set_value(f);
set_dirty();
};
@@ -511,6 +524,13 @@ MicTXView::MicTXView(
rogerbeep_enabled = v;
};
check_common_freq_tx_rx.on_select = [this](Checkbox&, bool v) {
bool_same_F_tx_rx_enabled = v;
field_rxfrequency.hidden(v); //Hide or show separated freq RX field . (When no hide user can enter down indep. freq for RX)
set_dirty(); //Refresh GUI interface
receiver_model.set_tuning_frequency(v ? tx_frequency : rx_frequency); // To go to the proper tuned freq. when toggling it
};
field_va_level.on_change = [this](int32_t v) {
va_level = v;
vumeter.set_mark(v);
@@ -563,12 +583,12 @@ MicTXView::MicTXView(
rx_frequency = receiver_model.tuning_frequency();
field_rxfrequency.set_value(rx_frequency);
field_rxfrequency.set_step(receiver_model.frequency_step());
field_rxfrequency.on_change = [this](rf::Frequency f) {
field_rxfrequency.on_change = [this](rf::Frequency f) { // available when field rxfrequency not hidden => user selected separated freq RX/TX-
rx_frequency = f;
if(rx_enabled)
receiver_model.set_tuning_frequency(f);
};
field_rxfrequency.on_edit = [this, &nav]() {
field_rxfrequency.on_edit = [this, &nav]() { // available when field rxfrequency not hidden => user selected separated freq RX/TX-
focused_ui = 1;
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(rx_frequency);
+18 -10
View File
@@ -80,6 +80,7 @@ private:
bool va_enabled { false };
bool ptt_enabled { true };
bool rogerbeep_enabled { false };
bool bool_same_F_tx_rx_enabled { false };
bool rx_enabled { false };
uint32_t tone_key_index { };
float mic_gain { 1.0 };
@@ -123,9 +124,9 @@ private:
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
{ { 7 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
{ {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW.
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
{ {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW.
{ {20 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F_RX:", Color::light_grey() },
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
{ {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()},
{ {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()}
@@ -145,8 +146,8 @@ private:
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
{ { (6 * 8)+4, 23 * 8 }, "VOL:", Color::light_grey() },
{ {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW.
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
{ {20 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F_RX:", Color::light_grey() },
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
{ {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()},
{ {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()}
@@ -287,7 +288,7 @@ OptionsField options_wm8731_boost_mode {
};
Checkbox check_rogerbeep {
{ 3 * 8, ( 16 * 8 ) + 4 },
{ 3 * 8, ( 16 * 8 ) + 7 },
10,
"Roger beep",
false
@@ -295,11 +296,18 @@ OptionsField options_wm8731_boost_mode {
Checkbox check_rxactive {
{ 3 * 8, ( 21 * 8 ) - 4 },
8,
18, // it was 8, but if it is string size should be 18
"RX audio listening",
false
};
Checkbox check_common_freq_tx_rx {
{ 18 * 8, ( 16* 8 ) + 7 },
8,
"F = F_RX",
false
};
NumberField field_volume {
{ 11* 8, 23 * 8 },
2,
@@ -319,7 +327,7 @@ OptionsField options_wm8731_boost_mode {
};
NumberField field_squelch {
{ 20 * 8, 25 * 8 },
{ 23 * 8, 25 * 8 },
2,
{ 0, 99 },
1,
@@ -327,7 +335,7 @@ OptionsField options_wm8731_boost_mode {
};
FrequencyField field_rxfrequency {
{ 7 * 8, 25 * 8 },
{ 10 * 8, 25 * 8 },
};
NumberField field_rxlna {
@@ -347,7 +355,7 @@ OptionsField options_wm8731_boost_mode {
};
NumberField field_rxamp {
{ 23 * 8, 27 * 8 },
{ 24 * 8, 27 * 8 },
1,
{ 0, 1 },
1,
+2 -2
View File
@@ -57,13 +57,13 @@ static msg_t ookthread_fn(void * arg) {
v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause
delay = morse_symbols[symbol];
gpio_tx.write(v);
gpio_og_tx.write(v);
arg_c->on_tx_progress(i, false);
chThdSleepMilliseconds(delay * arg_c->time_unit_ms);
}
gpio_tx.write(0); // Ensure TX is off
gpio_og_tx.write(0); // Ensure TX is off
arg_c->on_tx_progress(0, true);
chThdExit(0);
File diff suppressed because it is too large Load Diff
+325 -300
View File
@@ -31,372 +31,397 @@
#include "analog_audio_app.hpp"
#include "audio.hpp"
#include "ui_mictx.hpp"
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "portapack_persistent_memory.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "file.hpp"
#include "app_settings.hpp"
// maximum usable freq
#define MAX_UFREQ 7200000000
namespace ui {
class ReconThread {
public:
ReconThread(freqman_db *database );
~ReconThread();
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_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_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_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_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 change_recon_direction();
bool get_recon_direction();
void set_recon_direction( const bool v);
void set_continuous(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 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();
void run();
void stop();
ReconThread(const ReconThread&) = delete;
ReconThread(ReconThread&&) = delete;
ReconThread& operator=(const ReconThread&) = delete;
ReconThread& operator=(ReconThread&&) = delete;
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 ;
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 };
int64_t _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);
};
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);
~ReconView();
class ReconView : public View {
public:
ReconView(NavigationView& nav);
~ReconView();
void focus() override;
void focus() override;
void big_display_freq( int64_t f );
const Style style_grey { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
const Style style_grey { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
const Style style_white { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
const Style style_white { // recon
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
const Style style_yellow { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_yellow { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_green { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_green { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_blue { // quick recon, wait == 0
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::blue(),
};
const Style style_blue { // quick recon, wait == 0
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::blue(),
};
std::string title() const override { return "Recon"; };
std::string title() const override { return "Recon"; };
//void set_parent_rect(const Rect new_parent_rect) override;
//void set_parent_rect(const Rect new_parent_rect) override;
private:
NavigationView& nav_;
private:
NavigationView& nav_;
void start_recon_thread();
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 handle_coded_squelch(const uint32_t value);
void start_recon_thread();
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();
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 };
freqman_db frequency_list = { };
uint32_t current_index { 0 };
bool userpause { false };
bool continuous_lock { false };
std::string input_file = { "RECON" };
std::string output_file = { "RECON_RESULTS" };
bool autosave = { true };
bool autostart = { true };
bool continuous = { true };
bool filedelete = { true };
bool load_freqs = { true };
bool load_ranges = { true };
bool load_hamradios = { true };
bool update_ranges = { true };
bool fwd = { true };
uint32_t recon_lock_nb_match = { 10 };
uint32_t recon_lock_duration = { 50 };
uint32_t recon_match_mode = { 0 };
bool scanner_mode { false };
bool manual_mode { false };
bool sd_card_mounted = false ;
int32_t volume = 40 ;
jammer::jammer_range_t frequency_range { false, 0, 0 }; //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 };
freqman_db frequency_list = { };
uint32_t current_index { 0 };
bool userpause { false };
bool continuous_lock { false };
std::string input_file = { "RECON" };
std::string output_file = { "RECON_RESULTS" };
bool autosave = { true };
bool autostart = { true };
bool continuous = { true };
bool filedelete = { true };
bool load_freqs = { true };
bool load_ranges = { true };
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 scanner_mode { false };
bool manual_mode { false };
bool sd_card_mounted = false ;
int32_t volume = 40 ;
Labels labels
{
{ { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() },
{ { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() },
{ { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() },
{ { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() },
{ { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() },
};
Labels labels
{
{ { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() },
{ { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() },
{ { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() },
{ { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() },
{ { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() },
};
LNAGainField field_lna {
{ 4 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 4 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 11 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 11 * 8, 0 * 16 }
};
RFAmpField field_rf_amp {
{ 18 * 8, 0 * 16 }
};
RFAmpField field_rf_amp {
{ 18 * 8, 0 * 16 }
};
NumberField field_volume {
{ 24 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
NumberField field_volume {
{ 24 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
OptionsField field_bw {
{ 3 * 8, 1 * 16 },
6,
{ }
};
OptionsField field_bw {
{ 3 * 8, 1 * 16 },
6,
{ }
};
NumberField field_squelch {
{ 12 * 8, 1 * 16 },
3,
{ -90, 20 },
1,
' ',
};
NumberField field_squelch {
{ 12 * 8, 1 * 16 },
3,
{ -90, 20 },
1,
' ',
};
NumberField field_wait {
{ 20 * 8, 1 * 16 },
5,
{ -9000, 9000 },
100,
' ',
};
NumberField field_wait {
{ 20 * 8, 1 * 16 },
5,
{ -9000, 9000 },
100,
' ',
};
NumberField field_lock_wait {
{ 26 * 8, 1 * 16 },
4,
{ 100 , 9000 },
100,
' ',
};
NumberField field_lock_wait {
{ 26 * 8, 1 * 16 },
4,
{ 100 , 9000 },
100,
' ',
};
RSSI rssi {
{ 0 * 16, 2 * 16, 240 - 8 * 8 + 4 , 16 },
};
RSSI rssi {
{ 0 * 16, 2 * 16, 15 * 16, 8 },
};
ButtonWithEncoder text_cycle {
{ 0, 3 * 16, 4 * 8, 16 },
""
};
Text text_cycle {
{ 0, 3 * 16, 3 * 8, 16 },
};
Text text_max {
{ 4 * 8, 3 * 16, 240 - 7 * 8 - 4 * 8 , 16 },
};
Text text_max {
{ 3 * 8, 3 * 16, 20 * 8 , 16 },
};
Text desc_cycle {
{0, 4 * 16, 240 , 16 },
};
Text desc_cycle {
{0, 4 * 16, 240, 16 },
};
/* BigFrequency big_display { //Show frequency in glamour
{ 4, 7 * 16 - 8 , 28 * 8, 52 },
0
}; */
/* BigFrequency big_display { //Show frequency in glamour
{ 4, 7 * 16 - 8 , 28 * 8, 52 },
0
}; */
Text big_display { //Show frequency in text mode
{ 0, 5 * 16 , 23 * 8, 16 },
};
Text big_display { //Show frequency in text mode
{ 0, 5 * 16 , 28 * 8, 16 },
};
Text freq_stats { //Show frequency stats in text mode
{ 0, 6 * 16 , 23 * 8, 16 },
};
Text freq_stats { //Show frequency stats in text mode
{ 0, 6 * 16 , 28 * 8, 16 },
};
// TIMER: 9999
Text text_timer { //Show frequency stats in text mode
{ 0, 7 * 16 , 11 * 8, 16 },
};
Text text_timer { //Show frequency stats in text mode
{ 0, 7 * 16 , 28 * 8, 16 },
};
// T: Senn. 32.000k
Text text_ctcss {
{ 12 * 8 + 4, 7 * 16 , 14 * 8, 1 * 8 },
""
};
Button button_recon_setup {
{ 25 * 8 , 2 * 16 + 8 , 4 * 8, 28 },
"OPT"
};
Button button_recon_setup {
{ 240 - 7 * 8 , 2 * 16 , 7 * 8, 28 },
"CONFIG"
};
Button button_scanner_mode {
{ 21 * 8 , 8 * 16 , 9 * 8, 28 },
"RECON"
};
Button button_looking_glass {
{ 240 - 7 * 8 , 5 * 16 , 7 * 8, 28 },
"GLASS"
};
Text file_name { //Show file used
{ 0 , 8 * 16 + 4 , 20 * 8, 16 },
};
// Button can be RECON or SCANNER
Button button_scanner_mode {
{ 240 - 7 * 8 , 8 * 16 , 7 * 8, 28 },
"RECON"
};
Text file_name { //Show file used
{ 0 , 8 * 16 + 6 , 240 - 7 * 8, 16 },
};
ButtonWithEncoder button_manual_start {
{ 0 * 8, 11 * 16, 11 * 8, 28 },
""
};
ButtonWithEncoder button_manual_end {
{ 12 * 8 - 6, 11 * 16, 11 * 8, 28 },
""
};
Button button_manual_recon {
{ 23 * 8 - 3, 11 * 16, 7 * 8 , 28 },
"SEARCH"
};
OptionsField field_mode {
{ 5 * 8, (26 * 8) + 4 },
6,
{
}
};
OptionsField step_mode {
{ 17 * 8, (26 * 8) + 4 },
12,
{
}
};
ButtonWithEncoder button_pause {
{ 0, (15 * 16) - 4, 72, 28 },
"PAUSE"
};
ButtonWithEncoder button_manual_start {
{ 0 * 8, 11 * 16, 11 * 8, 28 },
""
};
Button button_audio_app {
{ 84, (15 * 16) - 4, 72, 28 },
"AUDIO"
};
ButtonWithEncoder button_manual_end {
{ 12 * 8 - 6, 11 * 16, 11 * 8, 28 },
""
};
ButtonWithEncoder button_add {
{ 168, (15 * 16) - 4, 72, 28 },
"<STORE>"
};
Button button_manual_recon {
{ 23 * 8 - 3, 11 * 16, 7 * 8 , 28 },
"SEARCH"
};
Button button_dir {
{ 0, (35 * 8) - 4, 34, 28 },
"FW>"
};
OptionsField field_mode {
{ 5 * 8, (26 * 8) + 4 },
6,
{
}
};
OptionsField step_mode {
{ 17 * 8, (26 * 8) + 4 },
12,
{
}
};
ButtonWithEncoder button_pause {
{ 0, (15 * 16) - 4, 72, 28 },
"PAUSE"
};
Button button_restart {
{ 38, (35 * 8) - 4, 34, 28 },
"RST"
};
Button button_audio_app {
{ 84, (15 * 16) - 4, 72, 28 },
"AUDIO"
};
Button button_mic_app {
{ 84, (35 * 8) - 4, 72, 28 },
"MIC TX"
};
ButtonWithEncoder button_add {
{ 168, (15 * 16) - 4, 72, 28 },
"<STORE>"
};
ButtonWithEncoder button_remove {
{ 168, (35 * 8) - 4, 72, 28 },
"<REMOVE>"
};
Button button_dir {
{ 0, (35 * 8) - 4, 34, 28 },
"FW>"
};
std::unique_ptr<ReconThread> recon_thread { };
Button button_restart {
{ 38, (35 * 8) - 4, 34, 28 },
"RST"
};
MessageHandlerRegistration message_handler_coded_squelch {
Message::ID::CodedSquelch,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}
};
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);
}
};
Button button_mic_app {
{ 84, (35 * 8) - 4, 72, 28 },
"MIC TX"
};
ButtonWithEncoder button_remove {
{ 168, (35 * 8) - 4, 72, 28 },
"<REMOVE>"
};
std::unique_ptr<ReconThread> recon_thread { };
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) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
};
// app save settings
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
};
MessageHandlerRegistration message_handler_stats {
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
};
// app save settings
std::app_settings settings { };
std::app_settings::AppSettings app_settings { };
};
} /* namespace ui */
+232 -232
View File
@@ -34,268 +34,268 @@ 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];
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 ];
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 ;
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
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);
}
}
// 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 > 0 )
input_file = params[ 0 ];
else
input_file = "RECON" ;
if( it > 1 )
output_file= params[ 1 ];
else
output_file = "RECON_RESULTS" ;
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 > 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 > 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 > 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 > 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 > 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 > 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 > 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 ;
}
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;
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 ;
}
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);
add_children({
&button_load_freqs,
&text_input_file,
&button_save_freqs,
&button_output_file,
&checkbox_autosave_freqs,
&checkbox_autostart_recon,
&checkbox_continuous,
&checkbox_clear_output
});
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);
add_children({
&button_load_freqs,
&text_input_file,
&button_save_freqs,
&button_output_file,
&checkbox_autosave_freqs,
&checkbox_autostart_recon,
&checkbox_continuous,
&checkbox_clear_output
});
checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() );
checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() );
checkbox_continuous.set_value( persistent_memory::recon_continuous() );
checkbox_clear_output.set_value( persistent_memory::recon_clear_output() );
checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() );
checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() );
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");
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
std::string dir_filter = "FREQMAN/";
std::string str_file_path = new_file_path.string();
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
// get the filename without txt extension so we can use load_freqman_file fcn
_input_file = new_file_path.stem().string();
text_input_file.set( _input_file );
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
};
};
button_load_freqs.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
std::string dir_filter = "FREQMAN/";
std::string str_file_path = new_file_path.string();
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
// get the filename without txt extension so we can use load_freqman_file fcn
_input_file = new_file_path.stem().string();
text_input_file.set( _input_file );
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
};
};
button_save_freqs.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
std::string dir_filter = "FREQMAN/";
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 );
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
};
};
button_save_freqs.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
std::string dir_filter = "FREQMAN/";
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 );
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
};
};
button_output_file.on_select =[this, &nav](Button&) {
text_prompt( nav, _output_file , 28 ,
[this](std::string& buffer) {
_output_file = buffer ;
button_output_file.set_text( _output_file );
} );
};
};
button_output_file.on_select =[this, &nav](Button&) {
text_prompt( nav, _output_file , 28 ,
[this](std::string& buffer) {
_output_file = buffer ;
button_output_file.set_text( _output_file );
} );
};
};
void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) {
persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value());
persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value());
persistent_memory::set_recon_continuous(checkbox_continuous.value());
persistent_memory::set_recon_clear_output(checkbox_clear_output.value());
input_file=_input_file ;
output_file=_output_file ;
};
void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) {
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
recon_lock_duration = field_recon_lock_duration.value();
recon_lock_nb_match = field_recon_lock_nb_match.value();
recon_match_mode = field_recon_match_mode . selected_index_value() ;
};
void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) {
persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value());
persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value());
persistent_memory::set_recon_continuous(checkbox_continuous.value());
persistent_memory::set_recon_clear_output(checkbox_clear_output.value());
input_file=_input_file ;
output_file=_output_file ;
};
void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) {
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
recon_lock_duration = field_recon_lock_duration.value();
recon_lock_nb_match = field_recon_lock_nb_match.value();
recon_match_mode = field_recon_match_mode . selected_index_value() ;
};
void ReconSetupViewMain::focus() {
button_load_freqs.focus();
}
void ReconSetupViewMain::focus() {
button_load_freqs.focus();
}
ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode }
{
(void)nav;
hidden(true);
ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode }
{
(void)nav;
hidden(true);
add_children({
&checkbox_load_freqs,
&checkbox_load_ranges,
&checkbox_load_hamradios,
&checkbox_update_ranges_when_recon,
&text_recon_lock_duration,
&field_recon_lock_duration,
&text_recon_lock_nb,
&field_recon_lock_nb_match,
&field_recon_match_mode,
});
add_children({
&checkbox_load_freqs,
&checkbox_load_ranges,
&checkbox_load_hamradios,
&checkbox_update_ranges_when_recon,
&text_recon_lock_duration,
&field_recon_lock_duration,
&text_recon_lock_nb,
&field_recon_lock_nb_match,
&field_recon_match_mode,
});
checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() );
checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() );
checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() );
checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() );
field_recon_lock_duration.set_value( _recon_lock_duration );
field_recon_lock_nb_match.set_value( _recon_lock_nb_match );
field_recon_match_mode.set_by_value( _recon_match_mode );
};
checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() );
checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() );
checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() );
checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() );
field_recon_lock_duration.set_value( _recon_lock_duration );
field_recon_lock_nb_match.set_value( _recon_lock_nb_match );
field_recon_match_mode.set_by_value( _recon_match_mode );
};
void ReconSetupViewMore::focus() {
checkbox_load_freqs.focus();
}
void ReconSetupViewMore::focus() {
checkbox_load_freqs.focus();
}
void ReconSetupView::focus() {
viewMain.focus();
}
void ReconSetupView::focus() {
viewMain.focus();
}
ReconSetupView::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 ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode }
{
add_children({
&tab_view,
&viewMain,
&viewMore,
&button_save
});
ReconSetupView::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 ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode }
{
add_children({
&tab_view,
&viewMain,
&viewMore,
&button_save
});
button_save.on_select = [this,&nav](Button&) {
viewMain.Save( input_file , output_file );
viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode );
std::vector<std::string> messages ;
messages.push_back( input_file );
messages.push_back( output_file );
messages.push_back( to_string_dec_uint( recon_lock_duration ) );
messages.push_back( to_string_dec_uint( recon_lock_nb_match ) );
messages.push_back( to_string_dec_uint( recon_match_mode ) );
on_changed( messages );
nav.pop();
};
}
button_save.on_select = [this,&nav](Button&) {
viewMain.Save( input_file , output_file );
viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode );
std::vector<std::string> messages ;
messages.push_back( input_file );
messages.push_back( output_file );
messages.push_back( to_string_dec_uint( recon_lock_duration ) );
messages.push_back( to_string_dec_uint( recon_lock_nb_match ) );
messages.push_back( to_string_dec_uint( recon_match_mode ) );
on_changed( messages );
nav.pop();
};
}
} /* namespace ui */
+134 -134
View File
@@ -29,166 +29,166 @@
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 );
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 );
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 );
class ReconSetupViewMain : public View {
public:
ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file );
void Save( std::string &input_file , std::string &output_file );
void focus() override;
class ReconSetupViewMain : public View {
public:
ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file );
void Save( std::string &input_file , std::string &output_file );
void focus() override;
private:
std::string _input_file = { "RECON" };
std::string _output_file = { "RECON_RESULTS" };
private:
std::string _input_file = { "RECON" };
std::string _output_file = { "RECON_RESULTS" };
Button button_load_freqs {
{ 1 * 8 , 12 , 18 * 8 , 22 },
"select input file"
};
Text text_input_file {
{ 1 * 8 , 4 + 2 * 16, 18 * 8, 22 },
"RECON"
};
Button button_load_freqs {
{ 1 * 8 , 12 , 18 * 8 , 22 },
"select input file"
};
Text text_input_file {
{ 1 * 8 , 4 + 2 * 16, 18 * 8, 22 },
"RECON"
};
Button button_save_freqs {
{ 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 },
"select output file"
};
Button button_output_file {
{ 1 * 8 , 5 * 16 - 2, 18 * 8, 22 },
"RECON_RESULTS"
};
Button button_save_freqs {
{ 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 },
"select output file"
};
Button button_output_file {
{ 1 * 8 , 5 * 16 - 2, 18 * 8, 22 },
"RECON_RESULTS"
};
Checkbox checkbox_autosave_freqs {
{ 1 * 8, 7 * 16 - 4 },
3,
"autosave freqs"
};
Checkbox checkbox_autosave_freqs {
{ 1 * 8, 7 * 16 - 4 },
3,
"autosave freqs"
};
Checkbox checkbox_autostart_recon {
{ 1 * 8, 9 * 16 - 4 },
3,
"autostart recon"
};
Checkbox checkbox_autostart_recon {
{ 1 * 8, 9 * 16 - 4 },
3,
"autostart recon"
};
Checkbox checkbox_continuous {
{ 1 * 8, 11 * 16 - 4 },
3,
"continuous"
};
Checkbox checkbox_clear_output {
{ 1 * 8, 13 * 16 - 4 },
3,
"clear output at start"
};
};
Checkbox checkbox_continuous {
{ 1 * 8, 11 * 16 - 4 },
3,
"continuous"
};
Checkbox checkbox_clear_output {
{ 1 * 8, 13 * 16 - 4 },
3,
"clear output at start"
};
};
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 );
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 );
void Save( 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 );
void focus() override;
void focus() override;
private:
private:
const uint32_t _recon_lock_duration = 50 ;
const uint32_t _recon_lock_nb_match = 10 ;
const uint32_t _recon_match_mode = 0 ;
const uint32_t _recon_lock_duration = 50 ;
const uint32_t _recon_lock_nb_match = 10 ;
const uint32_t _recon_match_mode = 0 ;
Checkbox checkbox_load_freqs {
{ 1 * 8, 12 },
3,
"input: load freqs"
};
Checkbox checkbox_load_freqs {
{ 1 * 8, 12 },
3,
"input: load freqs"
};
Checkbox checkbox_load_ranges {
{ 1 * 8, 42 },
3,
"input: load ranges"
};
Checkbox checkbox_load_ranges {
{ 1 * 8, 42 },
3,
"input: load ranges"
};
Checkbox checkbox_load_hamradios {
{ 1 * 8, 72 },
3,
"input: load hamradios"
};
Checkbox checkbox_load_hamradios {
{ 1 * 8, 72 },
3,
"input: load hamradios"
};
Checkbox checkbox_update_ranges_when_recon {
{ 1 * 8, 102 },
3,
"auto update m-ranges"
};
Text text_recon_lock_duration {
{ 1 * 8 , 132 , 22 * 8 , 22 },
" ms (lock duration)"
};
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
};
Text text_recon_lock_nb {
{ 1 * 8 , 162 , 25 * 8 , 22 },
" x (nb lock to match freq)"
};
NumberField field_recon_lock_nb_match {
{ 1 * 8, 162 },
4,
{ 1, 99 },
1,
' ',
false
};
OptionsField field_recon_match_mode {
{ 1 * 8, 192 },
20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE
{
{ "SQL MATCH: CONTINOUS" , 0 },
{ "SQL MATCH: SPARSE" , 1 }
}
};
};
Checkbox checkbox_update_ranges_when_recon {
{ 1 * 8, 102 },
3,
"auto update m-ranges"
};
Text text_recon_lock_duration {
{ 1 * 8 , 132 , 22 * 8 , 22 },
" ms (lock duration)"
};
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
};
Text text_recon_lock_nb {
{ 1 * 8 , 162 , 25 * 8 , 22 },
" x (nb lock to match freq)"
};
NumberField field_recon_lock_nb_match {
{ 1 * 8, 162 },
4,
{ 1, 99 },
1,
' ',
false
};
OptionsField field_recon_match_mode {
{ 1 * 8, 192 },
20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE
{
{ "SQL MATCH: CONTINOUS" , 0 },
{ "SQL MATCH: SPARSE" , 1 }
}
};
};
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 );
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 );
std::function<void( std::vector<std::string> messages )> on_changed { };
std::function<void( std::vector<std::string> messages )> on_changed { };
void focus() override;
void focus() override;
std::string title() const override { return "Recon setup"; };
std::string title() const override { return "Recon setup"; };
private:
private:
NavigationView& nav_ ;
NavigationView& nav_ ;
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 ;
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 ;
Rect view_rect = { 0, 3 * 8, 240, 230 };
Rect view_rect = { 0, 3 * 8, 240, 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 };
ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file };
ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode };
TabView tab_view {
{ "Main", Color::cyan() , &viewMain },
{ "More", Color::green(), &viewMore }
};
Button button_save {
{ 9 * 8, 255, 14 * 8 , 40 },
"SAVE"
};
};
TabView tab_view {
{ "Main", Color::cyan() , &viewMain },
{ "More", Color::green(), &viewMore }
};
Button button_save {
{ 9 * 8, 255, 14 * 8 , 40 },
"SAVE"
};
};
} /* namespace ui */
+118 -44
View File
@@ -31,13 +31,23 @@ ScannerThread::ScannerThread(
std::vector<rf::Frequency> frequency_list
) : frequency_list_ { std::move(frequency_list) }
{
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
create_thread();
}
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step) : frequency_range_(frequency_range), def_step_(def_step)
{
create_thread();
}
ScannerThread::~ScannerThread() {
stop();
}
void ScannerThread::create_thread()
{
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
}
void ScannerThread::stop() {
if( thread ) {
chThdTerminate(thread);
@@ -102,12 +112,13 @@ void ScannerThread::run() {
else
restart_scan=false; //Effectively skipping first retuning, giving system time
}
message.freq = frequency_list_[frequency_index];
message.range = frequency_index; //Inform freq (for coloring purposes also!)
EventDispatcher::send_message(message);
}
else { //NOT scanning
if (_freq_del != 0) { //There is a frequency to delete
for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
for (uint32_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
if (frequency_list_[i] == _freq_del)
{ //found: Erase it
frequency_list_.erase(frequency_list_.begin() + i);
@@ -126,16 +137,58 @@ void ScannerThread::run() {
}
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
}
}else if(def_step_ > 0) // manual mode
{
RetuneMessage message { };
int64_t frequency_index = 0;
int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_;
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
while( !chThdShouldTerminate() ) {
if (_scanning) { //Scanning
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
if (!restart_scan) { //looping at full speed
if (_fwd) { //forward
frequency_index++;
if (frequency_index >= size)
frequency_index = 0;
} else { //reverse
if (frequency_index < 1)
frequency_index = size;
frequency_index--;
}
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_); // Retune
}
else
restart_scan=false; //Effectively skipping first retuning, giving system time
}
message.freq = frequency_range_.min + frequency_index * def_step_;
message.range = 0; //Inform freq (for coloring purposes also!)
EventDispatcher::send_message(message);
}
else { //NOT scanning
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
}
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
}
}
}
void ScannerView::handle_retune(uint32_t i) {
void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
current_index = freq_idx; //since it is an ongoing scan, this is a new index
current_frequency = freq;
switch (scan_thread->is_freq_lock())
{
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
text_cycle.set( to_string_dec_uint(i + 1,3) );
current_index = i; //since it is an ongoing scan, this is a new index
if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description
if (frequency_list.size() > 0) {
text_cycle.set( to_string_dec_uint(freq_idx + 1,3) );
}
if (freq_idx < description_list.size() && description_list[freq_idx].size() > 0) {
desc_cycle.set( description_list[freq_idx] ); //Show new description
}
break;
case 1: //STARTING LOCK FREQ
big_display.set_style(&style_yellow);
@@ -146,7 +199,7 @@ void ScannerView::handle_retune(uint32_t i) {
default: //freq lock is checking the signal, do not update display
return;
}
big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
big_display.set(freq); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
}
void ScannerView::focus() {
@@ -154,6 +207,8 @@ void ScannerView::focus() {
}
ScannerView::~ScannerView() {
// make sure to stop the thread before shutting down the receiver
scan_thread.reset();
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
@@ -297,23 +352,15 @@ ScannerView::ScannerView(
description_list.clear();
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
description_list.push_back(
"M" + to_string_short_freq(frequency_range.min) + ">"
+ to_string_short_freq(frequency_range.max) + "S"
+ to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
);
rf::Frequency frequency = frequency_range.min;
while (frequency_list.size() < FREQMAN_MAX_PER_FILE && frequency <= frequency_range.max) { //add manual range
frequency_list.push_back(frequency);
description_list.push_back(""); //If empty, will keep showing the last description
frequency+=def_step;
}
show_max();
desc_cycle.set("");
text_max.set("");
text_cycle.set("");
if ( userpause ) //If user-paused, resume
user_resume();
big_display.set_style(&style_grey); //Back to grey color
start_scan_thread(); //RESTART SCANNER THREAD
start_scan_thread(frequency_range, def_step); //RESTART SCANNER THREAD
}
};
@@ -333,35 +380,56 @@ ScannerView::ScannerView(
big_display.set_style(&style_grey); //Back to grey color
};
button_add.on_select = [this](Button&) { //frequency_list[current_index]
button_add.on_select = [this](Button&) { // current_frequency
File scanner_file;
std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
if (!result.is_valid()) {
std::string frequency_to_add = "f="
+ to_string_dec_uint(frequency_list[current_index] / 1000)
+ to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0');
char one_char[1]; //Read it char by char
std::string line; //and put read line in here
const std::string frequency_to_add = "f="
+ to_string_dec_uint(current_frequency / 1000)
+ to_string_dec_uint(current_frequency % 1000UL, 3, '0');
bool found=false;
for (size_t pointer=0; pointer < scanner_file.size();pointer++) {
constexpr size_t buffer_size = 1024;
char buffer[buffer_size];
for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) {
size_t adjusted_buffer_size;
if (pointer + buffer_size >= scanner_file.size()) {
memset(buffer, 0, sizeof(buffer));
adjusted_buffer_size = scanner_file.size() - pointer;
}
else {
adjusted_buffer_size = buffer_size;
}
scanner_file.seek(pointer);
scanner_file.read(one_char, 1);
if ((int)one_char[0] > 31) { //ascii space upwards
line += one_char[0]; //Add it to the textline
}
else if (one_char[0] == '\n') { //New Line
if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) {
found=true;
break;
scanner_file.read(buffer, adjusted_buffer_size);
for (size_t i = 0; i < adjusted_buffer_size; ++i) {
if (buffer[i] == frequency_to_add.data()[freq_str_idx]) {
++freq_str_idx;
if (freq_str_idx >= frequency_to_add.size()) {
found = true;
break;
}
}
else {
freq_str_idx = 0;
}
line.clear(); //Ready for next textline
}
}
if (found) {
break;
}
}
if (found) {
nav_.display_modal("Error", "Frequency already exists");
big_display.set(frequency_list[current_index]); //After showing an error
big_display.set(current_frequency); //After showing an error
}
else {
scanner_file.append(freq_file_path); //Second: append if it is not there
@@ -370,7 +438,7 @@ ScannerView::ScannerView(
} else
{
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
big_display.set(frequency_list[current_index]); //After showing an error
big_display.set(current_frequency); //After showing an error
}
};
@@ -516,9 +584,9 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
switch (new_mod) {
case NFM: //bw 16k (2) default
bw.emplace_back("8k5", 0);
bw.emplace_back("11k", 0);
bw.emplace_back("16k", 0);
bw.emplace_back("8k5 ", 0);
bw.emplace_back("11k ", 0);
bw.emplace_back("16k ", 0);
field_bw.set_options(bw);
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
@@ -545,7 +613,7 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
break;
case WFM:
bw.emplace_back("200k", 0);
bw.emplace_back("200k ", 0);
field_bw.set_options(bw);
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
@@ -566,4 +634,10 @@ void ScannerView::start_scan_thread() {
scan_thread = std::make_unique<ScannerThread>(frequency_list);
}
void ScannerView::start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step) {
receiver_model.enable();
receiver_model.set_squelch_level(0);
scan_thread = std::make_unique<ScannerThread>(frequency_range, def_step);
}
} /* namespace ui */
+8 -4
View File
@@ -46,6 +46,7 @@ size_t const mod_step[3] = {9000, 100000, 12500 };
class ScannerThread {
public:
ScannerThread(std::vector<rf::Frequency> frequency_list);
ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step);
~ScannerThread();
void set_scanning(const bool v);
@@ -67,6 +68,8 @@ public:
private:
std::vector<rf::Frequency> frequency_list_ { };
jammer::jammer_range_t frequency_range_ {false, 0, 0};
size_t def_step_ { 0 };
Thread* thread { nullptr };
bool _scanning { true };
@@ -75,6 +78,7 @@ private:
uint32_t _freq_del { 0 };
static msg_t static_fn(void* arg);
void run();
void create_thread();
};
class ScannerView : public View {
@@ -84,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(),
@@ -120,6 +122,7 @@ private:
NavigationView& nav_;
void start_scan_thread();
void start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step);
size_t change_mode(uint8_t mod_type);
void show_max();
void scan_pause();
@@ -129,7 +132,7 @@ private:
void on_statistics_update(const ChannelStatistics& statistics);
void on_headphone_volume_changed(int32_t v);
void handle_retune(uint32_t i);
void handle_retune(int64_t freq, uint32_t freq_idx);
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
int32_t squelch { 0 };
@@ -139,6 +142,7 @@ private:
freqman_db database { };
std::string loaded_file_name;
uint32_t current_index { 0 };
rf::Frequency current_frequency { 0 };
bool userpause { false };
Labels labels {
@@ -295,7 +299,7 @@ private:
Message::ID::Retune,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const RetuneMessage*>(p);
this->handle_retune(message.range);
this->handle_retune(message.freq, message.range);
}
};
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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_sd_over_usb.hpp"
#include "portapack_shared_memory.hpp"
namespace ui {
SdOverUsbView::SdOverUsbView(NavigationView& nav) : nav_ (nav) {
add_children({
&labels,
&button_run
});
button_run.on_select = [this](Button&) {
ui::Painter painter;
painter.fill_rectangle(
{ 0, 0, portapack::display.width(), portapack::display.height() },
ui::Color::black()
);
painter.draw_bitmap(
{ portapack::display.width()/2-8, portapack::display.height()/2-8 },
bitmap_icon_hackrf,
ui::Color::yellow(),
ui::Color::black()
);
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
portapack::shutdown(true);
m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false);
m0_halt();
/* will not return*/
};
}
void SdOverUsbView::focus() {
button_run.focus();
}
} /* namespace ui */
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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.
*/
#ifndef __UI_SD_OVER_USB_H__
#define __UI_SD_OVER_USB_H__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "ff.h"
#include "baseband_api.hpp"
#include "core_control.hpp"
#include <cstdint>
namespace ui {
class SdOverUsbView : public View {
public:
SdOverUsbView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Flash Utility"; };
private:
NavigationView& nav_;
Labels labels {
{ { 3 * 8, 2 * 16 }, "Click Run to start the", Color::white() },
{ { 3 * 8, 3 * 16 }, "USB Mass Storage Mode.", Color::white() },
{ { 3 * 8, 5 * 16 }, "It can take up to 20s", Color::white() },
{ { 3 * 8, 6 * 16 }, "for the drive to be", Color::white() },
{ { 3 * 8, 7 * 16 }, "available.", Color::white() },
};
Button button_run {
{ 9 * 8, 15 * 16, 12 * 8, 3 * 16 },
"Run"
};
};
} /* namespace ui */
#endif /*__UI_SD_OVER_USB_H__*/
+1 -1
View File
@@ -38,7 +38,7 @@ public:
~WipeSDView();
void focus() override;
std::string title() const override { return "Wipe SD Card"; };
std::string title() const override { return "Wipe sdcard"; };
private:
NavigationView& nav_;
+502 -313
View File
@@ -38,353 +38,542 @@ using namespace portapack;
#include "ui_font_fixed_8x16.hpp"
#include "cpld_update.hpp"
#include "freqman.hpp"
namespace ui {
SetDateTimeView::SetDateTimeView(
NavigationView& nav
) {
button_save.on_select = [&nav, this](Button&){
const auto model = this->form_collect();
const rtc::RTC new_datetime {
model.year, model.month, model.day,
model.hour, model.minute, model.second
};
rtcSetTime(&RTCD1, &new_datetime);
nav.pop();
},
SetDateTimeView::SetDateTimeView(
NavigationView& nav
) {
button_save.on_select = [&nav, this](Button&){
const auto model = this->form_collect();
const rtc::RTC new_datetime {
model.year, model.month, model.day,
model.hour, model.minute, model.second
button_cancel.on_select = [&nav](Button&){
nav.pop();
},
add_children({
&labels,
&field_year,
&field_month,
&field_day,
&field_hour,
&field_minute,
&field_second,
&button_save,
&button_cancel,
});
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
SetDateTimeModel model {
datetime.year(),
datetime.month(),
datetime.day(),
datetime.hour(),
datetime.minute(),
datetime.second()
};
rtcSetTime(&RTCD1, &new_datetime);
nav.pop();
},
button_cancel.on_select = [&nav](Button&){
nav.pop();
},
add_children({
&labels,
&field_year,
&field_month,
&field_day,
&field_hour,
&field_minute,
&field_second,
&button_save,
&button_cancel,
});
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
SetDateTimeModel model {
datetime.year(),
datetime.month(),
datetime.day(),
datetime.hour(),
datetime.minute(),
datetime.second()
};
form_init(model);
}
void SetDateTimeView::focus() {
button_cancel.focus();
}
void SetDateTimeView::form_init(const SetDateTimeModel& model) {
field_year.set_value(model.year);
field_month.set_value(model.month);
field_day.set_value(model.day);
field_hour.set_value(model.hour);
field_minute.set_value(model.minute);
field_second.set_value(model.second);
}
SetDateTimeModel SetDateTimeView::form_collect() {
return {
.year = static_cast<uint16_t>(field_year.value()),
.month = static_cast<uint8_t>(field_month.value()),
.day = static_cast<uint8_t>(field_day.value()),
.hour = static_cast<uint8_t>(field_hour.value()),
.minute = static_cast<uint8_t>(field_minute.value()),
.second = static_cast<uint8_t>(field_second.value())
};
}
SetRadioView::SetRadioView(
NavigationView& nav
) {
button_cancel.on_select = [&nav](Button&){
nav.pop();
};
const auto reference = portapack::clock_manager.get_reference();
std::string source_name("---");
switch(reference.source) {
case ClockManager::ReferenceSource::Xtal: source_name = "HackRF"; break;
case ClockManager::ReferenceSource::PortaPack: source_name = "PortaPack"; break;
case ClockManager::ReferenceSource::External: source_name = "External"; break;
form_init(model);
}
value_source.set(source_name);
value_source_frequency.set(to_string_dec_uint(reference.frequency / 1000000, 2) + "." + to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz");
void SetDateTimeView::focus() {
button_cancel.focus();
}
label_source.set_style(&style_text);
value_source.set_style(&style_text);
value_source_frequency.set_style(&style_text);
void SetDateTimeView::form_init(const SetDateTimeModel& model) {
field_year.set_value(model.year);
field_month.set_value(model.month);
field_day.set_value(model.day);
field_hour.set_value(model.hour);
field_minute.set_value(model.minute);
field_second.set_value(model.second);
}
add_children({
&label_source,
&value_source,
&value_source_frequency,
});
SetDateTimeModel SetDateTimeView::form_collect() {
return {
.year = static_cast<uint16_t>(field_year.value()),
.month = static_cast<uint8_t>(field_month.value()),
.day = static_cast<uint8_t>(field_day.value()),
.hour = static_cast<uint8_t>(field_hour.value()),
.minute = static_cast<uint8_t>(field_minute.value()),
.second = static_cast<uint8_t>(field_second.value())
};
}
SetRadioView::SetRadioView(
NavigationView& nav
) {
button_cancel.on_select = [&nav](Button&){
nav.pop();
};
const auto reference = portapack::clock_manager.get_reference();
std::string source_name("---");
switch(reference.source) {
case ClockManager::ReferenceSource::Xtal: source_name = "HackRF"; break;
case ClockManager::ReferenceSource::PortaPack: source_name = "PortaPack"; break;
case ClockManager::ReferenceSource::External: source_name = "External"; break;
}
value_source.set(source_name);
value_source_frequency.set(to_string_dec_uint(reference.frequency / 1000000, 2) + "." + to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz");
label_source.set_style(&style_text);
value_source.set_style(&style_text);
value_source_frequency.set_style(&style_text);
if( reference.source == ClockManager::ReferenceSource::Xtal ) {
add_children({
&labels_correction,
&field_ppm,
});
&label_source,
&value_source,
&value_source_frequency,
});
if( reference.source == ClockManager::ReferenceSource::Xtal ) {
add_children({
&labels_correction,
&field_ppm,
});
}
add_children({
&check_clkout,
&field_clkout_freq,
&labels_clkout_khz,
&value_freq_step,
&labels_bias,
&check_bias,
&button_save,
&button_cancel
});
SetFrequencyCorrectionModel model {
static_cast<int8_t>(portapack::persistent_memory::correction_ppb() / 1000) , 0
};
form_init(model);
check_clkout.set_value(portapack::persistent_memory::clkout_enabled());
check_clkout.on_select = [this](Checkbox&, bool v) {
clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v);
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq());
value_freq_step.set_style(&style_text);
field_clkout_freq.on_select = [this](NumberField&) {
freq_step_khz++;
if(freq_step_khz > 3) {
freq_step_khz = 0;
}
switch(freq_step_khz) {
case 0:
value_freq_step.set(" |");
break;
case 1:
value_freq_step.set(" | ");
break;
case 2:
value_freq_step.set(" | ");
break;
case 3:
value_freq_step.set("| ");
break;
}
field_clkout_freq.set_step(pow(10, freq_step_khz));
};
check_bias.set_value(portapack::get_antenna_bias());
check_bias.on_select = [this](Checkbox&, bool v) {
portapack::set_antenna_bias(v);
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
button_save.on_select = [this, &nav](Button&){
const auto model = this->form_collect();
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
portapack::persistent_memory::set_clkout_freq(model.freq);
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled());
nav.pop();
};
}
add_children({
&check_clkout,
&field_clkout_freq,
&labels_clkout_khz,
&value_freq_step,
&labels_bias,
&check_bias,
&button_save,
&button_cancel
});
void SetRadioView::focus() {
button_save.focus();
}
SetFrequencyCorrectionModel model {
static_cast<int8_t>(portapack::persistent_memory::correction_ppb() / 1000) , 0
};
void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
field_ppm.set_value(model.ppm);
}
form_init(model);
check_clkout.set_value(portapack::persistent_memory::clkout_enabled());
check_clkout.on_select = [this](Checkbox&, bool v) {
clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v);
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq());
value_freq_step.set_style(&style_text);
field_clkout_freq.on_select = [this](NumberField&) {
freq_step_khz++;
if(freq_step_khz > 3) {
freq_step_khz = 0;
}
switch(freq_step_khz) {
case 0:
value_freq_step.set(" |");
break;
case 1:
value_freq_step.set(" | ");
break;
case 2:
value_freq_step.set(" | ");
break;
case 3:
value_freq_step.set("| ");
break;
}
field_clkout_freq.set_step(pow(10, freq_step_khz));
};
check_bias.set_value(portapack::get_antenna_bias());
check_bias.on_select = [this](Checkbox&, bool v) {
portapack::set_antenna_bias(v);
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
button_save.on_select = [this, &nav](Button&){
const auto model = this->form_collect();
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
portapack::persistent_memory::set_clkout_freq(model.freq);
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled());
nav.pop();
};
}
void SetRadioView::focus() {
button_save.focus();
}
void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
field_ppm.set_value(model.ppm);
}
SetFrequencyCorrectionModel SetRadioView::form_collect() {
return {
.ppm = static_cast<int8_t>(field_ppm.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
};
}
SetUIView::SetUIView(NavigationView& nav) {
add_children({
&checkbox_disable_touchscreen,
&checkbox_speaker,
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
&checkbox_showclock,
&options_clockformat,
&checkbox_guireturnflag,
&button_save,
&button_cancel
});
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen());
checkbox_speaker.set_value(persistent_memory::config_speaker());
checkbox_showsplash.set_value(persistent_memory::config_splash());
checkbox_showclock.set_value(!persistent_memory::hide_clock());
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
const auto backlight_config = persistent_memory::config_backlight_timer();
checkbox_bloff.set_value(backlight_config.timeout_enabled());
options_bloff.set_by_value(backlight_config.timeout_enum());
if (persistent_memory::clock_with_date()) {
options_clockformat.set_selected_index(1);
} else {
options_clockformat.set_selected_index(0);
SetFrequencyCorrectionModel SetRadioView::form_collect() {
return {
.ppm = static_cast<int8_t>(field_ppm.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
};
}
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_config_backlight_timer({
(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(),
checkbox_bloff.value()
});
if (checkbox_showclock.value()){
if (options_clockformat.selected_index() == 1)
persistent_memory::set_clock_with_date(true);
else
persistent_memory::set_clock_with_date(false);
SetUIView::SetUIView(NavigationView& nav) {
add_children({
&checkbox_disable_touchscreen,
&checkbox_speaker,
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
&checkbox_showclock,
&options_clockformat,
&checkbox_guireturnflag,
&button_save,
&button_cancel
});
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen());
checkbox_speaker.set_value(persistent_memory::config_speaker());
checkbox_showsplash.set_value(persistent_memory::config_splash());
checkbox_showclock.set_value(!persistent_memory::hide_clock());
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
const auto backlight_config = persistent_memory::config_backlight_timer();
checkbox_bloff.set_value(backlight_config.timeout_enabled());
options_bloff.set_by_value(backlight_config.timeout_enum());
if (persistent_memory::clock_with_date()) {
options_clockformat.set_selected_index(1);
} else {
options_clockformat.set_selected_index(0);
}
if (checkbox_speaker.value()) audio::output::speaker_mute(); //Just mute audio if speaker is disabled
persistent_memory::set_config_speaker(checkbox_speaker.value()); //Store Speaker status
StatusRefreshMessage message { }; //Refresh status bar with/out speaker
EventDispatcher::send_message(message);
persistent_memory::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value());
persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetUIView::focus() {
button_save.focus();
}
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_config_backlight_timer({
(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(),
checkbox_bloff.value()
});
if (checkbox_showclock.value()){
if (options_clockformat.selected_index() == 1)
persistent_memory::set_clock_with_date(true);
else
persistent_memory::set_clock_with_date(false);
}
if (checkbox_speaker.value()) audio::output::speaker_mute(); //Just mute audio if speaker is disabled
persistent_memory::set_config_speaker(checkbox_speaker.value()); //Store Speaker status
StatusRefreshMessage message { }; //Refresh status bar with/out speaker
EventDispatcher::send_message(message);
persistent_memory::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value());
persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetUIView::focus() {
button_save.focus();
}
// ---------------------------------------------------------
// Appl. Settings
// ---------------------------------------------------------
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
add_children({
&checkbox_load_app_settings,
&checkbox_save_app_settings,
&button_save,
&button_cancel
});
checkbox_load_app_settings.set_value(persistent_memory::load_app_settings());
checkbox_save_app_settings.set_value(persistent_memory::save_app_settings());
// ---------------------------------------------------------
// Appl. Settings
// ---------------------------------------------------------
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
add_children({
&checkbox_load_app_settings,
&checkbox_save_app_settings,
&button_save,
&button_cancel
});
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());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
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());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetAppSettingsView::focus() {
button_save.focus();
}
void SetAppSettingsView::focus() {
button_save.focus();
}
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({
&labels,
&field_tone_mix,
&button_save,
&button_cancel
});
// ---------------------------------------------------------
// Converter Settings
// ---------------------------------------------------------
SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
add_children({
&check_show_converter,
&check_converter,
&converter_mode,
&button_converter_freq,
&button_save,
&button_cancel
});
field_tone_mix.set_value(persistent_memory::tone_mix());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_tone_mix(field_tone_mix.value());
nav.pop();
};
check_show_converter.set_value(!portapack::persistent_memory::config_hide_converter());
check_show_converter.on_select = [this](Checkbox&, bool v) {
portapack::persistent_memory::set_config_hide_converter(!v);
if( !v )
{
check_converter.set_value(false);
}
// Retune to take converter change in account
receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() );
//Refresh status bar with/out converter
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
check_converter.set_value(portapack::persistent_memory::config_converter());
check_converter.on_select = [this](Checkbox&, bool v) {
if( v )
{
check_show_converter.set_value(true);
portapack::persistent_memory::set_config_hide_converter(false);
}
portapack::persistent_memory::set_config_converter(v);
// Retune to take converter change in account
receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() );
//Refresh status bar with/out converter
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
void SetAudioView::focus() {
button_save.focus();
}
converter_mode.set_by_value( portapack::persistent_memory::config_updown_converter() );
converter_mode.on_change = [this](size_t, OptionsField::value_t v) {
portapack::persistent_memory::set_config_updown_converter( v );
//Refresh status bar with icon up or down
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
SetQRCodeView::SetQRCodeView(NavigationView& nav) {
add_children({
&checkbox_bigger_qr,
&button_save,
&button_cancel
});
button_converter_freq.set_text( to_string_short_freq( portapack::persistent_memory::config_converter_freq() ) + "MHz");
button_converter_freq.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(portapack::persistent_memory::config_converter_freq() );
new_view->on_changed = [this, &button](rf::Frequency f) {
portapack::persistent_memory::set_config_converter_freq( f );
// Retune to take converter change in account
receiver_model.set_tuning_frequency( portapack::persistent_memory::tuned_frequency() );
button_converter_freq.set_text( "<" + to_string_short_freq( f ) + " MHz>" );
};
};
checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value());
nav.pop();
};
button_save.on_select = [&nav, this](Button&) {
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
void SetConverterSettingsView::focus() {
button_save.focus();
}
}
// ---------------------------------------------------------
// Persistent Memory Settings
// ---------------------------------------------------------
SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
add_children({
&text_pmem_about,
&text_pmem_informations,
&text_pmem_status,
&check_load_mem_at_startup,
&button_save_mem_to_file,
&button_load_mem_from_file,
&button_load_mem_defaults,
&button_return
});
void SetQRCodeView::focus() {
button_save.focus();
}
bool load_mem_at_startup = false ;
File pmem_flag_file_handle ;
// ---------------------------------------------------------
// Settings main menu
// ---------------------------------------------------------
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
if( portapack::persistent_memory::show_gui_return_icon() )
{
add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } );
}
add_items({
{ "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push<SetAudioView>(); } },
{ "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push<SetRadioView>(); } },
{ "User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push<SetUIView>(); } },
{ "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push<SetDateTimeView>(); } },
{ "Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push<TouchCalibrationView>(); } },
{ "App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav](){ nav.push<SetAppSettingsView>(); } },
{ "QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav](){ nav.push<SetQRCodeView>(); } }
});
set_max_rows(2); // allow wider buttons
}
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())
{
load_mem_at_startup = true ;
}
check_load_mem_at_startup.set_value(load_mem_at_startup);
check_load_mem_at_startup.on_select = [this](Checkbox&, bool v) {
File pmem_flag_file_handle ;
std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ;
if( v )
{
auto result = pmem_flag_file_handle.open(pmem_flag_file);
if(result.is_valid())
{
auto result = pmem_flag_file_handle.create(pmem_flag_file); //third: create if it is not there
if( !result.is_valid() )
{
text_pmem_status.set("pmem flag file created");
}
else
{
text_pmem_status.set("!err. creating pmem flagfile!");
}
}
else
{
text_pmem_status.set("pmem flag already present");
}
}
else
{
auto result = delete_file( pmem_flag_file );
if( result != 0 )
{
text_pmem_status.set("!err. deleting pmem flagfile!");
}
else
{
text_pmem_status.set("pmem flag file deleted");
}
}
};
button_save_mem_to_file.on_select = [&nav, this](Button&) {
if( !portapack::persistent_memory::save_persistent_settings_to_file("SETTINGS/pmem_settings") )
{
text_pmem_status.set("!problem saving settings!");
}
else
{
text_pmem_status.set("settings saved");
}
};
button_load_mem_from_file.on_select = [&nav, this](Button&) {
if( !portapack::persistent_memory::load_persistent_settings_from_file("SETTINGS/pmem_settings") )
{
text_pmem_status.set("!problem loading settings!");
}
else
{
text_pmem_status.set("settings loaded");
//Refresh status bar with icon up or down
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
}
};
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();
};
}
void SetPersistentMemoryView::focus() {
button_return.focus();
}
//
// Audio settings
//
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({
&labels,
&field_tone_mix,
&button_save,
&button_cancel
});
field_tone_mix.set_value(persistent_memory::tone_mix());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_tone_mix(field_tone_mix.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetAudioView::focus() {
button_save.focus();
}
SetQRCodeView::SetQRCodeView(NavigationView& nav) {
add_children({
&checkbox_bigger_qr,
&button_save,
&button_cancel
});
checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetQRCodeView::focus() {
button_save.focus();
}
// ---------------------------------------------------------
// Settings main menu
// ---------------------------------------------------------
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
if( portapack::persistent_memory::show_gui_return_icon() )
{
add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } );
}
add_items({
{ "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push<SetAudioView>(); } },
{ "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push<SetRadioView>(); } },
{ "User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push<SetUIView>(); } },
{ "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push<SetDateTimeView>(); } },
{ "Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push<TouchCalibrationView>(); } },
{ "App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav](){ nav.push<SetAppSettingsView>(); } },
{ "Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push<SetConverterSettingsView>(); } },
{ "QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav](){ nav.push<SetQRCodeView>(); } },
{ "P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav](){ nav.push<SetPersistentMemoryView>(); } },
});
set_max_rows(2); // allow wider buttons
}
} /* namespace ui */
+106 -6
View File
@@ -124,7 +124,7 @@ public:
void focus() override;
std::string title() const override { return "Radio settings"; };
std::string title() const override { return "Radio"; };
private:
const Style style_text {
@@ -154,7 +154,7 @@ private:
{ { 6 * 8, 4 * 16 }, "PPM", Color::light_grey() },
};
Checkbox check_clkout {
Checkbox check_clkout {
{ 18, (6 * 16 - 4) },
13,
"Enable CLKOUT"
@@ -193,7 +193,7 @@ private:
};
Checkbox check_bias {
{ 28, 13 * 16 },
{ 18, 12 * 16 },
5,
"Turn on bias voltage"
};
@@ -219,7 +219,7 @@ public:
void focus() override;
std::string title() const override { return "UI settings"; };
std::string title() const override { return "UI"; };
private:
@@ -299,7 +299,7 @@ public:
void focus() override;
std::string title() const override { return "App Settings"; };
std::string title() const override { return "AppSettings"; };
private:
@@ -314,6 +314,53 @@ private:
25,
"Save app settings"
};
Button button_save {
{ 2 * 8, 16 * 16, 12 * 8, 32 },
"Save"
};
Button button_cancel {
{ 16 * 8, 16 * 16, 12 * 8, 32 },
"Cancel",
};
};
class SetConverterSettingsView : public View {
public:
SetConverterSettingsView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Converter"; };
private:
Checkbox check_show_converter {
{ 18, 4 * 16},
19,
"show/hide converter"
};
Checkbox check_converter {
{ 18, 6 * 16},
7,
"enable/disable converter"
};
OptionsField converter_mode {
{ 18 , 8 * 16 + 4 },
0,
{
{" + ",0}, // up converter
{" - ",1} // down converter
}
};
Button button_converter_freq {
{ 18 + 4 * 8 , 8 * 16 , 16 * 8 , 24 },
"",
};
Button button_save {
{ 2 * 8, 16 * 16, 12 * 8, 32 },
@@ -332,7 +379,7 @@ public:
void focus() override;
std::string title() const override { return "Audio settings"; };
std::string title() const override { return "Audio"; };
private:
Labels labels {
@@ -385,6 +432,59 @@ private:
};
};
class SetPersistentMemoryView : public View {
public:
SetPersistentMemoryView(NavigationView& nav);
void focus() override;
std::string title() const override { return "P.Mem Mgmt"; };
private:
Text text_pmem_about {
{ 0, 1 * 16, 240 , 16 },
"PersistentMemory from/to SD"
};
Text text_pmem_informations {
{ 0, 2 * 16, 240 , 16 },
"use: when no/dead coin bat."
};
Text text_pmem_status {
{ 0, 3 * 16, 240 , 16 },
""
};
Checkbox check_load_mem_at_startup {
{ 18, 6 * 16},
19,
"load from sd at startup"
};
Button button_save_mem_to_file {
{ 0, 8 * 16, 240, 32 },
"save p.mem to sdcard"
};
Button button_load_mem_from_file {
{ 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",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
+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 -1
View File
@@ -62,7 +62,7 @@ public:
void focus() override;
std::string title() const override { return "Radiosonde RX"; };
std::string title() const override { return "Radiosnd RX"; };
+1 -1
View File
@@ -53,7 +53,7 @@ public:
void focus() override;
void paint(Painter&) override;
std::string title() const override { return "SSTV TX (beta)"; };
std::string title() const override { return "SSTV TX"; };
private:
NavigationView& nav_;
+1 -1
View File
@@ -110,7 +110,7 @@ public:
void focus() override;
std::string title() const override { return "TouchTunes TX"; };
std::string title() const override { return "TouchTunes"; };
private:
uint32_t scan_button_index { };
+1 -1
View File
@@ -37,7 +37,7 @@ namespace ui
public:
WhipCalcView(NavigationView &nav);
void focus() override;
std::string title() const override { return "Antenna length"; };
std::string title() const override { return "Ant. length"; };
private:
const double speed_of_light_mps = 299792458.0; // m/s
+1 -1
View File
@@ -307,7 +307,7 @@ void run_image(const portapack::spi_flash::image_tag_t image_tag) {
creg::m4txevent::clear();
m4_init(image_tag, portapack::memory::map::m4_code);
m4_init(image_tag, portapack::memory::map::m4_code, false);
baseband_image_running = true;
creg::m4txevent::enable();
File diff suppressed because it is too large Load Diff
+204 -91
View File
@@ -41,12 +41,18 @@ constexpr si5351::Inputs si5351_inputs {
static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong");
static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong");
constexpr si5351::PLLInputSource::Type si5351_pll_input_sources {
constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources {
si5351::PLLInputSource::PLLA_Source_XTAL
| si5351::PLLInputSource::PLLB_Source_CLKIN
| si5351::PLLInputSource::CLKIN_Div1
};
constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources {
si5351::PLLInputSource::PLLA_Source_XTAL
| si5351::PLLInputSource::PLLB_Source_XTAL
| si5351::PLLInputSource::CLKIN_Div1
};
constexpr si5351::PLL si5351_pll_xtal_25m {
.f_in = si5351_inputs.f_xtal,
.a = 32,
@@ -61,7 +67,8 @@ constexpr si5351::PLL si5351_pll_clkin_10m {
.b = 0,
.c = 1,
};
constexpr auto si5351_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0);
static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong");
static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong");
@@ -98,7 +105,7 @@ constexpr si5351::MultisynthFractional si5351_ms_0_8m {
.c = 1,
.r_div = 1,
};
constexpr auto si5351_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_codec);
constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec);
constexpr si5351::MultisynthFractional si5351_ms_group {
.f_src = si5351_vco_f,
@@ -107,8 +114,17 @@ constexpr si5351::MultisynthFractional si5351_ms_group {
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_cpld);
constexpr auto si5351_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_sgpio);
constexpr auto si5351c_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_og_cpld);
constexpr auto si5351c_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_og_sgpio);
constexpr si5351::MultisynthFractional si5351_ms_16m {
.f_src = si5351_vco_f,
.a = 50,
.b = 0,
.c = 1,
.r_div = 0,
};
constexpr auto si5351a_ms_1_sgpio_16m_reg = si5351_ms_16m.reg(clock_generator_output_r9_sgpio);
constexpr si5351::MultisynthFractional si5351_ms_10m {
.f_src = si5351_vco_f,
@@ -117,7 +133,8 @@ constexpr si5351::MultisynthFractional si5351_ms_10m {
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_3_10m_reg = si5351_ms_10m.reg(3);
constexpr auto si5351c_ms_3_10m_reg = si5351_ms_10m.reg(3);
constexpr auto si5351a_ms_2_mcu_10m_reg = si5351_ms_10m.reg(clock_generator_output_r9_mcu_clkin);
constexpr si5351::MultisynthFractional si5351_ms_40m {
.f_src = si5351_vco_f,
@@ -128,10 +145,11 @@ constexpr si5351::MultisynthFractional si5351_ms_40m {
};
constexpr auto si5351_ms_rffc5072 = si5351_ms_40m;
constexpr auto si5351_ms_max2837 = si5351_ms_40m;
constexpr auto si5351_ms_max283x = si5351_ms_40m;
constexpr auto si5351_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_first_if);
constexpr auto si5351_ms_5_reg = si5351_ms_max2837.reg(clock_generator_output_second_if);
constexpr auto si5351c_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_og_first_if);
constexpr auto si5351c_ms_5_reg = si5351_ms_max283x.reg(clock_generator_output_og_second_if);
constexpr auto si5351a_ms_0_if_40m_reg = si5351_ms_40m.reg(clock_generator_output_r9_if);
static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong");
static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong");
@@ -139,14 +157,8 @@ static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong");
static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong");
static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 50MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 50MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 50MHz P3 wrong");
static_assert(si5351_ms_max2837.f_out() == max2837_reference_f, "MAX2837 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 40MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 40MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 40MHz P3 wrong");
static_assert(si5351_ms_max283x.f_out() == max283x_reference_f, "MAX283x reference f_out wrong");
constexpr si5351::MultisynthInteger si5351_ms_int_off {
.f_src = si5351_vco_f,
@@ -154,40 +166,60 @@ constexpr si5351::MultisynthInteger si5351_ms_int_off {
.r_div = 0,
};
constexpr si5351::MultisynthInteger si5351_ms_int_mcu_clkin {
constexpr si5351::MultisynthInteger si5351_ms_int_40m {
.f_src = si5351_vco_f,
.a = 20,
.r_div = 0,
};
constexpr auto si5351_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_mcu_clkin);
constexpr si5351::MultisynthInteger si5351_ms_int_10m {
.f_src = si5351_vco_f,
.a = 80,
.r_div = 0,
};
constexpr auto si5351c_ms_int_mcu_clkin = si5351_ms_int_40m;
constexpr auto si5351a_ms_int_mcu_clkin = si5351_ms_int_10m;
constexpr auto si5351c_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351c_ms_int_mcu_clkin);
constexpr auto si5351a_ms6_7_off_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_off);
static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong");
static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong");
static_assert(si5351_ms_int_mcu_clkin.f_out() == mcu_clkin_f, "MS int MCU CLKIN f_out wrong");
// static_assert(si5351_ms_int_mcu_clkin.p1() == 20, "MS int MCU CLKIN P1 wrong");
static_assert(si5351c_ms_int_mcu_clkin.f_out() == mcu_clkin_og_f, "MS int MCU CLKIN OG f_out wrong");
static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CLKIN r9 f_out wrong");
using namespace si5351;
static constexpr ClockControl::MultiSynthSource get_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
static constexpr ClockControl::MultiSynthSource get_si5351c_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
return (reference_source == ClockManager::ReferenceSource::Xtal)
? ClockControl::MultiSynthSource::PLLA
: ClockControl::MultiSynthSource::PLLB
;
}
constexpr ClockControls si5351_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
constexpr ClockControls si5351c_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
} };
constexpr ClockControls si5351a_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
} };
ClockManager::Reference ClockManager::get_reference() const {
return reference;
}
@@ -212,11 +244,25 @@ void ClockManager::init_clock_generator() {
clock_generator.reset();
clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF);
clock_generator.enable_fanout();
clock_generator.set_pll_input_sources(si5351_pll_input_sources);
clock_generator.set_pll_input_sources(hackrf_r9
? si5351a_pll_input_sources
: si5351c_pll_input_sources);
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
auto clock_generator_output_mcu_clkin = hackrf_r9
? clock_generator_output_r9_mcu_clkin
: clock_generator_output_og_mcu_clkin;
clock_generator.set_clock_control(
clock_generator_output_mcu_clkin,
si5351_clock_control_common[clock_generator_output_mcu_clkin].clk_src(ClockControl::ClockSource::CLKIN).clk_pdn(ClockControl::ClockPowerDown::Power_On)
si5351_clock_control_common[clock_generator_output_mcu_clkin]
.clk_src(hackrf_r9
? ClockControl::ClockSource::Xtal
: ClockControl::ClockSource::CLKIN)
.clk_pdn(ClockControl::ClockPowerDown::Power_On)
);
clock_generator.enable_output(clock_generator_output_mcu_clkin);
@@ -224,7 +270,10 @@ void ClockManager::init_clock_generator() {
clock_generator.disable_output(clock_generator_output_mcu_clkin);
const auto ref_pll = get_reference_clock_generator_pll(reference.source);
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
const ClockControls si5351_clock_control = ClockControls { {
si5351_clock_control_common[0].ms_src(ref_pll),
si5351_clock_control_common[1].ms_src(ref_pll),
@@ -237,21 +286,35 @@ void ClockManager::init_clock_generator() {
} };
clock_generator.set_clock_control(si5351_clock_control);
clock_generator.write(si5351_pll_a_xtal_reg);
clock_generator.write(si5351_pll_b_clkin_reg);
clock_generator.write(si5351_ms_0_8m_reg);
clock_generator.write(si5351_ms_1_group_reg);
clock_generator.write(si5351_ms_2_group_reg);
clock_generator.write(si5351_ms_3_10m_reg);
clock_generator.write(si5351_ms_4_reg);
clock_generator.write(si5351_ms_5_reg);
clock_generator.write(si5351_ms6_7_off_mcu_clkin_reg);
if (hackrf_r9) {
const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal)
? si5351_pll_a_xtal_reg
: si5351a_pll_a_clkin_reg;
clock_generator.write(pll_reg);
clock_generator.write(si5351a_ms_0_if_40m_reg);
clock_generator.write(si5351a_ms_1_sgpio_16m_reg);
clock_generator.write(si5351a_ms_2_mcu_10m_reg);
clock_generator.write(si5351a_ms6_7_off_reg);
} else {
clock_generator.write(si5351_pll_a_xtal_reg);
clock_generator.write(si5351c_pll_b_clkin_reg);
clock_generator.write(si5351c_ms_0_8m_reg);
clock_generator.write(si5351c_ms_1_group_reg);
clock_generator.write(si5351c_ms_2_group_reg);
clock_generator.write(si5351c_ms_3_10m_reg);
clock_generator.write(si5351c_ms_4_reg);
clock_generator.write(si5351c_ms_5_reg);
clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg);
}
clock_generator.reset_plls();
// Wait for both PLLs to lock.
// TODO: Disable the unused PLL?
const uint8_t device_status_mask = (ref_pll == ClockControl::MultiSynthSource::PLLB) ? 0x40 : 0x20;
// Wait for PLL(s) to lock.
uint8_t device_status_mask = hackrf_r9
? 0x20
: (ref_pll == ClockControl::MultiSynthSource::PLLB)
? 0x40
: 0x20;
while((clock_generator.device_status() & device_status_mask) != 0);
clock_generator.set_clock_control(
@@ -268,12 +331,18 @@ uint32_t ClockManager::measure_gp_clkin_frequency() {
return get_frequency_monitor_measurement_in_hertz();
}
bool ClockManager::loss_of_signal() {
return hackrf_r9
? clock_generator.plla_loss_of_signal()
: clock_generator.clkin_loss_of_signal();
}
ClockManager::ReferenceSource ClockManager::detect_reference_source() {
if( clock_generator.clkin_loss_of_signal() ) {
if(loss_of_signal()) {
// No external reference. Turn on PortaPack reference (if present).
portapack_tcxo_enable();
if( clock_generator.clkin_loss_of_signal() ) {
if(loss_of_signal()) {
// No PortaPack reference was detected. Choose the HackRF crystal as the reference.
return ReferenceSource::Xtal;
} else {
@@ -285,6 +354,11 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() {
}
ClockManager::Reference ClockManager::choose_reference() {
if (hackrf_r9) {
gpio_r9_clkin_en.write(1);
volatile uint32_t delay = 240000 + 24000;
while(delay--);
}
const auto detected_reference = detect_reference_source();
if( (detected_reference == ReferenceSource::External) ||
@@ -296,8 +370,12 @@ ClockManager::Reference ClockManager::choose_reference() {
}
}
if (hackrf_r9) {
gpio_r9_clkin_en.write(0);
}
portapack_tcxo_disable();
return { ReferenceSource::Xtal, 10000000 };
return { ReferenceSource::Xtal, 25000000 };
}
void ClockManager::shutdown() {
@@ -305,18 +383,26 @@ void ClockManager::shutdown() {
}
void ClockManager::enable_codec_clocks() {
clock_generator.enable_clock(clock_generator_output_codec);
clock_generator.enable_clock(clock_generator_output_cpld);
clock_generator.enable_clock(clock_generator_output_sgpio);
if (hackrf_r9) {
clock_generator.enable_clock(clock_generator_output_r9_sgpio);
} else {
clock_generator.enable_clock(clock_generator_output_og_codec);
clock_generator.enable_clock(clock_generator_output_og_cpld);
clock_generator.enable_clock(clock_generator_output_og_sgpio);
}
/* Turn on all outputs at the same time. This probably doesn't ensure
* their phase relationships. For example, clocks that output frequencies
* in a 2:1 relationship may start with the slower clock high or low?
*/
clock_generator.enable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
if (hackrf_r9) {
clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio);
} else {
clock_generator.enable_output_mask(
(1U << clock_generator_output_og_codec)
| (1U << clock_generator_output_og_cpld)
| (1U << clock_generator_output_og_sgpio)
);
}
}
void ClockManager::disable_codec_clocks() {
@@ -324,34 +410,45 @@ void ClockManager::disable_codec_clocks() {
* be enabled for the output to come to rest at the state specified by
* CLKx_DISABLE_STATE.
*/
clock_generator.disable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
clock_generator.disable_clock(clock_generator_output_codec);
clock_generator.disable_clock(clock_generator_output_cpld);
clock_generator.disable_clock(clock_generator_output_sgpio);
if (hackrf_r9) {
clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio);
clock_generator.disable_clock(clock_generator_output_r9_sgpio);
} else {
clock_generator.disable_output_mask(
(1U << clock_generator_output_og_codec)
| (1U << clock_generator_output_og_cpld)
| (1U << clock_generator_output_og_sgpio)
);
clock_generator.disable_clock(clock_generator_output_og_codec);
clock_generator.disable_clock(clock_generator_output_og_cpld);
clock_generator.disable_clock(clock_generator_output_og_sgpio);
}
}
void ClockManager::enable_first_if_clock() {
clock_generator.enable_clock(clock_generator_output_first_if);
clock_generator.enable_output_mask(1U << clock_generator_output_first_if);
void ClockManager::enable_if_clocks() {
if (hackrf_r9) {
clock_generator.enable_clock(clock_generator_output_r9_if);
clock_generator.enable_output_mask(1U << clock_generator_output_r9_if);
} else {
clock_generator.enable_clock(clock_generator_output_og_first_if);
clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if);
clock_generator.enable_clock(clock_generator_output_og_second_if);
clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if);
}
}
void ClockManager::disable_first_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_first_if);
clock_generator.disable_clock(clock_generator_output_first_if);
}
void ClockManager::enable_second_if_clock() {
clock_generator.enable_clock(clock_generator_output_second_if);
clock_generator.enable_output_mask(1U << clock_generator_output_second_if);
}
void ClockManager::disable_second_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_second_if);
clock_generator.disable_clock(clock_generator_output_second_if);
void ClockManager::disable_if_clocks() {
if (hackrf_r9) {
clock_generator.disable_output_mask(1U << clock_generator_output_r9_if);
clock_generator.disable_clock(clock_generator_output_r9_if);
} else {
clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if);
clock_generator.disable_clock(clock_generator_output_og_first_if);
clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if);
clock_generator.disable_clock(clock_generator_output_og_second_if);
}
}
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
@@ -360,14 +457,21 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) {
* necessary to change the MS0 synth frequency, and ensure the output
* is divided by two.
*/
clock_generator.set_ms_frequency(clock_generator_output_codec, frequency * 2, si5351_vco_f, 1);
if (hackrf_r9) {
clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0);
} else {
clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1);
}
}
void ClockManager::set_reference_ppb(const int32_t ppb) {
/* NOTE: This adjustment only affects PLLA, which is derived from the 25MHz crystal.
* It is assumed an external clock coming in to PLLB is sufficiently accurate as to not need adjustment.
/* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal.
* It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment.
* TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too.
*/
if (hackrf_r9 && reference.source != ReferenceSource::Xtal) {
return;
}
constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a;
constexpr uint32_t denominator = 1000000 / pll_multiplier;
const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1);
@@ -425,11 +529,13 @@ void ClockManager::start_audio_pll() {
/* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK:
* Fout=12.288MHz, Fcco=491.52MHz
* PSEL=20, NSEL=125, MSEL=768
* OG: PSEL=20, NSEL=125, MSEL=768
* PDEC=31, NDEC=45, MDEC=30542
* r9: PSEL=20, NSEL=125, MSEL=3072
* PDEC=31, NDEC=45, MDEC=8308
*/
cgu::pll0audio::mdiv({
.mdec = 30542,
.mdec = hackrf_r9 ? 8308UL : 30542UL,
});
cgu::pll0audio::np_div({
.pdec = 31,
@@ -467,18 +573,25 @@ void ClockManager::stop_audio_pll() {
void ClockManager::enable_clock_output(bool enable) {
if(enable) {
clock_generator.enable_output(clock_generator_output_clkout);
clock_generator.enable_output(clock_generator_output_og_clkout);
if(portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
} else {
clock_generator.disable_output(clock_generator_output_clkout);
clock_generator.disable_output(clock_generator_output_og_clkout);
}
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
if(enable)
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
else
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
}
+3 -5
View File
@@ -65,11 +65,8 @@ public:
void enable_codec_clocks();
void disable_codec_clocks();
void enable_first_if_clock();
void disable_first_if_clock();
void enable_second_if_clock();
void disable_second_if_clock();
void enable_if_clocks();
void disable_if_clocks();
void set_sampling_frequency(const uint32_t frequency);
@@ -99,6 +96,7 @@ private:
ReferenceSource detect_reference_source();
Reference choose_reference();
bool loss_of_signal();
};
#endif/*__CLOCK_MANAGER_H__*/
+15 -13
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
@@ -28,30 +29,31 @@ using namespace lpc43xx;
#include "message.hpp"
#include "baseband_api.hpp"
#include "lz4.h"
#include <cstring>
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
* a different image? Other than asking the old image to sleep while the M0
* makes changes?
*
* I suppose I could force M4MEMMAP to an invalid memory reason which would
* cause an exception and effectively halt the M4. But that feels gross.
*/
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to) {
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset) {
const portapack::spi_flash::chunk_t* chunk = reinterpret_cast<const portapack::spi_flash::chunk_t*>(portapack::spi_flash::images.base());
while(chunk->tag) {
if( chunk->tag == image_tag ) {
/* Initialize M4 code RAM */
std::memcpy(reinterpret_cast<void*>(to.base()), &chunk->data[0], chunk->length);
if(chunk->tag == image_tag) {
const void *src = &chunk->data[0];
void *dst = reinterpret_cast<void*>(to.base());
/* extract and initialize M4 code RAM */
unlz4_len(src, dst, chunk->compressed_data_size);
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
* with its address space and RAM without concern.
*/
LPC_CREG->M4MEMMAP = to.base();
/* Reset M4 core */
LPC_RGU->RESET_CTRL[0] = (1 << 13);
/* Reset M4 core and optionally all peripherals */
LPC_RGU->RESET_CTRL[0] = (full_reset) ?
(1 << 1) // PERIPH_RST
: (1 << 13) // M4_RST
;
return;
}
+1 -1
View File
@@ -27,7 +27,7 @@
#include "memory_map.hpp"
#include "spi_image.hpp"
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to);
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
void m4_request_shutdown();
void m0_halt();
+203
View File
@@ -0,0 +1,203 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* 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 "debug.hpp"
#include <ch.h>
#include <hal.h>
#include <string>
#include "ui_painter.hpp"
#include "portapack.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "string_format.hpp"
void runtime_error(LED);
std::string number_to_hex_string(uint32_t);
void draw_line(int32_t, const char *, regarm_t);
static bool error_shown = false;
void draw_guru_meditation_header(uint8_t source, const char *hint) {
ui::Painter painter;
ui::Style style_default {
.font = ui::font::fixed_8x16,
.background = ui::Color::black(),
.foreground = ui::Color::white()
};
painter.fill_rectangle(
{ 0, 0, portapack::display.width(), portapack::display.height() },
ui::Color::red()
);
constexpr int border = 8;
painter.fill_rectangle(
{ border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2) },
ui::Color::black()
);
// NOTE: in situations like a hard fault it seems not possible to write strings longer than 16 characters.
painter.draw_string({ 48, 24 }, style_default, "M? Guru");
painter.draw_string({ 48 + 8*8, 24 }, style_default, "Meditation");
if (source == CORTEX_M0)
painter.draw_string({ 48 + 8, 24 }, style_default, "0");
if (source == CORTEX_M4)
painter.draw_string({ 48 + 8, 24 }, style_default, "4");
painter.draw_string({ 15, 55 }, style_default, "Hint: ");
painter.draw_string({ 15 + 8*8, 55 }, style_default, hint);
}
void draw_guru_meditation(uint8_t source, const char *hint) {
if(error_shown == false){
error_shown = true;
draw_guru_meditation_header(source, hint);
}
if (source == CORTEX_M0)
runtime_error(hackrf::one::led_rx);
if (source == CORTEX_M4)
runtime_error(hackrf::one::led_tx);
}
void draw_guru_meditation(uint8_t source, const char *hint, struct extctx *ctxp, uint32_t cfsr = 0) {
if(error_shown == false)
{
error_shown = true;
draw_guru_meditation_header(source, hint);
if (ctxp != nullptr) {
int i = 0;
draw_line(80 + i++ * 20, "r0:", ctxp->r0);
draw_line(80 + i++ * 20, "r1:", ctxp->r1);
draw_line(80 + i++ * 20, "r2:", ctxp->r2);
draw_line(80 + i++ * 20, "r3:", ctxp->r3);
draw_line(80 + i++ * 20, "r12:", ctxp->r12);
// NOTE: run for M0
// # arm-none-eabi-gdb --batch --eval-command="x/8i 0x<link_register_value_here>" firmware/application/application.elf
// or for M4
// # arm-none-eabi-gdb --batch --eval-command="x/8i 0x<link_register_value_here>" firmware/baseband/<image_name_here>.elf
// to see whats causing the fault.
draw_line(80 + i++ * 20, "lr:", ctxp->lr_thd);
draw_line(80 + i++ * 20, "pc:", ctxp->pc);
// see SCB_CFSR_* in libopencm3/cm3/scb.h for details
if (cfsr != 0)
draw_line(80 + i++ * 20, "cfsr:", (void *)cfsr);
}
}
if (source == CORTEX_M0)
runtime_error(hackrf::one::led_rx);
if (source == CORTEX_M4)
runtime_error(hackrf::one::led_tx);
}
void draw_line(int32_t y_offset, const char *label, regarm_t value){
ui::Painter painter;
ui::Style style_default {
.font = ui::font::fixed_8x16,
.background = ui::Color::black(),
.foreground = ui::Color::white()
};
painter.draw_string({ 15, y_offset }, style_default, label);
painter.draw_string({ 15 + 8*8, y_offset }, style_default, to_string_hex((uint32_t)value, 8));
}
void runtime_error(LED led) {
led.off();
while(true) {
volatile size_t n = 1000000U;
while(n--);
led.toggle();
}
}
extern "C" {
#if CH_DBG_ENABLED
void port_halt(void) {
port_disable();
if (dbg_panic_msg == nullptr)
dbg_panic_msg = "system halted";
draw_guru_meditation(CORTEX_M0, dbg_panic_msg);
}
#endif
CH_IRQ_HANDLER(MemManageVector) {
#if CH_DBG_ENABLED
chDbgPanic("MemManage");
#else
chSysHalt();
#endif
}
CH_IRQ_HANDLER(BusFaultVector) {
#if CH_DBG_ENABLED
chDbgPanic("BusFault");
#else
chSysHalt();
#endif
}
CH_IRQ_HANDLER(UsageFaultVector) {
#if CH_DBG_ENABLED
chDbgPanic("UsageFault");
#else
chSysHalt();
#endif
}
CH_IRQ_HANDLER(HardFaultVector) {
#if CH_DBG_ENABLED
CH_IRQ_PROLOGUE();
struct extctx *ctxp;
port_lock_from_isr();
if ((uint32_t)_saved_lr & 0x04)
ctxp = (struct extctx *)__get_PSP();
else
ctxp = (struct extctx *)__get_MSP();
port_disable();
auto stack_space_left = get_free_stack_space();
if (stack_space_left < 16)
draw_guru_meditation(CORTEX_M0, "Stack Overflow", ctxp);
draw_guru_meditation(CORTEX_M0, "Hard Fault", ctxp);
CH_IRQ_EPILOGUE();
#else
chSysHalt();
#endif
}
} /* extern "C" */
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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.
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include "hackrf_gpio.hpp"
extern void draw_guru_meditation(uint8_t, const char *);
extern void draw_guru_meditation(uint8_t, const char *, struct extctx *, uint32_t);
extern uint32_t __process_stack_base__;
extern uint32_t __process_stack_end__;
#define CRT0_STACKS_FILL_PATTERN 0x55555555
inline uint32_t get_free_stack_space(){
uint32_t *p;
for (p = &__process_stack_base__; *p == CRT0_STACKS_FILL_PATTERN && p < &__process_stack_end__; p++);
auto stack_space_left = p - &__process_stack_base__;
return stack_space_left;
}
#endif/*__DEBUG_H__*/
+18 -32
View File
@@ -23,6 +23,7 @@
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "debug.hpp"
#include "sd_card.hpp"
#include "rtc_time.hpp"
@@ -42,6 +43,7 @@ using namespace lpc43xx;
#include <array>
#include "ui_font_fixed_8x16.hpp"
#include "ui_navigation.hpp"
extern "C" {
@@ -141,37 +143,14 @@ eventmask_t EventDispatcher::wait() {
void EventDispatcher::dispatch(const eventmask_t events) {
if( shared_memory.m4_panic_msg[0] != 0 ) {
halt = true;
}
if( halt ) {
if( shared_memory.m4_panic_msg[0] != 0 ) {
painter.fill_rectangle(
{ 0, 0, portapack::display.width(), portapack::display.height() },
ui::Color::red()
);
constexpr int border = 8;
painter.fill_rectangle(
{ border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2) },
ui::Color::black()
);
painter.draw_string({ 48, 24 }, top_widget->style(), "M4 Guru Meditation");
shared_memory.m4_panic_msg[sizeof(shared_memory.m4_panic_msg) - 1] = 0;
const std::string message = shared_memory.m4_panic_msg;
const int x_offset = (portapack::display.width() - (message.size() * 8)) / 2;
constexpr int y_offset = (portapack::display.height() - 16) / 2;
painter.draw_string(
{ x_offset, y_offset },
top_widget->style(),
message
);
shared_memory.m4_panic_msg[0] = 0;
}
return;
if (shared_memory.bb_data.data[0] == 0)
draw_guru_meditation(CORTEX_M4, shared_memory.m4_panic_msg);
else
draw_guru_meditation(
CORTEX_M4,
shared_memory.m4_panic_msg,
(struct extctx *)&shared_memory.bb_data.data[8],
*(uint32_t *)&shared_memory.bb_data.data[4]);
}
if( events & EVT_MASK_APPLICATION ) {
@@ -284,6 +263,8 @@ void EventDispatcher::on_touch_event(ui::TouchEvent event) {
void EventDispatcher::handle_lcd_frame_sync() {
DisplayFrameSyncMessage message;
message_map.send(&message);
static_cast<ui::SystemView *>(top_widget)->paint_overlay();
painter.paint_widget_tree(top_widget);
portapack::backlight()->on();
@@ -326,7 +307,12 @@ void EventDispatcher::handle_switches() {
if( switches_state[i] ) {
const auto event = static_cast<ui::KeyEvent>(i);
if( !event_bubble_key(event) ) {
context.focus_manager().update(top_widget, event);
if (switches_state[(size_t)ui::KeyEvent::Dfu]) {
static_cast<ui::SystemView *>(top_widget)->toggle_overlay();
}
else {
context.focus_manager().update(top_widget, event);
}
}
in_key_event = true;
-1
View File
@@ -98,7 +98,6 @@ private:
static bool is_running;
bool sd_card_present = false;
static bool display_sleep;
bool halt = false;
bool in_key_event = false;
eventmask_t wait();
+44 -1
View File
@@ -250,6 +250,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 +305,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,6 +321,18 @@ 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
@@ -311,7 +340,7 @@ directory_iterator::directory_iterator(
{
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 ) {
if( result != FR_OK || impl->filinfo.fname[0] == (TCHAR)'\0') {
impl.reset();
// TODO: Throw exception if/when I enable exceptions...
}
@@ -333,6 +362,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;
+16
View File
@@ -123,6 +123,7 @@ struct path {
return *this;
}
path parent_path() const;
path extension() const;
path filename() const;
path stem() const;
@@ -151,14 +152,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 +239,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);
@@ -245,6 +259,8 @@ uint32_t make_new_directory(const std::filesystem::path& dir_path);
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension);
std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory);
/* Gets an auto incrementing filename. */
std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern);
/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */
+13
View File
@@ -0,0 +1,13 @@
#include "../../hackrf/firmware/common/firmware_info.h"
#include "../../hackrf/firmware/common/platform_detect.h"
#define SUPPORTED_PLATFORM (PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
#define DFU_MODE_VALUE 0
__attribute__((section(".firmware_info"))) const struct firmware_info_t firmware_info = {
.magic = "HACKRFFW",
.struct_version = 1,
.dfu_mode = DFU_MODE_VALUE,
.supported_platform = SUPPORTED_PLATFORM,
.version_string = VERSION_STRING,
};
+5 -1
View File
@@ -51,6 +51,8 @@ options_t freqman_entry_bandwidths[ 4 ] = {
};
options_t freqman_entry_steps = {
{ "0.1KHz " , 100 },
{ "1KHz " , 1000 },
{ "5KHz (SA AM)" , 5000 },
{ "6.25KHz(NFM)" , 6250 },
{ "8.33KHz(AIR)" , 8330 },
@@ -68,6 +70,8 @@ options_t freqman_entry_steps = {
};
options_t freqman_entry_steps_short = {
{ "0.1KHz" , 100 },
{ "1KHz" , 1000 },
{ "5KHz" , 5000 },
{ "6.25KHz" , 6250 },
{ "8.33KHz" , 8330 },
@@ -225,7 +229,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;
+4 -3
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;
+35 -5
View File
@@ -32,8 +32,12 @@ using namespace hackrf::one;
namespace max2837 {
using namespace max283x;
namespace lna {
using namespace max283x::lna;
constexpr std::array<uint8_t, 8> lookup_8db_steps {
0b111, 0b011, 0b110, 0b010,
0b100, 0b000, 0b000, 0b000
@@ -48,6 +52,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace vga {
using namespace max283x::vga;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
@@ -57,6 +63,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace tx {
using namespace max283x::tx;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
uint8_t value = db_sat & 0x0f;
@@ -69,6 +77,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace filter {
using namespace max283x::filter;
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
/* Determine filter setting that will provide bandwidth greater than or
* equal to requested bandwidth.
@@ -84,13 +94,13 @@ static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
constexpr uint32_t reference_frequency = max2837_reference_f;
constexpr uint32_t reference_frequency = max283x_reference_f;
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
void MAX2837::init() {
set_mode(Mode::Shutdown);
gpio_max2837_enable.output();
gpio_max283x_enable.output();
gpio_max2837_rxenable.output();
gpio_max2837_txenable.output();
@@ -140,10 +150,30 @@ void MAX2837::init() {
set_mode(Mode::Standby);
}
enum class Mask {
Enable = 0b001,
RxEnable = 0b010,
TxEnable = 0b100,
Shutdown = 0b000,
Standby = Enable,
Receive = Enable | RxEnable,
Transmit = Enable | TxEnable,
};
Mask mode_mask(const Mode mode) {
switch (mode) {
case Mode::Standby: return Mask::Standby;
case Mode::Receive: return Mask::Receive;
case Mode::Transmit: return Mask::Transmit;
default: return Mask::Shutdown;
}
}
void MAX2837::set_mode(const Mode mode) {
gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
Mask mask = mode_mask(mode);
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
}
void MAX2837::flush() {
+14 -101
View File
@@ -22,6 +22,7 @@
#ifndef __MAX2837_H__
#define __MAX2837_H__
#include "max283x.hpp"
#include "gpio.hpp"
#include "spi_arbiter.hpp"
@@ -29,99 +30,11 @@
#include <array>
#include "dirty_registers.hpp"
#include "rf_path.hpp"
#include "utility.hpp"
namespace max2837 {
enum class Mode {
Mask_Enable = 0b001,
Mask_RxEnable = 0b010,
Mask_TxEnable = 0b100,
Shutdown = 0b000,
Standby = Mask_Enable,
Receive = Mask_Enable | Mask_RxEnable,
Transmit = Mask_Enable | Mask_TxEnable,
};
/*************************************************************************/
namespace lo {
constexpr std::array<rf::FrequencyRange, 4> band { {
{ 2300000000, 2400000000 },
{ 2400000000, 2500000000 },
{ 2500000000, 2600000000 },
{ 2600000000, 2700000000 },
} };
} /* namespace lo */
/*************************************************************************/
namespace lna {
constexpr range_t<int8_t> gain_db_range { 0, 40 };
constexpr int8_t gain_db_step = 8;
constexpr std::array<rf::FrequencyRange, 2> band { {
{ 2300000000, 2500000000 },
{ 2500000000, 2700000000 },
} };
} /* namespace lna */
/*************************************************************************/
namespace vga {
constexpr range_t<int8_t> gain_db_range { 0, 62 };
constexpr int8_t gain_db_step = 2;
} /* namespace vga */
/*************************************************************************/
namespace tx {
constexpr range_t<int8_t> gain_db_range { 0, 47 };
constexpr int8_t gain_db_step = 1;
}
/*************************************************************************/
namespace filter {
constexpr std::array<uint32_t, 16> bandwidths {
/* Assumption: these values are in ascending order */
1750000,
2500000, /* Some documentation says 2.25MHz */
3500000,
5000000,
5500000,
6000000,
7000000,
8000000,
9000000,
10000000,
12000000,
14000000,
15000000,
20000000,
24000000,
28000000,
};
constexpr auto bandwidth_minimum = bandwidths[0];
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
} /* namespace filter */
/*************************************************************************/
using reg_t = uint16_t;
using address_t = uint8_t;
using namespace max283x;
constexpr size_t reg_count = 32;
@@ -826,7 +739,7 @@ constexpr RegisterMap initial_register_values { Register_Type {
},
} };
class MAX2837 {
class MAX2837 : public MAX283x {
public:
constexpr MAX2837(
spi::arbiter::Target& target
@@ -834,13 +747,13 @@ public:
{
}
void init();
void set_mode(const Mode mode);
void init() override;
void set_mode(const Mode mode) override;
void set_tx_vga_gain(const int_fast8_t db);
void set_lna_gain(const int_fast8_t db);
void set_vga_gain(const int_fast8_t db);
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
void set_tx_vga_gain(const int_fast8_t db) override;
void set_lna_gain(const int_fast8_t db) override;
void set_vga_gain(const int_fast8_t db) override;
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
#if 0
void rx_cal() {
_map.r.spi_en.EN_SPI = 1;
@@ -882,16 +795,16 @@ public:
}
#endif
bool set_frequency(const rf::Frequency lo_frequency);
bool set_frequency(const rf::Frequency lo_frequency) override;
void set_rx_lo_iq_calibration(const size_t v);
void set_rx_lo_iq_calibration(const size_t v) override;
void set_rx_bias_trim(const size_t v);
void set_vco_bias(const size_t v);
void set_rx_buff_vcm(const size_t v);
void set_rx_buff_vcm(const size_t v) override;
reg_t temp_sense();
reg_t temp_sense() override;
reg_t read(const address_t reg_num);
reg_t read(const address_t reg_num) override;
private:
spi::arbiter::Target& _target;
+354
View File
@@ -0,0 +1,354 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2023 Great Scott Gadgets
*
* 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 "max2839.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "ch.h"
#include "hal.h"
#include <algorithm>
namespace max2839 {
namespace lna {
using namespace max283x::lna;
constexpr std::array<uint8_t, 8> lookup_8db_steps {
0b11, 0b11, 0b10, 0b10,
0b01, 0b00, 0b00, 0b00
};
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
}
} /* namespace lna */
namespace vga {
using namespace max283x::vga;
constexpr range_t<int8_t> gain_db_range_internal { 0, 63 };
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range_internal.clip(db);
return (db_sat & 0b111111) ^ 0b111111;
}
} /* namespace vga */
namespace tx {
using namespace max283x::tx;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return 47 - db_sat;
}
} /* namespace tx */
namespace filter {
using namespace max283x::filter;
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
/* Determine filter setting that will provide bandwidth greater than or
* equal to requested bandwidth.
*/
return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
}
} /* namespace filter */
/* Empirical testing indicates about 25us is necessary to get a valid
* temperature sense conversion from the ADC.
*/
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
constexpr uint32_t reference_frequency = max283x_reference_f;
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
static int_fast8_t requested_rx_lna_gain = 0;
static int_fast8_t requested_rx_vga_gain = 0;
void MAX2839::init() {
set_mode(Mode::Shutdown);
gpio_max283x_enable.output();
gpio_max2839_rxtx.output();
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
_map.r.lpf_vga_1.L = 0b000;
_map.r.lpf_vga_2.L = 0b000;
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
_map.r.lpf_vga_1.VGA = 0b000000;
_map.r.lpf_vga_2.VGA = 0b010101;
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
_map.r.lo_gen.LOGEN_2GM = 0;
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
/*
* There are two LNA band settings, but we only use one of them.
* Switching to the other one doesn't make the overall spectrum any
* flatter but adds a surprise step in the middle.
*/
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
_dirty.set();
flush();
set_mode(Mode::Standby);
}
enum class Mask {
Enable = 0b01,
RxTx = 0b10,
Shutdown = 0b00,
Standby = RxTx,
Receive = Enable | RxTx,
Transmit = Enable,
};
Mask mode_mask(const Mode mode) {
switch (mode) {
case Mode::Standby: return Mask::Standby;
case Mode::Receive: return Mask::Receive;
case Mode::Transmit: return Mask::Transmit;
default: return Mask::Shutdown;
}
}
void MAX2839::set_mode(const Mode mode) {
Mask mask = mode_mask(mode);
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
}
void MAX2839::flush() {
if( _dirty ) {
for(size_t n=0; n<reg_count; n++) {
if( _dirty[n] ) {
write(n, _map.w[n]);
}
}
_dirty.clear();
}
}
void MAX2839::flush_one(const Register reg) {
const auto reg_num = toUType(reg);
write(reg_num, _map.w[reg_num]);
_dirty.clear(reg_num);
}
void MAX2839::write(const address_t reg_num, const reg_t value) {
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
_target.transfer(&t, 1);
}
reg_t MAX2839::read(const address_t reg_num) {
uint16_t t = (1U << 15) | (reg_num << 10);
_target.transfer(&t, 1U);
return t & 0x3ffU;
}
void MAX2839::write(const Register reg, const reg_t value) {
write(toUType(reg), value);
}
reg_t MAX2839::read(const Register reg) {
return read(toUType(reg));
}
void MAX2839::set_tx_vga_gain(const int_fast8_t db) {
_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
_dirty[Register::TX_GAIN] = 1;
flush();
}
/*
* MAX2839 gain rain ranges differ slightly from MAX2837's but are close
* enough that it makes sense to emulate MAX2837 gain ranges for a consistent
* user experience.
*/
void MAX2839::configure_rx_gain() {
/* Apply MAX2837 restrictions to requested gain settings. */
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
lna_gain &= 0x38;
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
vga_gain &= 0x3e;
/*
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
* adjust the VGA (baseband) gain to compensate.
*/
vga_gain += 3;
/*
* If that adjustment puts VGA gain out of range, use LNA gain to
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
*/
if (vga_gain > 63) {
if (lna_gain <= 32) {
vga_gain -= 8;
lna_gain += 8;
} else {
vga_gain = 63;
}
}
/*
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
* settings, so we use VGA gain to compensate.
*/
if (lna_gain == 0) {
lna_gain = 8;
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
}
if (lna_gain == 16) {
if (vga_gain > 32) {
vga_gain -= 8;
lna_gain += 8;
} else {
vga_gain += 8;
lna_gain -= 8;
}
}
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
_dirty[Register::RXRF_2] = 1;
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
_dirty[Register::LPF_VGA_2] = 1;
flush();
}
void MAX2839::set_lna_gain(const int_fast8_t db) {
requested_rx_lna_gain = db;
configure_rx_gain();
}
void MAX2839::set_vga_gain(const int_fast8_t db) {
requested_rx_vga_gain = db;
configure_rx_gain();
}
void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
_dirty[Register::LPF] = 1;
flush();
}
bool MAX2839::set_frequency(const rf::Frequency lo_frequency) {
/* TODO: This is a sad implementation. Refactor. */
if( lo::band[0].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */
} else if( lo::band[1].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
} else if( lo::band[2].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
} else if( lo::band[3].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
} else {
return false;
}
_dirty[Register::SYN_INT_DIV] = 1;
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
_dirty[Register::SYN_INT_DIV] = 1;
_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
_dirty[Register::SYN_FR_DIV_2] = 1;
/* flush to commit high FRDIV first, as low FRDIV commits the change */
flush();
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
_dirty[Register::SYN_FR_DIV_1] = 1;
flush();
return true;
}
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
_dirty[Register::RXRF_2] = 1;
_map.r.rxrf_1.iqerr_trim = v;
_dirty[Register::RXRF_1] = 1;
flush();
}
void MAX2839::set_rx_buff_vcm(const size_t v) {
_map.r.lpf_vga_2.BUFF_VCM = v;
_dirty[Register::LPF_VGA_2] = 1;
flush();
}
reg_t MAX2839::temp_sense() {
if( !_map.r.rx_top_2.ts_en ) {
_map.r.rx_top_2.ts_en = 1;
flush_one(Register::RX_TOP_2);
chThdSleepMilliseconds(1);
}
_map.r.rx_top_2.ts_adc_trigger = 1;
flush_one(Register::RX_TOP_2);
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
/*
* Things look very similar to MAX2837, so this probably works, but the
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
*/
const auto value = read(Register::TEMP_SENSE);
_map.r.rx_top_2.ts_adc_trigger = 0;
flush_one(Register::RX_TOP_2);
return value;
}
}
+688
View File
@@ -0,0 +1,688 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2023 Great Scott Gadgets
*
* 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.
*/
#ifndef __MAX2839_H__
#define __MAX2839_H__
#include "max283x.hpp"
#include "gpio.hpp"
#include "spi_arbiter.hpp"
#include <cstdint>
#include <array>
#include "dirty_registers.hpp"
#include "utility.hpp"
namespace max2839 {
using namespace max283x;
constexpr size_t reg_count = 32;
enum class Register : address_t {
RXENABLE = 0,
RXRF_1 = 1,
RXRF_2 = 2,
RXRF_LPF = 3,
LPF = 4,
LPF_VGA_1 = 5,
LPF_VGA_2 = 6,
RSSI_VGA = 7,
RX_TOP_1 = 8,
RX_TOP_2 = 9,
TX_TOP_1 = 10,
TEMP_SENSE = 11,
HPFSM_1 = 12,
HPFSM_2 = 13,
HPFSM_3 = 14,
HPFSM_4 = 15,
SPI_EN = 16,
SYN_FR_DIV_1 = 17,
SYN_FR_DIV_2 = 18,
SYN_INT_DIV = 19,
SYN_CFG_1 = 20,
SYN_CFG_2 = 21,
VAS_CFG = 22,
LO_MISC = 23,
XTAL_CFG = 24,
VCO_CFG = 25,
LO_GEN = 26,
PA_DRV = 27,
PA_DAC = 28,
TX_GAIN = 29,
TX_LO_IQ = 30,
TX_DC_CORR = 31,
};
struct RXENABLE_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size");
struct RXRF_1_Type {
reg_t LNAband : 1;
reg_t RESERVED0 : 1;
reg_t MIMOmode : 1;
reg_t iqerr_trim : 5;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
struct RXRF_2_Type {
reg_t LNAgain_SPI_EN : 1;
reg_t RESERVED0 : 1;
reg_t RX_IQERR_SPI_EN : 1;
reg_t RESERVED1 : 7;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
struct RXRF_LPF_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXRF_LPF_Type) == sizeof(reg_t), "RXRF_LPF_Type wrong size");
struct LPF_Type {
reg_t RESERVED0 : 2;
reg_t dF : 2;
reg_t RESERVED1 : 2;
reg_t FT : 4;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(LPF_Type) == sizeof(reg_t), "LPF_Type wrong size");
struct LPF_VGA_1_Type {
reg_t L : 2;
reg_t VGA : 6;
reg_t ModeCtrl : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_VGA_1_Type) == sizeof(reg_t), "LPF_VGA_1_Type wrong size");
struct LPF_VGA_2_Type {
reg_t L : 2;
reg_t VGA : 6;
reg_t BUFF_VCM : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_VGA_2_Type) == sizeof(reg_t), "LPF_VGA_2_Type wrong size");
struct RSSI_VGA_Type {
reg_t RESERVED0 : 1;
reg_t RSSI_MUX : 1;
reg_t RSSI_MODE : 1;
reg_t RESERVED1 : 4;
reg_t RXBB_OUT_SEL : 1;
reg_t RESERVED2 : 1;
reg_t RSSI_INPUT : 1;
reg_t RESERVED3 : 6;
};
static_assert(sizeof(RSSI_VGA_Type) == sizeof(reg_t), "RSSI_VGA_Type wrong size");
struct RX_TOP_1_Type {
reg_t RESERVED0 : 1;
reg_t VGAgain_SPI_EN : 1;
reg_t LPF_MODE_SEL : 1;
reg_t RESERVED1 : 7;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RX_TOP_1_Type) == sizeof(reg_t), "RX_TOP_1_Type wrong size");
struct RX_TOP_2_Type {
reg_t ts_adc_trigger : 1;
reg_t ts_en : 1;
reg_t RESERVED0 : 1;
reg_t DOUT_DRVH : 1;
reg_t DOUT_CSB_SEL : 1;
reg_t DOUT_SEL : 3;
reg_t RESERVED1 : 2;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RX_TOP_2_Type) == sizeof(reg_t), "RX_TOP_2_Type wrong size");
struct TX_TOP_1_Type {
reg_t TXCAL_GAIN : 2;
reg_t TXCAL_V2I_FILT : 3;
reg_t RESERVED1 : 5;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
struct TEMP_SENSE_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
struct HPFSM_1_Type {
reg_t HPC_10M : 2;
reg_t HPC_10M_GAIN : 2;
reg_t HPC_600k : 3;
reg_t HPC_600k_GAIN : 3;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
struct HPFSM_2_Type {
reg_t HPC_100k : 2;
reg_t HPC_100k_GAIN : 2;
reg_t HPC_30k : 2;
reg_t HPC_30k_GAIN : 2;
reg_t HPC_1k : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
struct HPFSM_3_Type {
reg_t HPC_1k_B7B6 : 2;
reg_t HPC_DELAY : 2;
reg_t HPC_STOP : 2;
reg_t HPC_STOP_M2 : 2;
reg_t HPC_RXGAIN_EN : 1;
reg_t TXGATE_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
struct HPFSM_4_Type {
reg_t HPC_DIVH : 1;
reg_t RESERVED0 : 5;
reg_t HPC_SEQ_BYP : 1;
reg_t RESERVED1 : 2;
reg_t HPC_MODE : 1;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
struct SPI_EN_Type {
reg_t EN_SPI : 1;
reg_t CAL_SPI : 1;
reg_t RESERVED0 : 4;
reg_t PADAC_SPI_EN : 1;
reg_t PADAC_TX_EN : 1;
reg_t RESERVED1 : 2;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
struct SYN_FR_DIV_1_Type {
reg_t SYN_FRDIV_9_0 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
struct SYN_FR_DIV_2_Type {
reg_t SYN_FRDIV_19_10 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
struct SYN_INT_DIV_Type {
reg_t SYN_INTDIV : 8;
reg_t LOGEN_BSW : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
struct SYN_CFG_1_Type {
reg_t RESERVED0 : 1;
reg_t SYN_REF_DIV_RATIO : 2;
reg_t RESERVED1 : 2;
reg_t SYN_CLOCKOUT_DRIVE : 1;
reg_t RESERVED2 : 4;
reg_t RESERVED3 : 6;
};
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
struct SYN_CFG_2_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
struct VAS_CFG_Type {
reg_t VAS_MODE : 1;
reg_t VAS_RELOCK_SEL : 1;
reg_t VAS_DIV : 3;
reg_t VAS_DLY : 2;
reg_t VAS_TRIG_EN : 1;
reg_t RESERVED0 : 2;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
struct LO_MISC_Type {
reg_t VAS_SPI : 5;
reg_t XTAL_BIAS_SEL : 2;
reg_t RESERVED0 : 3;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
struct XTAL_CFG_Type {
reg_t XTAL_FTUNE : 7;
reg_t RESERVED0 : 1;
reg_t XTAL_CLKOUT_DIV : 1;
reg_t XTAL_CORE_EN : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
struct VCO_CFG_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
struct LO_GEN_Type {
reg_t RESERVED0 : 3;
reg_t LOGEN_2GM : 1;
reg_t RESERVED1 : 2;
reg_t VAS_TST : 4;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
struct PA_DRV_Type {
reg_t TXLO_IQ_SPI : 6;
reg_t TXLO_IQ_SPI_EN : 1;
reg_t TXVGA_GAIN_SPI_EN : 1;
reg_t TX_DCCORR_SPI_EN : 1;
reg_t RESERVED0 : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(PA_DRV_Type) == sizeof(reg_t), "PA_DRV_Type wrong size");
struct PA_DAC_Type {
reg_t PADAC_BIAS : 6;
reg_t PADAC_DLY : 4;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
struct TX_GAIN_Type {
reg_t TXVGA_GAIN_SPI : 6;
reg_t RESERVED0 : 4;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
struct TX_LO_IQ_Type {
reg_t TX_DCCORR_I : 6;
reg_t RESERVED0 : 2;
reg_t PADAC_IV : 1;
reg_t PADAC_VMODE : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
struct TX_DC_CORR_Type {
reg_t TX_DCCORR_Q : 6;
reg_t RESERVED0 : 3;
reg_t PADAC_DIVH : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
struct Register_Type {
RXENABLE_Type rxenable; /* 0 */
RXRF_1_Type rxrf_1;
RXRF_2_Type rxrf_2;
RXRF_LPF_Type rxrf_lpf_1;
LPF_Type lpf; /* 4 */
LPF_VGA_1_Type lpf_vga_1;
LPF_VGA_2_Type lpf_vga_2;
RSSI_VGA_Type rssi_vga;
RX_TOP_1_Type rx_top_1; /* 8 */
RX_TOP_2_Type rx_top_2;
TX_TOP_1_Type tx_top_1;
TEMP_SENSE_Type temp_sense;
HPFSM_1_Type hpfsm_1; /* 12 */
HPFSM_2_Type hpfsm_2;
HPFSM_3_Type hpfsm_3;
HPFSM_4_Type hpfsm_4;
SPI_EN_Type spi_en; /* 16 */
SYN_FR_DIV_1_Type syn_fr_div_1;
SYN_FR_DIV_2_Type syn_fr_div_2;
SYN_INT_DIV_Type syn_int_div;
SYN_CFG_1_Type syn_cfg_1; /* 20 */
SYN_CFG_2_Type syn_cfg_2;
VAS_CFG_Type vas_cfg;
LO_MISC_Type lo_misc;
XTAL_CFG_Type xtal_cfg; /* 24 */
VCO_CFG_Type vco_cfg;
LO_GEN_Type lo_gen;
PA_DRV_Type pa_drv;
PA_DAC_Type pa_dac; /* 28 */
TX_GAIN_Type tx_gain;
TX_LO_IQ_Type tx_lo_iq;
TX_DC_CORR_Type tx_dc_corr;
};
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
struct RegisterMap {
constexpr RegisterMap(
Register_Type values
) : r(values)
{
}
union {
Register_Type r;
std::array<reg_t, reg_count> w;
};
};
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
constexpr RegisterMap initial_register_values { Register_Type {
/* settings recommended by MAX2839 data sheet */
.rxenable = { /* 0 */
.RESERVED0 = 0,
.RESERVED1 = 0,
},
.rxrf_1 = { /* 1 */
.LNAband = 0,
.RESERVED0 = 0,
.MIMOmode = 1,
.iqerr_trim = 0b000001,
.RESERVED1 = 0,
},
.rxrf_2 = { /* 2 */
.LNAgain_SPI_EN = 0,
.RESERVED0 = 0,
.RX_IQERR_SPI_EN = 0,
.RESERVED1 = 0b0010000,
.RESERVED2 = 0,
},
.rxrf_lpf_1 = { /* 3 */
.RESERVED0 = 0b0110111001,
.RESERVED1 = 0,
},
.lpf = { /* 4 */
.RESERVED0 = 0b10,
.dF = 0b01,
.RESERVED1 = 0b10,
.FT = 0b1111,
.RESERVED2 = 0,
},
.lpf_vga_1 = { /* 5 */
.L = 0b00,
.VGA = 0b000000,
.ModeCtrl = 0b01,
.RESERVED0 = 0,
},
.lpf_vga_2 = { /* 6 */
.L = 0b00,
.VGA = 0b000000,
.BUFF_VCM = 0b00,
.RESERVED0 = 0,
},
.rssi_vga = { /* 7 */
.RESERVED0 = 0,
.RSSI_MUX = 0,
.RSSI_MODE = 0,
.RESERVED1 = 0b0001,
.RXBB_OUT_SEL = 0,
.RESERVED2 = 0,
.RSSI_INPUT = 1,
.RESERVED3 = 0,
},
.rx_top_1 = { /* 8 */
.RESERVED0 = 0,
.VGAgain_SPI_EN = 0,
.LPF_MODE_SEL = 0,
.RESERVED1 = 0b1000100,
.RESERVED2 = 0,
},
.rx_top_2 = { /* 9 */
.ts_adc_trigger = 0,
.ts_en = 0,
.RESERVED0 = 0,
.DOUT_DRVH = 1,
.DOUT_CSB_SEL = 1,
.DOUT_SEL = 0b000,
.RESERVED1 = 0b00,
.RESERVED2 = 0,
},
.tx_top_1 = { /* 10 */
.TXCAL_GAIN = 0b00,
.TXCAL_V2I_FILT = 0b011,
.RESERVED1 = 0b00000,
.RESERVED2 = 0,
},
.temp_sense = { /* 11 */
.RESERVED0 = 0b0000000100,
.RESERVED1 = 0,
},
.hpfsm_1 = { /* 12 */
.HPC_10M = 0b11,
.HPC_10M_GAIN = 0b11,
.HPC_600k = 0b100,
.HPC_600k_GAIN = 0b100,
.RESERVED0 = 0,
},
.hpfsm_2 = { /* 13 */
.HPC_100k = 0b00,
.HPC_100k_GAIN = 0b00,
.HPC_30k = 0b01,
.HPC_30k_GAIN = 0b01,
.HPC_1k = 0b01,
.RESERVED0 = 0,
},
.hpfsm_3 = { /* 14 */
.HPC_1k_B7B6 = 0b01,
.HPC_DELAY = 0b01,
.HPC_STOP = 0b00,
.HPC_STOP_M2 = 0b11,
.HPC_RXGAIN_EN = 1,
.TXGATE_EN = 1,
.RESERVED0 = 0,
},
.hpfsm_4 = { /* 15 */
.HPC_DIVH = 1,
.RESERVED0 = 0b00000,
.HPC_SEQ_BYP = 0,
.RESERVED1 = 0b00,
.HPC_MODE = 1,
.RESERVED2 = 0,
},
.spi_en = { /* 16 */
.EN_SPI = 0,
.CAL_SPI = 0,
.RESERVED0 = 0b0111,
.PADAC_SPI_EN = 0,
.PADAC_TX_EN = 0,
.RESERVED1 = 0b00,
.RESERVED2 = 0,
},
.syn_fr_div_1 = { /* 17 */
.SYN_FRDIV_9_0 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_fr_div_2 = { /* 18 */
.SYN_FRDIV_19_10 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_int_div = { /* 19 */
.SYN_INTDIV = 0b01010011,
.LOGEN_BSW = 0b01,
.RESERVED0 = 0,
},
.syn_cfg_1 = { /* 20 */
.RESERVED0 = 1,
.SYN_REF_DIV_RATIO = 0b00,
.RESERVED1 = 0b01,
.SYN_CLOCKOUT_DRIVE = 0,
.RESERVED2 = 0b1001,
.RESERVED3 = 0,
},
.syn_cfg_2 = { /* 21 */
.RESERVED0 = 0b0000101101,
.RESERVED1 = 0,
},
.vas_cfg = { /* 22 */
.VAS_MODE = 1,
.VAS_RELOCK_SEL = 0,
.VAS_DIV = 0b010,
.VAS_DLY = 0b01,
.VAS_TRIG_EN = 1,
.RESERVED0 = 0b01,
.RESERVED1 = 0,
},
.lo_misc = { /* 23 */
.VAS_SPI = 0b01111,
.XTAL_BIAS_SEL = 0b10,
.RESERVED0 = 0b100,
.RESERVED1 = 0,
},
.xtal_cfg = { /* 24 */
.XTAL_FTUNE = 0b0000000,
.RESERVED0 = 1,
.XTAL_CLKOUT_DIV = 1,
.XTAL_CORE_EN = 0,
.RESERVED1 = 0,
},
.vco_cfg = { /* 25 */
.RESERVED0 = 0b0000000000,
.RESERVED1 = 0,
},
.lo_gen = { /* 26 */
.RESERVED0 = 0b000,
.LOGEN_2GM = 0,
.RESERVED1 = 0b00,
.VAS_TST = 0b1111,
.RESERVED2 = 0,
},
.pa_drv = { /* 27 */
.TXLO_IQ_SPI = 0b000000,
.TXLO_IQ_SPI_EN = 0,
.TXVGA_GAIN_SPI_EN = 0,
.TX_DCCORR_SPI_EN = 0,
.RESERVED0 = 1,
.RESERVED1 = 0,
},
.pa_dac = { /* 28 */
.PADAC_BIAS = 0b000000,
.PADAC_DLY = 0b0011,
.RESERVED0 = 0,
},
.tx_gain = { /* 29 */
.TXVGA_GAIN_SPI = 0b111111,
.RESERVED0 = 0b0000,
.RESERVED1 = 0,
},
.tx_lo_iq = { /* 30 */
.TX_DCCORR_I = 0b000000,
.RESERVED0 = 0b00,
.PADAC_IV = 1,
.PADAC_VMODE = 1,
.RESERVED1 = 0,
},
.tx_dc_corr = { /* 31 */
.TX_DCCORR_Q = 0b000000,
.RESERVED0 = 0b101,
.PADAC_DIVH = 1,
.RESERVED1 = 0,
},
} };
class MAX2839 : public MAX283x {
public:
constexpr MAX2839(
spi::arbiter::Target& target
) : _target(target)
{
}
void init() override;
void set_mode(const Mode mode) override;
void set_tx_vga_gain(const int_fast8_t db) override;
void set_lna_gain(const int_fast8_t db) override;
void set_vga_gain(const int_fast8_t db) override;
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
bool set_frequency(const rf::Frequency lo_frequency) override;
void set_rx_lo_iq_calibration(const size_t v) override;
void set_rx_buff_vcm(const size_t v) override;
reg_t temp_sense() override;
reg_t read(const address_t reg_num) override;
private:
spi::arbiter::Target& _target;
RegisterMap _map { initial_register_values };
DirtyRegisters<Register, reg_count> _dirty { };
void flush_one(const Register reg);
void write(const address_t reg_num, const reg_t value);
void write(const Register reg, const reg_t value);
reg_t read(const Register reg);
void flush();
void configure_rx_gain();
};
}
#endif/*__MAX2839_H__*/
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2023 Great Scott Gadgets
*
* 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.
*/
#ifndef __MAX283X_H__
#define __MAX283X_H__
#include "rf_path.hpp"
namespace max283x {
/*************************************************************************/
namespace lo {
constexpr std::array<rf::FrequencyRange, 4> band { {
{ 2300000000, 2400000000 },
{ 2400000000, 2500000000 },
{ 2500000000, 2600000000 },
{ 2600000000, 2700000000 },
} };
} /* namespace lo */
/*************************************************************************/
namespace lna {
constexpr range_t<int8_t> gain_db_range { 0, 40 };
constexpr int8_t gain_db_step = 8;
constexpr std::array<rf::FrequencyRange, 2> band { {
{ 2300000000, 2500000000 },
{ 2500000000, 2700000000 },
} };
} /* namespace lna */
/*************************************************************************/
namespace vga {
constexpr range_t<int8_t> gain_db_range { 0, 62 };
constexpr int8_t gain_db_step = 2;
} /* namespace vga */
/*************************************************************************/
namespace tx {
constexpr range_t<int8_t> gain_db_range { 0, 47 };
constexpr int8_t gain_db_step = 1;
}
/*************************************************************************/
namespace filter {
constexpr std::array<uint32_t, 16> bandwidths {
/* Assumption: these values are in ascending order */
1750000,
2500000, /* Some documentation says 2.25MHz */
3500000,
5000000,
5500000,
6000000,
7000000,
8000000,
9000000,
10000000,
12000000,
14000000,
15000000,
20000000,
24000000,
28000000,
};
constexpr auto bandwidth_minimum = bandwidths[0];
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
} /* namespace filter */
/*************************************************************************/
enum Mode {
Shutdown,
Standby,
Receive,
Transmit,
};
using reg_t = uint16_t;
using address_t = uint8_t;
class MAX283x {
public:
virtual ~MAX283x() = default;
virtual void init();
virtual void set_mode(const Mode mode);
virtual void set_tx_vga_gain(const int_fast8_t db);
virtual void set_lna_gain(const int_fast8_t db);
virtual void set_vga_gain(const int_fast8_t db);
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
virtual bool set_frequency(const rf::Frequency lo_frequency);
virtual void set_rx_lo_iq_calibration(const size_t v);
virtual void set_rx_buff_vcm(const size_t v);
virtual reg_t temp_sense();
virtual reg_t read(const address_t reg_num);
};
}
#endif/*__MAX283X_H__*/
+4
View File
@@ -372,6 +372,10 @@ public:
while(device_status() & 0x80);
}
bool plla_loss_of_signal() {
return (device_status() >> 5) & 1;
}
bool clkin_loss_of_signal() {
return (device_status() >> 4) & 1;
}
+4 -2
View File
@@ -42,7 +42,7 @@ using namespace hackrf::one;
static Thread* thread_controls_event = NULL;
static std::array<Debounce, 7> switch_debounce;
static std::array<Debounce, 8> switch_debounce;
static Encoder encoder;
@@ -193,11 +193,13 @@ void controls_init() {
SwitchesState get_switches_state() {
SwitchesState result;
for(size_t i=0; i<result.size(); i++) {
for(size_t i=0; i<result.size()-1; i++) {
// TODO: Ignore multiple keys at the same time?
result[i] = switch_debounce[i].state();
}
result[result.size()-1] = switch_debounce[switch_debounce.size()-1].state();
return result;
}
+2 -1
View File
@@ -33,9 +33,10 @@ enum class Switch {
Down = 2,
Up = 3,
Sel = 4,
Dfu = 5,
};
using SwitchesState = std::bitset<5>;
using SwitchesState = std::bitset<6>;
using EncoderPosition = uint32_t;
+70
View File
@@ -0,0 +1,70 @@
/* source: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/lz4-decompression-routine-for-cortex-m0-and-later */
.syntax unified
.cpu cortex-m0
.thumb
/* License: Public Domain - I cannot be held responsible for what it does or does not do if you use it, whether it's modified or not. */
/* Entry point = unlz4. On entry: r0 = source, r1 = destination. The first two bytes of the source must contain the length of the compressed data. */
.func unlz4
.global unlz4,unlz4_len
.type unlz4,%function
.type unlz4_len,%function
.thumb_func
unlz4: ldrh r2,[r0] /* get length of compressed data */
adds r0,r0,#2 /* advance source pointer */
.thumb_func
unlz4_len: push {r4-r6,lr} /* save r4, r5, r6 and return-address */
adds r5,r2,r0 /* point r5 to end of compressed data */
getToken: ldrb r6,[r0] /* get token */
adds r0,r0,#1 /* advance source pointer */
lsrs r4,r6,#4 /* get literal length, keep token in r6 */
beq getOffset /* jump forward if there are no literals */
bl getLength /* get length of literals */
movs r2,r0 /* point r2 to literals */
bl copyData /* copy literals (r2=src, r1=dst, r4=len) */
movs r0,r2 /* update source pointer */
getOffset: ldrb r3,[r0,#0] /* get match offset's low byte */
subs r2,r1,r3 /* subtract from destination; this will become the match position */
ldrb r3,[r0,#1] /* get match offset's high byte */
lsls r3,r3,#8 /* shift to high byte */
subs r2,r2,r3 /* subtract from match position */
adds r0,r0,#2 /* advance source pointer */
lsls r4,r6,#28 /* get rid of token's high 28 bits */
lsrs r4,r4,#28 /* move the 4 low bits back where they were */
bl getLength /* get length of match data */
adds r4,r4,#4 /* minimum match length is 4 bytes */
bl copyData /* copy match data (r2=src, r1=dst, r4=len) */
cmp r0,r5 /* check if we've reached the end of the compressed data */
blt getToken /* if not, go get the next token */
pop {r4-r6,pc} /* restore r4, r5 and r6, then return */
.thumb_func
getLength: cmp r4,#0x0f /* if length is 15, then more length info follows */
bne gotLength /* jump forward if we have the complete length */
getLengthLoop: ldrb r3,[r0] /* read another byte */
adds r0,r0,#1 /* advance source pointer */
adds r4,r4,r3 /* add byte to length */
cmp r3,#0xff /* check if end reached */
beq getLengthLoop /* if not, go round loop */
gotLength: bx lr /* return */
.thumb_func
copyData: rsbs r4,r4,#0 /* index = -length */
subs r2,r2,r4 /* point to end of source */
subs r1,r1,r4 /* point to end of destination */
copyDataLoop: ldrb r3,[r2,r4] /* read byte from source_end[-index] */
strb r3,[r1,r4] /* store byte in destination_end[-index] */
adds r4,r4,#1 /* increment index */
bne copyDataLoop /* keep going until index wraps to 0 */
bx lr /* return */
.size unlz4,.-unlz4
.endfunc
/* 42 narrow instructions = 84 bytes */
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* 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.
*/
#ifndef __LZ4_H__
#define __LZ4_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void unlz4_len(const void *aSource, void *aDestination, uint32_t aLength);
#ifdef __cplusplus
}
#endif
#endif /*__LZ4_H__*/
+4 -4
View File
@@ -145,14 +145,14 @@ Continuous (Fox-oring)
rffc507x::RFFC507x first_if;
static void event_loop() {
ui::Context context;
ui::SystemView system_view {
static ui::Context context;
static ui::SystemView system_view {
context,
portapack::display.screen_rect()
};
EventDispatcher event_dispatcher { &system_view, context };
MessageHandlerRegistration message_handler_display_sleep {
static MessageHandlerRegistration message_handler_display_sleep {
Message::ID::DisplaySleep,
[&event_dispatcher](const Message* const) {
event_dispatcher.set_display_sleep(true);
@@ -182,7 +182,7 @@ int main(void) {
portapack::shutdown();
}
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf);
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true);
m0_halt();
return 0;
+26 -16
View File
@@ -61,7 +61,7 @@ portapack::IO io {
portapack::gpio_io_stbx,
portapack::gpio_addr,
portapack::gpio_lcd_te,
portapack::gpio_unused,
portapack::gpio_dfu,
};
portapack::BacklightCAT4004 backlight_cat4004;
@@ -381,8 +381,9 @@ static void shutdown_base() {
*
* PLL0USB = powered down
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
* PLL1 = GP_CLKIN * 10 = 200 MHz
*
* PLL1 =
* OG: GP_CLKIN * 10 = 200 MHz
* r9: GP_CLKIN * 20 = 200 MHz
* IDIVA = IRC / 1 = 12 MHz
* IDIVB = PLL1 / 2 = 100 MHz
* IDIVC = PLL1 / 1 = 200 MHz
@@ -432,9 +433,14 @@ bool init() {
*/
/* Step into the 90-110MHz M4 clock range */
/* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
/* OG:
* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
* r9:
* Fclkin = 10M
* /N=1 = 10M = PFDin
* Fcco = PFDin * (M=20) = 200M
* Fclk = Fcco / (2*(P=1)) = 100M
*/
cgu::pll1::ctrl({
@@ -444,8 +450,8 @@ bool init() {
.direct = 0,
.psel = 0,
.autoblock = 1,
.nsel = 1,
.msel = 9,
.nsel = hackrf_r9 ? 0UL : 1UL,
.msel = hackrf_r9 ? 19UL : 9UL,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
@@ -474,8 +480,7 @@ bool init() {
chThdSleepMilliseconds(10);
clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
clock_manager.enable_first_if_clock();
clock_manager.enable_second_if_clock();
clock_manager.enable_if_clocks();
clock_manager.enable_codec_clocks();
radio::init();
@@ -484,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;
}
@@ -510,11 +518,13 @@ bool init() {
return true;
}
void shutdown() {
void shutdown(const bool leave_screen_on) {
gpdma::controller.disable();
backlight()->off();
display.shutdown();
if (!leave_screen_on) {
backlight()->off();
display.shutdown();
}
radio::disable();
audio::shutdown();

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