Compare commits

..

183 Commits

Author SHA1 Message Date
jLynx 572776bb9e Update past_version.txt 2024-09-23 17:17:47 +12:00
jLynx 073c4ed9f2 Update version.txt 2024-09-23 17:17:38 +12:00
zxkmm 42233e62df transparent splash logo & fix tab widget color glitch (#2256) 2024-09-22 19:40:54 +02:00
zxkmm 9390317a75 add shift feat for protcol view tool (#2258)
* add shift

* add shift

* format

* add padding (wrote by AI)

* layout fine tune

* remove debug thing

* edit per request

* remove torjan (jk)

* int 16 should be good enough for it
2024-09-22 15:30:31 +02:00
Erwin Ried 89b51095bf New H4M version (#2259) 2024-09-21 16:15:17 +02:00
Totoo 9438c9a574 SSTV TX to ext (#2254)
* SSTV TX to ext
2024-09-20 21:41:44 +08:00
jLynx 7feef9d38b Learn ic fix (#2253)
* WIP

* Fixed merge

* Added test code

* WIP

* Clean up

* add reset learned params

* ui fix

* ui fix2

* Updated func

* Fixed english

* WIP

* WIP testing

* Added new debug app

* Got new app for debug

* Got new app for debug

* Got one full page showing

* Got app working with all reg

* Got app working with all reg

* Got full hex showing

* Fixed dp

* Fixed dp

* Moved entities

* Enabled apps again

* SHow battery debug if ic

* WIP

* Refactored further

* WIP

* Refactor and clean up

* Refactor and clean up

* fix warning, add tte/ttf, add cycles counter.

* wip

* morse tx to ext app

* fix morse crash

* fix ui

* Updated wording

* WIP

* WIP

* Updated to display hours and minutes

---------

Co-authored-by: HTotoo <ttotoo@gmail.com>
2024-09-20 09:59:17 +00:00
zxkmm 582bb02a75 add bitmap arr to file (or reverse) converter (#2248)
* add covert tool

* fix for generator

* fix for generator
2024-09-20 09:56:33 +02:00
Totoo bfa7f3ca6b Wardrive map improv (#2251)
Added paginator.
Added Flipper SUB file support.
2024-09-16 21:59:28 +02:00
Mark Thompson 21facd9cc3 Options to select 314.9 or 315MHz TPMS freq (#2252) 2024-09-15 23:20:04 -05:00
zxkmm d30234aaa6 reword of #2158 (#2249) 2024-09-11 19:11:11 +02:00
gullradriel d45a17236e USB Serial setfreq support (#2246) 2024-09-09 20:01:25 +02:00
zxkmm 281e52d143 fine tune the invert logic (#2243) 2024-09-08 17:38:40 +02:00
Totoo 1f78646054 only inv or fakebrightness (#2240) 2024-09-08 09:35:09 +02:00
zxkmm 31c844b48e fix black bg of those bitmap when we have themes enabled (#2239) 2024-09-07 17:39:50 +02:00
Totoo 87069f11e5 invert display option (#2232)
* invert display option

* text fix, format code
2024-09-07 10:58:57 +02:00
Totoo e6afd7744d SubghzD & Weather improvments (#2237)
* Weather restructure

* Added Solight TE44 protocol

* Add disabled Marantec24, and GangQi

* More opt in subghzd
2024-09-06 20:23:11 +02:00
Dongping Guo (Donny) 76763b9bab Add BLE RX filter options (#2236) 2024-09-06 20:22:00 +02:00
Totoo 02b75f567a Setfreq usb command (#2235)
* SetFreq usb command for rx apps
* code format, better check
2024-09-04 20:26:31 +02:00
Erwin Ried a2c4fefe34 Slighty smaller video on readme 2024-09-03 09:27:22 +02:00
Erwin Ried 121352b149 Fully remove references to bountysource + ltt video (#2233)
-Permanently remove the references to the bountysource scammers
-Added LTT video about the device
2024-09-02 21:47:11 +08:00
Totoo 0ae7768f20 Battery info switcher (#2230) 2024-08-28 11:32:24 +02:00
jLynx 1a0555f9eb Update README.md 2024-08-27 22:38:39 +12:00
GitClo 6752bf0279 Slovak FM radio frequencies (#2213)
* Slovak FM radio frequencies

Comprehensive list of most regional and local FM radio stations across Slovakia

* Update RADIOS_FM_SK.txt

Shortened list of Slovak FM radio stations - no longer crashing; transliterated non-english characters to avoid displaying problem
2024-08-21 16:49:25 +08:00
Brumi-2021 6dc7e3dfc5 Needed_changes_to_compile_with_gcc_13.2.1 (#2224)
* Needed_changes_to_compile_with_gcc_13.2.1

* re-activate_length_binary_size_protect
2024-08-15 19:31:34 +02:00
Mark Thompson c992780974 Fix compile error with gcc versions >9.2.1 (#2223) 2024-08-15 08:38:14 +02:00
Mark Thompson 750d592288 Fix small fonts in theme code (#2222) 2024-08-14 19:39:01 +02:00
ArjanOnwezen c77252cca2 ICAO24 database update. (#2221) 2024-08-14 17:11:34 +02:00
Totoo df3981876d Move ADSBTX to ext (#2214) 2024-08-09 23:47:08 +02:00
Totoo 05146638fc Weather size fix (#2211)
* Shrink weather size

* fix compiler warning
2024-07-29 10:46:45 +02:00
Totoo 765e3be55b SubghzD rework (#2210)
* Removed controller code

* Add Legrand

* Added Somify Keytis

* Somify

* better display
2024-07-29 07:47:50 +02:00
Totoo 9211975868 Protoview (#2206)
* ProtoView first tests

* Imp

* fix, reset
2024-07-29 07:45:56 +02:00
Erwin Ried 96a60e82bc Reshuffle the readme videos (#2208)
Does people click because thumb/title or just because the order? 🤔
2024-07-24 11:53:44 +02:00
Totoo 5d9428e568 Weather station update (#2209)
* Acurite609txc fix

* Added Kedsum

* Add auriol 5in1

* prev was: FPW_Acurite5in1, this is EmosE601x

* Remove BTN from weather
Saved flash space by creating chaos.
Fix EmosE601
2024-07-24 11:53:09 +02:00
Totoo 2fdd531fe7 Add fully randomized names to blespam (#2207)
* Add fully randomized names to blespam

* fix for all

* fix warning
2024-07-24 09:53:41 +02:00
E.T. f13756d4c7 copy ppmp to sdcard images (#2203) 2024-07-18 20:52:21 +02:00
Totoo ae6724b359 Better filter for max ic (#2201)
* fix unk

* next try
2024-07-18 09:55:11 +00:00
jLynx e11c28cf30 Added new functions (#2202) 2024-07-18 11:50:53 +02:00
Totoo b93fbad207 Add file attached in #2189 (#2199) 2024-07-18 00:11:13 +08:00
Totoo 2bedb5f09f Max17055 improvements (#2197)
* fixes

* more fixes

* more fixes

* fix

* more fixes

* fix icon updates

* fix init unk error

* simplify

* improve icon init

* code format

* Update ui_battinfo.cpp
2024-07-17 21:18:57 +12:00
Bernd Herzog 19eb6b44d5 fixed usb serial not working after reconnect (#2196)
* fixed usb serial not working after reconnect

* added restart for sd over usb disconnect

* improved usb serial fix

* fixed formatting
2024-07-17 21:17:42 +12:00
Bernd Herzog 048359fb0e added --print-memory-usage to linker parameters (#2195)
* added --print-memory-usage to linker parameters

* included standalone apps
2024-07-16 11:47:55 +02:00
jLynx ae75540d35 Max17055 (#2194)
max17055
2024-07-15 22:17:55 +02:00
Brumi-2021 9ef58b7763 updating_comments (#2191) 2024-07-08 11:11:12 +02:00
Brumi-2021 032168234a Invest limit bw ssb to 3 k (#2188)
* Adding_TX_BW_user_selection_to_the_Mic_SSB_mode

* TX_SSB_BW_adjustm_2k_3k_4k

* init_value_correction

* Comment_correction

* Leaving_only_OK_txbw_SSB_2K_3K

* Limti_SSB_TX_BW_2K_3K
2024-07-06 13:34:09 +02:00
Mihai b53b5c70d3 Radio FM and Airbands for Romania added (#2179)
* freq RO(FM and Airband)

* removed comments

* removed spaces

* Rename RO_AIRBAND.txt  to RO_AIRBAND.TXT

VS fuckup
2024-06-13 14:06:17 +08:00
Erwin Ried ee53016c6e Update README.md (#2175) 2024-06-07 11:33:45 +02:00
Totoo 2bf1116c6a New themes (#2166)
green, red themes
2024-05-28 17:51:31 +02:00
Totoo ced8012783 Theme system (#2164)
* Themes instead of Styles
* Colors changed to theme colors
* Reworked style management
* Theme settings app
* warn, menu dual set
* Added Aqua style
2024-05-27 21:02:52 +02:00
summermorningdream a8203a6372 add path class usage type debug (#2154) 2024-05-21 18:08:47 +08:00
jLynx e4d8bcb88d Updated submodule again (#2148) 2024-05-21 21:37:06 +12:00
summermorningdream db3ae29bd0 fix flash util err when no APPS dir (#2155)
* add path class usage

* fix when don't have apps dir

* clean up
2024-05-20 15:02:29 +08:00
summermorningdream 2a78c543a1 revert adding ccache tool (#2153) 2024-05-18 17:33:07 +08:00
Totoo 53b59a8a22 Fix for compile under docker (#2147) 2024-05-12 15:18:14 +02:00
Bernd Herzog 100bea644c Version independent external apps (standalone apps) (#2145)
This pull requests adds a new type of external app to the firmware: The standalone app.

Pros:

Will work after an upgrade.
Size of image is only limited by shared heap size of M0 (application) (64kb total).
Cons:

No full access to all functions in the main firmware. One well defined (and versioned) API handles all communication.
The Pacman app was converted to be the first the the new kind.
2024-05-12 14:55:11 +02:00
summermorningdream fe71592b68 revert submodule downgrade caused my my mistake (#2146) 2024-05-12 18:36:01 +08:00
Erwin Ried 4a96feb829 Adding some missing font 2024-05-12 01:00:55 +02:00
Totoo e666eb1797 MsgDblReg fix (#2144) 2024-05-11 10:52:35 +02:00
gullradriel 963c6e5238 Revert "Updated HackRF revision (#2122)" (#2142)
This reverts commit fac71677d4.
2024-05-08 18:32:38 +12:00
jLynx fac71677d4 Updated HackRF revision (#2122)
* WIP getting name from board
* Updated labels
2024-05-08 08:26:05 +02:00
summermorningdream 075b23b291 add bool type print (#2136) 2024-05-08 08:23:47 +02:00
Mark Thompson 3628576d60 Removed some blank lines (#2140) 2024-05-07 15:41:58 +02:00
Erwin Ried a3b71d0e3d Changing all links to grabify (#2139)
Note: If you are worried about grabify, one of our Mayhem maestros cooked up Grabify. Just using general stats to spruce up layout and discussions.
2024-05-05 09:54:25 +02:00
sommermorgentraum 0ea8453e8a tune output order (#2132) 2024-04-30 08:07:39 -05:00
sommermorgentraum 1ffedace7b replace_my_ide_s_default_comment (#2131) 2024-04-30 08:08:21 +02:00
Mark Thompson e43f7a7980 Change flashing message to match LED behavior (#2130) 2024-04-29 22:29:46 +02:00
Totoo f6a2d21624 support for battery if any + TPMS app to ext (#2129)
* batt initial
* batt widgets
* settings modify
* batt info screen
* ability to hide icon
* battView
* redo tmps
* hide curr + charge if no data
* fix flashing
2024-04-29 17:38:27 +02:00
sommermorgentraum f572b00391 add real gcc ver chk (#2118) 2024-04-29 17:36:30 +02:00
Totoo d74fd527e2 TPMS to ext app (#2128)
* initial

* fix
2024-04-29 13:30:21 +02:00
sommermorgentraum 819f35ac93 seperate asyncmsg (#2124) 2024-04-27 21:24:41 +02:00
Lpd738 60da661999 Update JAMMER_GSM_GPS.TXT (#2125)
Add L5 GPS range
2024-04-27 11:50:08 +08:00
jLynx 43077c79cf Updated submodule (#2121) 2024-04-26 19:49:21 +12:00
Totoo 06651dc97c Bmp File Viewer + extras (#2119)
* BMP initial

* Add vscode debug configuration as a template (#2109)

* usb serial debug interface & usb serial async msg (#2111)

* add serial_debug

* not use OSS

* add path print

* add string print and vec

* clean up

* clean up

* format

* add an async blocking bool

* add an async blocking bool - comment

* protect the unexpected tx

* naming

* remove demo code

* fix bottom-up format, and add auto extend, ..

* bmp write

* Minor additions

* Minor

* overwrite on create

* Tmp

* Basic view - WIP

* debug

* add literal str print in asyncmsg (#2113)

* add literal str print in asyncmsg

* remove debug things

* accept suggestion per gull

* fix documentary

* Fix bug (#2114)

* Disable Back button during Touch Calibration (#2115)

* ADS1100 (#2116)

* WIP

* WIP

* WIP

* Corrected name

* WIP

* WIP

* WIP

* WIP

* Added new calc

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Added debug serial lines

* WIP

* Fixed issue

* Fixed calculation issue

* Added voltage to performance DFU menu

* Added padding function and added voltage to perf menu

* Clean up

* Refactor

* Fixed linting

* Hides voltage if PP does not conatin IC

* WIP showing battery %

* made the percentage a int

* Added % to header

* Removed test UI

* Removed comment

* Added fix for precision too large

* Added fix for precision too large

* Linting

* widget

* auto zoom

* remove debug

* move in screen

* fix math

* remove test code

* fix

* fix compiler warning

* BMP File viewer

* Full screen

* bg instead of noice

* add comment

* Handle some not supported formats.

---------

Co-authored-by: E.T <tamas@eisenberger.hu>
Co-authored-by: sommermorgentraum <24917424+zxkmm@users.noreply.github.com>
Co-authored-by: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com>
Co-authored-by: jLynx <admin@jlynx.net>
2024-04-24 05:18:12 +02:00
jLynx 67975d76a0 ADS1100 (#2116)
* WIP

* WIP

* WIP

* Corrected name

* WIP

* WIP

* WIP

* WIP

* Added new calc

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Added debug serial lines

* WIP

* Fixed issue

* Fixed calculation issue

* Added voltage to performance DFU menu

* Added padding function and added voltage to perf menu

* Clean up

* Refactor

* Fixed linting

* Hides voltage if PP does not conatin IC

* WIP showing battery %

* made the percentage a int

* Added % to header

* Removed test UI

* Removed comment

* Added fix for precision too large

* Added fix for precision too large

* Linting
2024-04-21 20:44:47 +12:00
Mark Thompson 282e4da1cb Disable Back button during Touch Calibration (#2115) 2024-04-21 08:34:55 +02:00
Totoo e7359563c6 Fix bug (#2114) 2024-04-20 14:26:54 +02:00
sommermorgentraum 03ca87b13e add literal str print in asyncmsg (#2113)
* add literal str print in asyncmsg

* remove debug things

* accept suggestion per gull

* fix documentary
2024-04-20 18:31:57 +08:00
sommermorgentraum b2da21b88a usb serial debug interface & usb serial async msg (#2111)
* add serial_debug

* not use OSS

* add path print

* add string print and vec

* clean up

* clean up

* format

* add an async blocking bool

* add an async blocking bool - comment

* protect the unexpected tx

* naming

* remove demo code
2024-04-18 18:10:34 +08:00
E.T bb1b703dc1 Add vscode debug configuration as a template (#2109) 2024-04-17 20:21:16 +08:00
eried 990f63e5de Adjusting a bit the issue templates for clarity 2024-04-17 10:48:24 +02:00
Erwin Ried 6fe8ab4f99 Delete .github/ISSUE_TEMPLATE/03_problem-upgrading-the-firmware.md 2024-04-17 10:43:24 +02:00
Erwin Ried d6ed12191d Create 03_problem-upgrading-the-firmware.yml 2024-04-17 10:41:25 +02:00
Totoo 8b159f447a Smaller freq changes with encoder on SubghzD and Weather Station (#2106) 2024-04-16 14:20:12 +08:00
Totoo e9c32504d1 Pocsagtxserial (#2099)
* sendpocsag first test
* optimize
* nicer
* overflow
2024-04-11 15:10:59 +02:00
gullradriel 004799e1a3 Contributors scroll (#2093)
* adding on_right

* menu view instead of console

* fixing on_right typo

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-08 13:45:33 +08:00
jLynx 8e90c65a62 v2.0.1 (#2090)
* Update version.txt

* Update past_version.txt
2024-04-07 16:24:01 +12:00
gullradriel bfe67a13b7 removed dead link (#2089)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-07 00:59:40 +08:00
gullradriel f091db3270 Mic audio ak4951 fix + SSTV description (#2088)
* fixed too long description, thanks to ImDroided

* fixed ak4951 RxAudio not working on startup when enabled

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-05 16:29:18 -05:00
Mark Thompson a602abf1d8 Fix Cancel button in Settings->Autostart (#2087) 2024-04-05 16:04:39 +02:00
ImDroided 689224fd8d Added CubeSats to Presets 2024-04-05 08:52:14 +02:00
gullradriel 23cf1c2737 fix build warning in settings (#2081)
* fix warning ui::SetAutostartView::opts' should be initialized in the member initialization list

* clang

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-03 14:17:48 -05:00
Totoo c48cbb7e55 Autostart option (#2079)
* Autostart app on boot
* Settings page
* ext app support
2024-04-03 16:27:13 +02:00
Totoo 3665b3c607 Added sil value ASDB (#2078)
* Added sil value. resolves #2005

* readibility
2024-04-03 15:57:39 +08:00
Totoo 804b7c87b7 allow to load more files but unordered (#2077) 2024-04-02 20:19:28 +02:00
Totoo a30875b582 Fileman memory opt (#2076)
* paginator
* fixes
* string instead of path, so saves ram.
* fix FileLoadView
* pagination list number
* pagination "size" test
* added detection of special folder name to display page numbers instead of (0 or a number of byte)
* order of things: only one directory '..', only on first page
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-02 17:02:02 +02:00
Mark Thompson 56303eb385 Use freq from AppSettings unless passed from another app (#2073) 2024-03-30 12:57:43 -05:00
Totoo d29172f41e Add GPS icon, fix overwrite bug (#2072) 2024-03-30 16:48:45 +01:00
sommermorgentraum 6d9d5ad1af fix read file (#2070) 2024-03-30 13:03:27 +01:00
gullradriel 2acefee4d2 adjust GUI for more preset name room (#2068) 2024-03-30 10:34:37 +01:00
Mark Thompson cb9fee8f08 Modify warning text (#2066) 2024-03-29 18:50:28 -05:00
Mark Thompson b473930f94 Beep-on-packet support in ADSB RX app (#2065) 2024-03-29 16:34:18 -05:00
Mark Thompson 6e5eadd25c Beep-on-packet support in AIS app (#2064)
* Beep-on-packet support in AIS app
2024-03-29 22:08:32 +01:00
Dumpster-Diver 76017c91a9 Create RADIOS_FM_FR_PARIS.TXT with FM Radios in Paris (#2049)
* Create RADIOS_FM_FR.TXT with FM Radios in Paris

This new file will add FM frequencies for France. As a start it covers Paris FM radio frequencies. It is based on https://www.csa.fr/maradiofm/liste_ville?recherche=3&commune=Paris

* Update and rename RADIOS_FM_FR.TXT to RADIOS_FM_FR_PARIS.TXT

Reduce frequency description length to fit under the 30 chars limit
2024-03-29 15:52:00 -05:00
Mark Thompson f1ebb1a6e3 Beep-on-packet support in Weather app (#2063) 2024-03-29 21:29:50 +01:00
Mark Thompson 746bf1c15f Beep-on-packet support in TPMS app (#2062)
* Beep-on-packet support in TPMS app
* audio::output::stop when exiting
2024-03-29 21:28:27 +01:00
Mark Thompson 0db65ccb6b Beep-on-packet support in POCSAG app (#2061) 2024-03-29 21:27:53 +01:00
Mark Thompson fe2fbb847f Beep-on-packets option in Settings and updated ERT & Sonde apps (#2058)
* Beep-on-packets option in Settings
* Add beep to ERT app
2024-03-29 19:26:45 +01:00
Mark Thompson ba36680a7b FileMan support for folder Cut & Paste and Timestamp Rename (#2055) 2024-03-29 10:44:37 +01:00
set sail into the sea of life 90ff1e62ce move the no ext app tile to forward (#2047)
* strong notice of missing ext app

* textual

* move the notice tile to front and remove the painter string

* remove notice icon

* format

* for back button

* remove <>
2024-03-28 21:05:09 -05:00
Mark Thompson 2e1e02baac Removed special font characters (#2056) 2024-03-28 21:04:19 -05:00
Totoo 2dca408e45 Wardrive Map app (#2050)
* WardriveMap app

* better txt

* Count only displayed markers
2024-03-28 19:25:23 -05:00
Daniel fa56cdd015 Adding Australian UHF channels and Brisbane Airband to FREQMAN, adding Australian UHF to Looking Glass (#2048) 2024-03-27 19:57:40 -05:00
Mark Thompson 264fa9a28e Continued folder names consolidation & GPS default folder (#2042)
* Continued folder names consolidation

* Clang
2024-03-26 11:32:24 -05:00
Mark Thompson d5c8525afc Declare all folder names in a single source file (#2039) 2024-03-25 08:44:49 +01:00
ImDroided d9bbd1b9ff SSTV Freqs (#2038)
* Added and fixed freqs

Figured I would clean these up and add a bit.

* Create SSTV.TXT

SSTV Frequencies for SSTV app
2024-03-25 01:21:05 -05:00
gullradriel b47cee435e fix typo (#2037) 2024-03-24 18:50:42 -05:00
gullradriel c078bac0e7 Looking glass beep (#2036)
* first draft of looking beep
* fixed beep squelch range in percent
* took out steps 
* gui adjustements
* uniformize calculation and beep squelch in db
* uniformisation, fix 24/48 error
2024-03-24 22:32:13 +01:00
gullradriel 6177b08632 fixing level beep, bad copy paste on special SPEC mode (#2035)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-03-24 18:47:15 +08:00
Mark Thompson 53a1bacd46 Revert "Add cmake dependency on baseband for external APPS" (#2034)
This reverts commit abb9b6c8ad.
2024-03-24 10:39:20 +08:00
ImDroided 4c012a9493 Added and fixed freqs (#2033)
Figured I would clean these up and add a bit.
2024-03-23 21:16:27 -05:00
Mark Thompson abb9b6c8ad Add make dependency on baseband for external APPS (#2031) 2024-03-23 21:34:15 +01:00
gullradriel 6e2507d609 fix gui glitch on beep squelch (#2030) 2024-03-23 21:01:48 +01:00
gullradriel 536981998b Level fix and beep, RSSI avg fix (#2027)
* fix average value being overflow'd
* fix audio and mod changes, preps for beep mode
* fixed beep_freq range, added a stop and set a variable sooner
* added support for audio beep messages
* better scaler for beep
* added bip squelch and saving of bip squelch and audio mode
* saving modulation, fixing audio
* added save and restore of bandwidth
* simpler ctcss clean on change mode
2024-03-23 19:27:05 +01:00
Mark Thompson 1a87f2d701 Revert accidental newbutton bg color change (#2029) 2024-03-23 13:26:46 -05:00
Mark Thompson 81e24d582d More debug Audio Beep Test tweaks (#2028) 2024-03-23 13:25:36 -05:00
Mark Thompson 28a5fc5915 Improved Audio Beep Test (#2026) 2024-03-23 08:21:35 +01:00
Mark Thompson ba4290cf0d Audio Beep Test (Debug) external app (#2023) 2024-03-22 11:10:16 +01:00
Mark Thompson 692644d214 Radiosonde QR code fixes (#2021)
* Increase size of small QR Code

* Fixed QR code lat/lon for iPhone

* Show current geopos location on map

* Deprecate large QR code
2024-03-21 10:56:06 -05:00
Mark Thompson 4aeaf94d2d Radiosonde beep tone tweaks (#2020)
* Tweak RSSI audio beep frequency range
* Clang & changed min freq
* Save beep/log/crc settings in .ini file
* Update copyright string
* Added generic audio_beep message
2024-03-21 10:14:54 +01:00
Totoo 8391ca8052 Fox hunt (#2015)
* foxhunt_rx
2024-03-19 21:33:53 +01:00
Mark Thompson 807c76346b Improved audio beep tone (#2014)
* Improved sine wave beep tone

* Prevent divide-by-zero
2024-03-19 15:30:05 -05:00
Mark Thompson 308573918c Added a beep duration parameter (#2013) 2024-03-19 18:23:06 +01:00
Mark Thompson 74442f197d Fixed Sonde RSSI "Beep" (#2012) 2024-03-19 10:53:55 +01:00
Mark Thompson b53c8e1f80 Stop audio DMA in baseband::shutdown() (#2011) 2024-03-19 05:58:23 +01:00
Mark Thompson c5f73cf8f4 Darken FileMan button colors for visibility (#2010) 2024-03-19 05:56:23 +01:00
Totoo c1bf2620c7 Bt namespam (#2009)
* Add random names
2024-03-18 21:45:45 +01:00
Mark Thompson 8383363e74 Preserve radio mode when updating iq_tx_phase_cal (#2008) 2024-03-18 21:45:02 +01:00
Alexandros Andreou 6e34343bde added all and all-safe attack types to blespam (#2003) 2024-03-18 09:13:10 +01:00
gullradriel 01e4ff65a2 fix butngrid/capture and fix contributor name (#1995)
* fixing contributor name

* used clear instead of swap

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-16 00:39:34 +08:00
Totoo 9e40e38f07 Record gps lat, lon, satinuse from recorder app if there was gps signal. (#1992)
* Record gps lat, lon, satinuse from recorder app if there was gps signal.

* Add _GEO to filename that has geotag
2024-03-15 21:56:20 +08:00
not tre mann 181624daf1 lambda the flash util menu add item code (#1993)
* lambda the flash util menu add code

* clean up
2024-03-14 22:56:55 -05:00
Mark Thompson f0614c882b File manager "Clean" button tweaks (#1990)
* File Manager "clean" button tweaks

* Add files via upload
2024-03-13 22:42:40 +01:00
not tre mann 14f42d1fe8 support "clean" files (#1983)
* init for clean files
* textual
* icons position
2024-03-13 18:06:14 +01:00
Mark Thompson 999f9e2ded Fix #1982 (enable DMA interrupt for all apps) (#1987) 2024-03-13 16:57:35 +01:00
Mark Thompson 61dc8a0225 Disabled unused baseband "no-op" process (#1986) 2024-03-13 16:08:52 +01:00
Mark Thompson 0b2d5f75cc Start audio DMA only in apps that use audio (#1982)
* Start audio DMA only in apps that use audio
* Rename main.cpp to main.cpp.unuse
* shrink_tx_buffer fix for transfers_per_buffer==1 scenario
2024-03-13 16:07:44 +01:00
Erwin Ried 3c489e1a81 Steamlining the warning (#1984)
I think it is enough to note nobody should pay for the download
2024-03-13 19:30:05 +08:00
gullradriel 910fd82051 freqman: limiting description size to 30, and minor fix (#1977)
* Limiting description size to 30 as it was documented before

* cosmetic adjustement

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-11 16:40:47 +01:00
gullradriel 866e12fbc5 added RXIQCALC (#1974)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-10 23:25:18 +01:00
Mark Thompson ccd71d9593 Set iq_phase_cal in radio when starting SPEC mode (#1972) 2024-03-10 20:50:48 +01:00
Mark Thompson e9b9ba4602 Deallocate menu buttons to save memory (#1971)
* Deallocate hidden menu buttons
* Added Copyright
* Added comments
2024-03-10 20:49:36 +01:00
gullradriel 986e37a3c9 fix unused warning (#1970) 2024-03-10 20:05:05 +01:00
gullradriel 2ac36d2d0b Level app: coloration of RxSat value (#1969) 2024-03-10 18:29:21 +01:00
Mark Thompson 3b79549129 Added enums for recon_config bits (#1965) 2024-03-10 08:30:05 +01:00
gullradriel 160a778104 Recon repeat auto recorded file mode (#1960)
* added everything needed to have a repeated file mode option to keep or delete files

* automatic filename for keep file

* fixing restart after replay

* fixed auto record replay/repeat restart

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-10 08:45:54 +08:00
Mark Thompson e496f8ecc2 Eliminate unnecessary NumberField to save code space (#1964)
* Eliminate extra NumberField to save code space

* Clang
2024-03-09 17:36:48 -06:00
Brumi-2021 9d8132978f Adding Rx IQ error phase CAL to SPEC Audio_App (#1963) 2024-03-09 23:46:38 +01:00
Mark Thompson 73c29f666f Commented out Debug -> Memory Usage app (#1962) 2024-03-09 22:03:07 +01:00
Mark Thompson d895c9fa91 Show true M0 free heap space in DFU overlay (#1961) 2024-03-09 21:39:06 +01:00
gullradriel b5e66387c3 Rxsat in Level app (#1959)
* added Rx Saturation

* testing reducing values to uint8_t

* clang format

* refactorisation

* cleanings

* cleanings

* set back request_m4_performance_counter to zero on app exit

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-09 22:26:56 +08:00
Mark Thompson 1fbfdbccf8 Remove unused text_info widget (#1955) 2024-03-08 01:29:13 +08:00
Totoo 2648f18b12 External sensor tester app (#1949)
* added tilt
* added extsensors app
* added icon
* Added EnvironmentData to ExtSensor app and message.hpp
* Added gotenv command
2024-03-07 17:22:24 +01:00
Mark Thompson 908c0e2cd5 Fix compiler warning (#1952) 2024-03-07 17:18:08 +01:00
Mark Thompson 4f3530db5e Update FPV_ANALOG.TXT (#1951)
* Include FPV in freq description

* Added comment referencing source of this data
2024-03-08 00:00:53 +08:00
not tre mann 73137891c4 change sun bitmap to make it more like a sun (#1946) 2024-03-07 07:50:49 +01:00
not tre mann c28087ac44 cheat code protect in pacman app (#1945) 2024-03-07 07:49:17 +01:00
Totoo 9e9dd3a521 Dynamic usb buffer fill for screenframeshort (#1935)
* fully fill usb serial buffer
* Update usb_serial_shell.cpp
* Added a comment, to trigger wf
2024-03-07 07:42:58 +01:00
Dima Danylevskyi 3bf689f339 Add FPV analog frequenies to FREQMAN (#1940) 2024-03-07 07:37:41 +01:00
Erwin Ried 86c326ef12 Update README.md (#1937)
Adding a warning
2024-02-26 17:50:11 +01:00
jLynx 5ae97d47f1 Fixed existing dir check (#1933) 2024-02-25 08:28:44 +01:00
Erwin Ried 762512b7dc Rename problem-upgrading-the-firmware.md to 03_problem-upgrading-the-firmware.md 2024-02-23 14:13:54 +01:00
Erwin Ried 9c848a6215 Update config.yml 2024-02-23 14:13:23 +01:00
Erwin Ried 9c86e15ddb Rename feature_request.yml to 02_feature_request.yml 2024-02-23 14:12:20 +01:00
Erwin Ried 57ccdb0044 Update and rename feature_request.md to feature_request.yml 2024-02-23 14:11:13 +01:00
Erwin Ried 1172175cc8 Rename bug_report.md to 01_bug_report.yml 2024-02-23 14:06:04 +01:00
Erwin Ried 9ed8ad5903 Create config.yml 2024-02-23 14:04:52 +01:00
Erwin Ried e4da8b979e Update bug_report.md (#1929) 2024-02-23 14:02:11 +01:00
Erwin Ried 4b83fd8a40 Updating the videos from people about hackrf (#1918)
The latest series from sasquash looks promising to engage beginners :) also will do some tracking with grabify to check engagement
2024-02-19 19:30:14 +08:00
Mark Thompson cd80da58d8 Improved flash image validation (#1916) 2024-02-18 19:02:42 +01:00
Mark Thompson a1f7010e08 Optimized fake brightness code (#1915) 2024-02-17 18:44:06 -06:00
Noah Axon 3499d2fa75 Fix Recon Modulation/Bandwidth presets (#1913) 2024-02-17 19:24:11 +01:00
notComposer 948e039574 fix shift back in screenshot and mayhem hub (#1910)
* fix_shift_back

* clean up

* gitignore

* remove the workaround in notpad cuz it's been fixed in this PR

* format

* add credit for mark

* 2024
2024-02-18 00:44:31 +08:00
Bernd Herzog 27dc37713b added crc32 command to usb shell (#1911) 2024-02-17 10:38:09 +01:00
483 changed files with 15683 additions and 4247 deletions
+49
View File
@@ -0,0 +1,49 @@
name: Bug report
description: File a bug reports regarding the firmware.
labels:
- bug
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Mayhem firmware.
Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
- type: textarea
id: description
attributes:
label: Describe the bug.
description: "A clear and concise description of what the bug is."
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction
description: "How can this bug be reproduced?"
placeholder: |
1. Switch on...
2. Press button '....'
3. Wait for the end of the universe
4. It burns
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: "A clear and concise description of what you expected to happen"
placeholder: |
1. Generates file on...
2. I get a cheeseburger...
validations:
required: true
- type: input
id: target
attributes:
label: Environment/versions
description: Specify extra details about versions and environments affected
- type: textarea
id: anything-else
attributes:
label: Anything else?
description: Let us know if you have anything else to share.
@@ -0,0 +1,25 @@
name: Feature Request
description: For feature requests regarding the firmware.
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.
Please try the latest nightly release before submitting this, make sure it has not been implemented already. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
- type: textarea
id: proposal
attributes:
label: "Description of the feature you're suggesting."
description: |
Please describe your feature request in as many details as possible.
- Describe what it should do.
- Note whether it is to extend existing functionality or introduce new functionality.
validations:
required: true
- type: textarea
id: anything-else
attributes:
label: Anything else?
description: Let us know if you have anything else to share.
@@ -0,0 +1,45 @@
name: Firmware/boot issue
description: If you are having firmware upgrade or booting problems.
labels:
- firmware
body:
- type: markdown
attributes:
value: >2-
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Mayhem firmware.
Before creating this issue, **do the following**:
* Read the Wiki on booting:
https://github.com/portapack-mayhem/mayhem-firmware/wiki/Won't-boot
* Read:
https://github.com/portapack-mayhem/mayhem-firmware/wiki/Update-firmware
* Watch carefully: https://www.youtube.com/watch?v=_zx4ZvurgOs
* (if you are not in Windows) also check:
https://www.youtube.com/watch?v=kjFB58Y1TAo
* Check hardware versions:
https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions
- type: textarea
id: problem
attributes:
label: What happened?
description: If after following the above troubleshooting the problem still remains
placeholder: Describe what was is the problem
- type: input
id: hardware
attributes:
label: Your hardware
description: Check the link with the versions above
placeholder: Which device you have?
- type: checkboxes
id: firmware_update_before
attributes:
label: Tests
description: >-
Things might be confusing the first time, please check the documentation
above
options:
- label: I managed to update/boot previously
required: false
-35
View File
@@ -1,35 +0,0 @@
---
name: Bug report
about: Create a report to help us improve the software
title: ''
labels: bug
assignees: ''
---
----
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases)
----
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Tap on '....'
3.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Affected versions**
Please write any difference related with the Expected behavior, on the following versions:
* Latest Stable release:
* Latest Nightly release:
* Previous working release:
**Additional**
If the bug is difficult to explain, additionally to the text please include images and videos.
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Need more help?
url: https://discord.com/invite/tuwVMv3
about: For any question that does not fit as an issue, check our discord.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen. Remember that adding stuff is always possible, but time is a limited resource for everyone. Check the wiki for more information how to compile the firmware and try to explore modifying the code yourself.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional**
If the suggestion is difficult to explain, additionally to the text please include images and videos.
@@ -1,44 +0,0 @@
---
name: Problem upgrading the firmware or booting
about: If you are having firmware upgrade or booting problems
title: ''
labels: 'firmware'
assignees: ''
---
----
Before creating this issue, **do the following**:
* Read the Wiki on booting: https://github.com/portapack-mayhem/mayhem-firmware/wiki/Won't-boot
* Read: https://github.com/portapack-mayhem/mayhem-firmware/wiki/Update-firmware
* Watch carefully: https://www.youtube.com/watch?v=_zx4ZvurgOs
* (if you are not in Windows) also check: https://www.youtube.com/watch?v=kjFB58Y1TAo
----
**Describe the issue**
A clear and concise description of what the issue you are facing is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Tap on '....'
**My Hardware**
Please specify what PortaPack hardware version you are using.
You can find the list of versions here: https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions
**Affected versions**
Please tell us what version you are running.
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/portapack-mayhem/mayhem-firmware/releases
**Were you able to update the firmware before?**
Things might be confusing the first time, please check the video available on the link above.
**Can you try the upgrade with a different PC/Portapack/HackRF?**
If is possible, swap hardware and try again. Also, try different USB cables, even if the one you are using works fine for other purposes.
**Additional**
If the issue is difficult to explain, additionally to the text please include images and videos.
+2 -2
View File
@@ -54,7 +54,7 @@ jobs:
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
- name: Download world map
run: |
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
@@ -66,7 +66,7 @@ jobs:
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
- name: Download world map
run: |
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
@@ -50,7 +50,7 @@ jobs:
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1 +1 @@
v1.9.1
v2.0.1
+1 -1
View File
@@ -1 +1 @@
v2.0.0
v2.0.2
+8 -1
View File
@@ -51,6 +51,7 @@
.dep/
/build*
CMakeFiles/
cmake-build-debug/
# Debugging
.gdbinit*
@@ -68,9 +69,15 @@ CMakeFiles/
.DS_Store
/firmware/CMakeCache.txt
# Python env/ venv
# Python env/ venv and cache
env/
venv/
**/__pycache__/
*.pyc
# Other
*.bak
# generated bitmap arr file
# TODO: generate bitmap during build, since we use python during build anyway, lemme know if this is a bad idea @zxkmm
/firmware/tools/bitmap.hpp
+148
View File
@@ -0,0 +1,148 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) JTAG probe",
"type": "cppdbg",
"request": "launch",
"miDebuggerPath": "arm-none-eabi-gdb",
"targetArchitecture": "arm",
"program": "${workspaceRoot}/build/firmware/baseband/baseband_adsbrx.elf",
"cwd": "${workspaceRoot}",
"setupCommands": [
// use logging for troubleshooting
//{"text": "set logging file ${workspaceRoot}/build/arm-none-eabi-gdb.log"},
//{"text": "set logging on"},
{
"text": "file '${workspaceRoot}/build/firmware/baseband/baseband_adsbrx.elf'"
},
{
"text": "target extended-remote /dev/ttyACM0"
},
{
"text": "monitor swdp_scan"
},
{
"text": "attach 1"
},
],
"launchCompleteCommand": "None",
"externalConsole": false,
},
{
"name": "(gdb) OpenOCD m4 baseband",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/build/firmware/baseband/baseband_sd_over_usb.elf",
"args": [],
"cwd": "${workspaceRoot}",
// "environment": [
// {
// "name": "PATH",
// "value": "${env:PATH}"
// }
// ],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "arm-none-eabi-gdb",
"targetArchitecture": "arm",
"debugServerPath": "openocd",
"debugServerArgs": "-f interface/ftdi/um232h.cfg -f target/lpc4350.cfg -c \"gdb_breakpoint_override hard\"",
"serverStarted": "Listening on port [0-9]+ for gdb connections",
"filterStderr": true,
"filterStdout": false,
"launchCompleteCommand": "None",
"postRemoteConnectCommands": [
{
"description": "Target Remote Device on Port 3333",
"text": "target extended-remote :3333",
"ignoreFailures": false
},
{
"description": "Respect Hardware Limitations",
"text": "set remote hardware-watchpoint-limit 2",
"ignoreFailures": false
},
{
"description": "Respect Hardware Limitations",
"text": "set remote hardware-breakpoint-limit 4",
"ignoreFailures": false
},
{
"description": "Shutdown GDB Server on GDB Detach",
"text": "monitor [target current] configure -event gdb-detach { shutdown }",
"ignoreFailures": false
},
],
"stopAtConnect": false,
"logging": {
"exceptions": true,
"engineLogging": false,
"moduleLoad": true,
"programOutput": true,
"trace": false,
"traceResponse": false
},
"useExtendedRemote": true
},
{
"name": "(gdb) OpenOCD m0 application",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/build/firmware/application/application.elf",
"args": [],
"cwd": "${workspaceRoot}",
// "environment": [
// {
// "name": "PATH",
// "value": "${env:PATH}"
// }
// ],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "arm-none-eabi-gdb",
"targetArchitecture": "arm",
"debugServerPath": "openocd",
"debugServerArgs": "-f interface/ftdi/um232h.cfg -f target/lpc4350.cfg -c \"gdb_breakpoint_override hard\"",
"serverStarted": "Listening on port [0-9]+ for gdb connections",
"filterStderr": true,
"filterStdout": false,
"launchCompleteCommand": "None",
"postRemoteConnectCommands": [
{
"description": "Target Remote Device on Port 3334",
"text": "target extended-remote :3334",
"ignoreFailures": false
},
{
"description": "Respect Hardware Limitations",
"text": "set remote hardware-watchpoint-limit 1",
"ignoreFailures": false
},
{
"description": "Respect Hardware Limitations",
"text": "set remote hardware-breakpoint-limit 2",
"ignoreFailures": false
},
{
"description": "Shutdown GDB Server on GDB Detach",
"text": "monitor [target current] configure -event gdb-detach { shutdown }",
"ignoreFailures": false
},
],
"stopAtConnect": false,
"logging": {
"exceptions": true,
"engineLogging": false,
"moduleLoad": true,
"programOutput": true,
"trace": false,
"traceResponse": false
},
"useExtendedRemote": true
},
]
}
+7 -6
View File
@@ -72,12 +72,13 @@ set(HACKRF_CPLD_XSVF_PATH ${HACKRF_PATH}/firmware/cpld/sgpio_if/${HACKRF_CPLD_XS
set(HACKRF_FIRMWARE_DFU_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_DFU_FILENAME})
set(HACKRF_FIRMWARE_BIN_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_BIN_FILENAME})
find_program(CCACHE "ccache")
if(CCACHE)
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros)
endif(CCACHE)
# this seems causing some issues (ref. in discord discussions), so temporarily disabled, until figure out the pros and cons and bugs of this tool.
#find_program(CCACHE "ccache")
#if(CCACHE)
# set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
# set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
# set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros)
#endif(CCACHE)
enable_testing()
add_subdirectory(firmware)
+12 -16
View File
@@ -1,5 +1,7 @@
> [!IMPORTANT]
> this repository **has just been moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please keep this in mind to **update your links** accordingly.
> [!WARNING]
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED VERSIONS, YOU'RE BEING SCAMMED.__
>
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.
# PortaPack Mayhem
@@ -9,15 +11,15 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
*[PortaPack H2+HackRF+battery](https://grabify.link/7T28JP) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
# What is this?
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[![Beginner's Guide To The HackRF & Portapak With Mayhem](https://img.youtube.com/vi/H-bqdWfbhpg/0.jpg)](https://www.youtube.com/watch?v=H-bqdWfbhpg)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="700">](https://grabify.link/X4D5TF)
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="230">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="230">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="230">](https://grabify.link/C0J6ZR)
# Frequently Asked Questions
@@ -25,11 +27,11 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/VPMPSL), featuring numerous improvements and accessories. Sold by our friends at OpenSourceSDRLab. Join their giveaways on discord (check the badge on top).
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of their [PortaPack H2](https://www.aliexpress.com/item/4000247041639.html?gatewayAdapt=4itemAdapt), you can support them too by ordering.
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/7T28JP), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/HTDXG5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
: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:
@@ -53,13 +55,7 @@ Consider that the hardware and firmware has been created and maintain by a [lot]
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).
## What if I really want something specific?
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md).
<del>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/portapack-mayhem/mayhem-firmware/issues/new/choose) or [choosing](https://github.com/portapack-mayhem/mayhem-firmware/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).</del>
<del>Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).</del>
Bountysource has not been reliable lately, so until this changes, please **DO NOT** post a bounty there. Go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md). Alternatively, go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
## What if I need help?
First, check the [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose).
+4 -2
View File
@@ -50,6 +50,7 @@ endif()
add_subdirectory(application)
add_subdirectory(baseband)
add_subdirectory(standalone)
add_subdirectory(test)
# NOTE: Dependencies break if the .bin files aren't included in DEPENDS. WTF, CMake?
@@ -88,7 +89,7 @@ add_custom_target(
program-external-apps
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${PROJECT_SOURCE_DIR}/tools/copy_external_apps.sh
DEPENDS program
DEPENDS program standalone_apps
)
add_custom_command(
@@ -100,8 +101,9 @@ add_custom_command(
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
COMMAND mkdir -p firmware_tar/APPS
COMMAND cp application/*.ppma firmware_tar/APPS
COMMAND cp standalone/*/*.ppmp firmware_tar/APPS
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
DEPENDS firmware ${FIRMWARE_FILENAME}
DEPENDS firmware ${FIRMWARE_FILENAME} standalone_apps
# Dont use VERBATIM here as it prevents usage of globbing (*)
# There shouldnt be any funny business in the filenames above :)
)
+18 -6
View File
@@ -176,7 +176,11 @@ set(CPPSRC
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/ads1110.cpp
${COMMON}/max17055.cpp
${COMMON}/battery.cpp
${COMMON}/performance_counter.cpp
${COMMON}/bmpfile.cpp
app_settings.cpp
audio.cpp
baseband_api.cpp
@@ -190,6 +194,7 @@ set(CPPSRC
event_m0.cpp
file_reader.cpp
file.cpp
file_path.cpp
freqman_db.cpp
freqman.cpp
io_convert.cpp
@@ -201,6 +206,7 @@ set(CPPSRC
irq_rtc.cpp
log_file.cpp
metadata_file.cpp
flipper_subfile.cpp
portapack.cpp
usb_serial_shell.cpp
usb_serial_shell_filesystem.cpp
@@ -208,6 +214,7 @@ set(CPPSRC
usb_serial_thread.cpp
usb_serial.cpp
usb_serial_host_to_device.cpp
usb_serial_asyncmsg.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
@@ -220,6 +227,7 @@ set(CPPSRC
spectrum_color_lut.cpp
string_format.cpp
temperature_logger.cpp
theme.cpp
touch.cpp
tone_key.cpp
transmitter_model.cpp
@@ -253,11 +261,11 @@ set(CPPSRC
ui/ui_freqlist.cpp
ui/ui_tv.cpp
ui/ui_spectrum.cpp
ui/ui_styles.cpp
ui/ui_tabview.cpp
ui/ui_textentry.cpp
ui/ui_tone_key.cpp
ui/ui_transmitter.cpp
ui/ui_bmpview.cpp
apps/acars_app.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
@@ -272,17 +280,19 @@ set(CPPSRC
apps/pocsag_app.cpp
# apps/replay_app.cpp
apps/soundboard_app.cpp
apps/tpms_app.cpp
apps/tpms_app.cpp
# apps/tpms_app.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp
# apps/ui_adsb_tx.cpp #moved to ext
apps/ui_aprs_rx.cpp
apps/ui_aprs_tx.cpp
apps/ui_battinfo.cpp
apps/ui_bht_tx.cpp
apps/ui_bmp_file_viewer.cpp
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_debug_battery.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
apps/ui_fileman.cpp
@@ -297,7 +307,7 @@ set(CPPSRC
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
# apps/ui_morse.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
@@ -317,7 +327,8 @@ set(CPPSRC
# apps/ui_spectrum_painter_text.cpp
# apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
# apps/ui_sstvtx.cpp #moved to ext
apps/ui_standalone_view.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
apps/ui_text_editor.cpp
@@ -500,6 +511,7 @@ include_directories(. ${INCDIR})
link_directories(${LLIBDIR})
target_link_libraries(${PROJECT_NAME}.elf ${LIBS} "-L${CMAKE_CURRENT_LIST_DIR}/external")
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
target_link_libraries(${PROJECT_NAME}.elf -Wl,--print-memory-usage)
add_custom_command(
OUTPUT ${PROJECT_NAME}.bin
+5 -8
View File
@@ -30,6 +30,7 @@
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include <algorithm>
#include <cstring>
@@ -40,7 +41,7 @@ using namespace portapack;
namespace {
fs::path get_settings_path(const std::string& app_name) {
return fs::path{SETTINGS_DIR} / app_name + u".ini";
return settings_dir / app_name + u".ini";
}
} // namespace
@@ -156,7 +157,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
File f;
auto path = get_settings_path(std::string{store_name});
ensure_directory(SETTINGS_DIR);
ensure_directory(settings_dir);
auto error = f.create(path);
if (error)
return false;
@@ -174,16 +175,12 @@ void copy_to_radio_model(const AppSettings& settings) {
// Specifically 'modulation' which requires a running baseband.
if (flags_enabled(settings.mode, Mode::TX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.tx_frequency);
persistent_memory::set_target_frequency(settings.tx_frequency);
transmitter_model.configure_from_app_settings(settings);
}
if (flags_enabled(settings.mode, Mode::RX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.rx_frequency);
persistent_memory::set_target_frequency(settings.rx_frequency);
receiver_model.configure_from_app_settings(settings);
receiver_model.set_configuration_without_update(
static_cast<ReceiverModel::Mode>(settings.modulation),
-6
View File
@@ -36,9 +36,6 @@
#include "max283x.hpp"
#include "string_format.hpp"
// Folder to store app settings, pmem_fileflag, and date_fileflag
#define SETTINGS_DIR u"/SETTINGS"
// Bring in the string_view literal.
using std::literals::operator""sv;
@@ -121,9 +118,6 @@ enum class Mode : uint8_t {
enum class Options {
None = 0x0000,
/* Don't use target frequency from app settings. */
UseGlobalTargetFrequency = 0x0001,
};
/* NB: See RX/TX model headers for default values. */
+2 -1
View File
@@ -24,6 +24,7 @@
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
using namespace portapack;
using namespace acars;
@@ -77,7 +78,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
logger = std::make_unique<ACARSLogger>();
if (logger)
logger->append(LOG_ROOT_DIR "/ACARS.TXT");
logger->append(logs_dir / u"ACARS.TXT");
}
ACARSAppView::~ACARSAppView() {
+1 -1
View File
@@ -35,7 +35,7 @@
class ACARSLogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
+16 -1
View File
@@ -20,9 +20,11 @@
*/
#include "ais_app.hpp"
#include "audio.hpp"
#include "string_format.hpp"
#include "database.hpp"
#include "file_path.hpp"
#include "baseband_api.hpp"
@@ -31,6 +33,8 @@ using namespace portapack;
#include <algorithm>
namespace pmem = portapack::persistent_memory;
namespace ais {
namespace format {
@@ -189,6 +193,10 @@ void AISLogger::on_packet(const ais::Packet& packet) {
}
log_file.write_entry(packet.received_at(), entry);
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void AISRecentEntry::update(const ais::Packet& packet) {
@@ -376,6 +384,7 @@ AISAppView::AISAppView(NavigationView& nav)
&field_lna,
&field_vga,
&rssi,
&field_volume,
&channel,
&recent_entries_view,
&recent_entry_detail_view,
@@ -399,11 +408,17 @@ AISAppView::AISAppView(NavigationView& nav)
logger = std::make_unique<AISLogger>();
if (logger) {
logger->append(LOG_ROOT_DIR "/AIS.TXT");
logger->append(logs_dir / u"AIS.TXT");
}
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
AISAppView::~AISAppView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
+3
View File
@@ -209,6 +209,9 @@ class AISAppView : public View {
{21 * 8, 0, 6 * 8, 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
+41 -16
View File
@@ -30,6 +30,7 @@
#include "string_format.hpp"
#include "ui_freqman.hpp"
#include "utility.hpp"
#include "radio.hpp"
using namespace portapack;
using namespace tonekey;
@@ -38,8 +39,6 @@ namespace ui {
/* AMOptionsView *********************************************************/
static const Style& style_options_group = Styles::bg_blue;
AMOptionsView::AMOptionsView(
Rect parent_rect,
const Style* style)
@@ -112,10 +111,14 @@ SPECOptionsView::SPECOptionsView(
: View{parent_rect} {
set_style(style);
add_children({&label_config,
&options_config,
&text_speed,
&field_speed});
add_children({
&label_config,
&options_config,
&text_speed,
&field_speed,
&text_rx_cal,
&field_rx_iq_phase_cal,
});
options_config.set_selected_index(view->get_spec_bw_index());
options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) {
@@ -126,6 +129,14 @@ SPECOptionsView::SPECOptionsView(
field_speed.on_change = [this, view](int32_t v) {
view->set_spec_trigger(v);
};
field_rx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
field_rx_iq_phase_cal.set_value(view->get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini
field_rx_iq_phase_cal.on_change = [this, view](int32_t v) {
view->set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini
};
view->set_spec_iq_phase_calibration_value(view->get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
}
/* AnalogAudioView *******************************************************/
@@ -195,7 +206,9 @@ AnalogAudioView::AnalogAudioView(
NavigationView& nav,
ReceiverModel::settings_t override)
: AnalogAudioView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
// TODO: Which other settings make sense to override?
field_frequency.set_value(override.frequency_app_override);
on_frequency_step_changed(override.frequency_step);
options_modulation.set_by_value(toUType(override.mode));
}
@@ -213,6 +226,15 @@ void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
receiver_model.set_baseband_bandwidth(bw / 2);
}
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
return iq_phase_calibration_value;
}
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
iq_phase_calibration_value = cal_value;
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
}
uint16_t AnalogAudioView::get_spec_trigger() {
return spec_trigger;
}
@@ -273,13 +295,13 @@ void AnalogAudioView::set_options_widget(std::unique_ptr<Widget> new_widget) {
options_widget = std::move(new_widget);
} else {
// TODO: Lame hack to hide options view due to my bad paint/damage algorithm.
options_widget = std::make_unique<Rectangle>(options_view_rect, style_options_group.background);
options_widget = std::make_unique<Rectangle>(options_view_rect, Theme::getInstance()->option_active->background);
}
add_child(options_widget.get());
}
void AnalogAudioView::on_show_options_frequency() {
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group);
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, Theme::getInstance()->option_active);
widget->set_step(receiver_model.frequency_step());
widget->on_change_step = [this](rf::Frequency f) {
@@ -291,14 +313,14 @@ void AnalogAudioView::on_show_options_frequency() {
};
set_options_widget(std::move(widget));
field_frequency.set_style(&style_options_group);
field_frequency.set_style(Theme::getInstance()->option_active);
}
void AnalogAudioView::on_show_options_rf_gain() {
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, Theme::getInstance()->option_active);
set_options_widget(std::move(widget));
field_lna.set_style(&style_options_group);
field_lna.set_style(Theme::getInstance()->option_active);
}
void AnalogAudioView::on_show_options_modulation() {
@@ -307,25 +329,25 @@ void AnalogAudioView::on_show_options_modulation() {
const auto modulation = receiver_model.modulation();
switch (modulation) {
case ReceiverModel::Mode::AMAudio:
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
widget = std::make_unique<AMOptionsView>(options_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(true);
break;
case ReceiverModel::Mode::NarrowbandFMAudio:
widget = std::make_unique<NBFMOptionsView>(nbfm_view_rect, &style_options_group);
widget = std::make_unique<NBFMOptionsView>(nbfm_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(false);
break;
case ReceiverModel::Mode::WidebandFMAudio:
widget = std::make_unique<WFMOptionsView>(options_view_rect, &style_options_group);
widget = std::make_unique<WFMOptionsView>(options_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(true);
text_ctcss.hidden(true);
break;
case ReceiverModel::Mode::SpectrumAnalysis:
widget = std::make_unique<SPECOptionsView>(this, nbfm_view_rect, &style_options_group);
widget = std::make_unique<SPECOptionsView>(this, nbfm_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(true);
break;
@@ -336,7 +358,7 @@ void AnalogAudioView::on_show_options_modulation() {
}
set_options_widget(std::move(widget));
options_modulation.set_style(&style_options_group);
options_modulation.set_style(Theme::getInstance()->option_active);
}
void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) {
@@ -413,4 +435,7 @@ void AnalogAudioView::handle_coded_squelch(uint32_t value) {
text_ctcss.set(tone_key_string_by_value(value, text_ctcss.parent_rect().width() / 8));
}
void AnalogAudioView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+28 -3
View File
@@ -29,7 +29,6 @@
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp"
#include "ui_record_view.hpp"
#include "ui_styles.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "tone_key.hpp"
@@ -133,6 +132,16 @@ class SPECOptionsView : public View {
1,
' ',
};
Text text_rx_cal{
{19 * 8, 0 * 16, 11 * 8, 1 * 16}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars.
"Rx_IQ_CAL "};
NumberField field_rx_iq_phase_cal{
{28 * 8, 0 * 16},
2,
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
1,
' ',
};
};
class AnalogAudioView : public View {
@@ -152,14 +161,21 @@ class AnalogAudioView : public View {
uint16_t get_spec_trigger();
void set_spec_trigger(uint16_t trigger);
uint8_t get_spec_iq_phase_calibration_value();
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
app_settings::SettingsManager settings_{
"rx_audio", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
"rx_audio",
app_settings::Mode::RX,
{
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
}};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
@@ -231,12 +247,21 @@ class AnalogAudioView : public View {
void handle_coded_squelch(uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} /* namespace ui */
+2 -1
View File
@@ -32,6 +32,7 @@
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_text.hpp"
#include "file_path.hpp"
using namespace portapack;
using namespace modems;
@@ -100,7 +101,7 @@ BLECommView::BLECommView(NavigationView& nav)
logging = v;
if (logger && logging)
logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
logger->append(logs_dir.string() + "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
options_channel.on_change = [this](size_t, int32_t i) {
+5 -5
View File
@@ -42,7 +42,7 @@
class BLECommLogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -148,13 +148,13 @@ class BLECommView : public View {
{24 * 8, 5, 6 * 8, 4}};
Labels label_send_adv{
{{0 * 8, 2 * 8}, "Send Advertisement:", Color::light_grey()}};
{{0 * 8, 2 * 8}, "Send Advertisement:", Theme::getInstance()->fg_light->foreground}};
ImageButton button_send_adv{
{21 * 8, 1 * 16, 10 * 8, 2 * 16},
&bitmap_play,
Color::green(),
Color::black()};
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Checkbox check_log{
{24 * 8, 2 * 8},
@@ -163,7 +163,7 @@ class BLECommView : public View {
true};
Labels label_packets_sent{
{{0 * 8, 4 * 8}, "Packets Left:", Color::light_grey()}};
{{0 * 8, 4 * 8}, "Packets Left:", Theme::getInstance()->fg_light->foreground}};
Text text_packets_sent{
{13 * 8, 2 * 16, 12 * 8, 16},
+40 -14
View File
@@ -428,6 +428,7 @@ BLERxView::BLERxView(NavigationView& nav)
&text_found_count,
&check_serial_log,
&button_filter,
&options_filter,
&button_save_list,
&button_clear_list,
&button_switch,
@@ -470,10 +471,7 @@ BLERxView::BLERxView(NavigationView& nav)
logging = v;
if (logger && logging)
logger->append(
"BLERX/Logs"
"/BLELOG_" +
to_string_timestamp(rtc_time::now()) + ".TXT");
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_log.set_value(logging);
@@ -502,6 +500,7 @@ BLERxView::BLERxView(NavigationView& nav)
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
// update the include_name instance variable value of each entry in recent entries
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
@@ -528,8 +527,14 @@ BLERxView::BLERxView(NavigationView& nav)
handle_entries_sort(v);
};
options_filter.on_change = [this](size_t index, int32_t v) {
filter_index = (uint8_t)index;
handle_filter_options(v);
};
options_channel.set_selected_index(channel_index, true);
options_sort.set_selected_index(sort_index, true);
options_filter.set_selected_index(filter_index, true);
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
@@ -719,10 +724,11 @@ void BLERxView::on_data(BlePacketData* packet) {
updateEntry(packet, entry, (ADV_PDU_TYPE)packet->type);
// Add entries if they meet the criteria.
auto value = filter;
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// auto value = filter;
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// });
handle_filter_options(options_filter.selected_index());
handle_entries_sort(options_sort.selected_index());
@@ -759,16 +765,17 @@ void BLERxView::on_data(BlePacketData* packet) {
void BLERxView::on_filter_change(std::string value) {
// New filter? Reset list from recent entries.
if (filter != value) {
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos) && (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
// });
filter = value;
handle_filter_options(options_filter.selected_index());
}
filter = value;
}
void BLERxView::on_file_changed(const std::filesystem::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
found_count = 0;
total_count = 0;
searchList.clear();
@@ -855,6 +862,24 @@ void BLERxView::handle_entries_sort(uint8_t index) {
recent_entries_view.set_dirty();
}
void BLERxView::handle_filter_options(uint8_t index) {
auto value = filter;
switch (index) {
case 0: // filter by Data
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
break;
case 1: // filter by MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF)
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
});
break;
default:
break;
}
}
void BLERxView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height};
@@ -917,6 +942,7 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
// Subtract 1 because type is part of the length.
for (int i = 0; i < length - 1; i++) {
// parse the name of bluetooth device: 0x08->Shortened Local Name; 0x09->Complete Local Name
if (type == 0x08 || type == 0x09) {
decoded_data += (char)advertiseData->Data[currentByte];
}
+35 -25
View File
@@ -36,12 +36,13 @@
#include "log_file.hpp"
#include "utility.hpp"
#include "usb_serial_thread.hpp"
#include "file_path.hpp"
#include "recent_entries.hpp"
class BLELogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -133,28 +134,28 @@ class BleRecentEntryDetailView : public View {
void on_save_file(const std::string value, BLETxPacket packetToSave);
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
std::string packetFileBuffer{};
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
static constexpr uint8_t total_data_lines{5};
Labels label_mac_address{
{{0 * 8, 0 * 16}, "Mac Address:", Color::light_grey()}};
{{0 * 8, 0 * 16}, "Mac Address:", Theme::getInstance()->fg_light->foreground}};
Text text_mac_address{
{12 * 8, 0 * 16, 17 * 8, 16},
"-"};
Labels label_pdu_type{
{{0 * 8, 1 * 16}, "PDU Type:", Color::light_grey()}};
{{0 * 8, 1 * 16}, "PDU Type:", Theme::getInstance()->fg_light->foreground}};
Text text_pdu_type{
{9 * 8, 1 * 16, 17 * 8, 16},
"-"};
Labels labels{
{{0 * 8, 3 * 16}, "Len", Color::light_grey()},
{{5 * 8, 3 * 16}, "Type", Color::light_grey()},
{{10 * 8, 3 * 16}, "Value", Color::light_grey()},
{{0 * 8, 3 * 16}, "Len", Theme::getInstance()->fg_light->foreground},
{{5 * 8, 3 * 16}, "Type", Theme::getInstance()->fg_light->foreground},
{{10 * 8, 3 * 16}, "Value", Theme::getInstance()->fg_light->foreground},
};
Button button_send{
@@ -202,6 +203,7 @@ class BLERxView : public View {
void file_error();
void on_timer();
void handle_entries_sort(uint8_t index);
void handle_filter_options(uint8_t index);
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type);
NavigationView& nav_;
@@ -213,6 +215,7 @@ class BLERxView : public View {
uint8_t channel_index{0};
uint8_t sort_index{0};
uint8_t filter_index{0};
std::string filter{};
bool logging{false};
bool serial_logging{false};
@@ -226,6 +229,7 @@ class BLERxView : public View {
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
{"filter_index"sv, &filter_index},
// disabled to always start without USB serial activated until we can make it non blocking if not connected
// {"serial_log"sv, &serial_logging},
{"name"sv, &name_enable},
@@ -250,17 +254,17 @@ class BLERxView : public View {
uint64_t total_count = 0;
std::vector<std::string> searchList{};
std::filesystem::path find_packet_path{u"BLERX/Find/????.TXT"};
std::filesystem::path log_packets_path{u"BLERX/Logs/????.TXT"};
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
std::filesystem::path find_packet_path{blerx_dir / u"Find/????.TXT"};
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
static constexpr auto header_height = 4 * 16;
static constexpr auto header_height = 9 * 8;
static constexpr auto switch_button_height = 3 * 16;
OptionsField options_channel{
{0 * 8, 0 * 8},
5,
{{"Ch.37 ", 37},
{{"Ch.37", 37},
{"Ch.38", 38},
{"Ch.39", 39},
{"Auto", 40}}};
@@ -285,10 +289,10 @@ class BLERxView : public View {
{24 * 8, 5, 6 * 8, 4}};
Labels label_sort{
{{0 * 8, 3 * 8}, "Sort:", Color::light_grey()}};
{{0 * 8, 2 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_sort{
{5 * 8, 3 * 8},
{5 * 8, 2 * 8},
4,
{{"MAC", 0},
{"Hits", 1},
@@ -297,40 +301,46 @@ class BLERxView : public View {
{"Name", 4}}};
Button button_filter{
{11 * 8, 3 * 8, 4 * 8, 16},
"Filter"};
{11 * 8, 2 * 8, 7 * 8, 16},
"Filter:"};
OptionsField options_filter{
{18 * 8 + 2, 2 * 8},
4,
{{"Data", 0},
{"MAC", 1}}};
Checkbox check_log{
{17 * 8, 3 * 8},
{10 * 8, 4 * 8 + 2},
3,
"Log",
true};
Checkbox check_name{
{23 * 8, 3 * 8},
{0 * 8, 4 * 8 + 2},
3,
"Name",
true};
Button button_find{
{0 * 8, 6 * 8, 4 * 8, 16},
{0 * 8, 7 * 8 - 2, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 6 * 8}, "Found:", Color::light_grey()}};
{{5 * 8, 7 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}};
Text text_found_count{
{11 * 8, 3 * 16, 20 * 8, 16},
{11 * 8, 7 * 8 - 2, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{17 * 8, 3 * 16 - 2},
{18 * 8 + 2, 4 * 8 + 2},
7,
"USB Log",
true};
Console console{
{0, 4 * 16, 240, 240}};
// Console console{
// {0, 10 * 8, 240, 240}};
Button button_clear_list{
{2 * 8, 320 - (16 + 32), 7 * 8, 32},
@@ -370,7 +380,7 @@ class BLERxView : public View {
[this](const Message* const) {
this->on_timer();
}};
};
}; /* BLERxView */
} /* namespace ui */
+2 -1
View File
@@ -34,6 +34,7 @@
#include "portapack_persistent_memory.hpp"
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
using namespace portapack;
using namespace modems;
@@ -448,7 +449,7 @@ BLETxView::BLETxView(
}
void BLETxView::on_file_changed(const fs::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
num_packets = 0;
{ // Get the size of the data file.
+12 -11
View File
@@ -36,6 +36,7 @@
#include "replay_thread.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include "recent_entries.hpp"
@@ -44,7 +45,7 @@
class BLELoggerTx {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -138,7 +139,7 @@ class BLETxView : public View {
uint32_t prev_value{0};
std::filesystem::path file_path{};
std::filesystem::path packet_save_path{u"BLETX/BLETX_????.TXT"};
std::filesystem::path packet_save_path{bletx_dir / u"BLETX_????.TXT"};
uint8_t channel_number = 37;
bool auto_channel = false;
@@ -165,7 +166,7 @@ class BLETxView : public View {
std::unique_ptr<FileWrapper> dataFileWrapper{};
File dataFile{};
std::filesystem::path dataTempFilePath{u"BLETX/dataFileTemp.TXT"};
std::filesystem::path dataTempFilePath{bletx_dir / u"dataFileTemp.TXT"};
std::vector<uint16_t> markedBytes{};
CursorPos cursor_pos{};
uint8_t marked_counter = 0;
@@ -217,11 +218,11 @@ class BLETxView : public View {
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Color::green(),
Color::black()};
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Labels label_speed{
{{0 * 8, 6 * 8}, "Speed:", Color::light_grey()}};
{{0 * 8, 6 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_speed{
{7 * 8, 6 * 8},
@@ -253,7 +254,7 @@ class BLETxView : public View {
{"CONNECT_REQ", PKT_TYPE_CONNECT_REQ}}};
Labels label_marked_data{
{{0 * 8, 4 * 16}, "Marked Data:", Color::light_grey()}};
{{0 * 8, 4 * 16}, "Marked Data:", Theme::getInstance()->fg_light->foreground}};
OptionsField marked_data_sequence{
{12 * 8, 8 * 8},
@@ -263,28 +264,28 @@ class BLETxView : public View {
{"Random", 2}}};
Labels label_packet_index{
{{0 * 8, 12 * 8}, "Packet Index:", Color::light_grey()}};
{{0 * 8, 12 * 8}, "Packet Index:", Theme::getInstance()->fg_light->foreground}};
Text text_packet_index{
{13 * 8, 6 * 16, 12 * 8, 16},
"-"};
Labels label_packets_sent{
{{0 * 8, 14 * 8}, "Repeat Count:", Color::light_grey()}};
{{0 * 8, 14 * 8}, "Repeat Count:", Theme::getInstance()->fg_light->foreground}};
Text text_packets_sent{
{13 * 8, 7 * 16, 12 * 8, 16},
"-"};
Labels label_mac_address{
{{0 * 8, 16 * 8}, "Mac Address:", Color::light_grey()}};
{{0 * 8, 16 * 8}, "Mac Address:", Theme::getInstance()->fg_light->foreground}};
Text text_mac_address{
{12 * 8, 8 * 16, 20 * 8, 16},
"-"};
Labels label_data_packet{
{{0 * 8, 9 * 16}, "Packet Data:", Color::light_grey()}};
{{0 * 8, 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}};
Console console{
{0, 9 * 18, 240, 240}};
+12
View File
@@ -104,6 +104,14 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
};
}
CaptureAppView::CaptureAppView(
NavigationView& nav,
ReceiverModel::settings_t override)
: CaptureAppView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
field_frequency.set_value(override.frequency_app_override);
}
CaptureAppView::~CaptureAppView() {
receiver_model.disable();
baseband::shutdown();
@@ -120,4 +128,8 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+15 -5
View File
@@ -31,12 +31,14 @@
#include "ui_spectrum.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "file_path.hpp"
namespace ui {
class CaptureAppView : public View {
public:
CaptureAppView(NavigationView& nav);
CaptureAppView(NavigationView& nav, ReceiverModel::settings_t override);
~CaptureAppView();
void focus() override;
@@ -51,12 +53,11 @@ class CaptureAppView : public View {
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::Capture};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
"rx_capture", app_settings::Mode::RX};
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
{{11 * 8, 1 * 16}, "Format:", Color::light_grey()},
{{0 * 8, 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 1 * 16}, "Format:", Theme::getInstance()->fg_light->foreground},
};
RSSI rssi{
@@ -101,12 +102,21 @@ class CaptureAppView : public View {
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
u"BBD_????.*",
u"CAPTURES",
captures_dir,
RecordView::FileType::RawS16,
16384,
3};
spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui */
+20 -2
View File
@@ -23,7 +23,7 @@
#include "ert_app.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
@@ -31,6 +31,9 @@ using namespace portapack;
#include "crc.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
namespace pmem = portapack::persistent_memory;
namespace ert {
@@ -120,6 +123,7 @@ ERTAppView::ERTAppView(NavigationView& nav)
&field_lna,
&field_vga,
&rssi,
&field_volume,
&recent_entries_view,
});
@@ -129,11 +133,17 @@ ERTAppView::ERTAppView(NavigationView& nav)
logger = std::make_unique<ERTLogger>();
if (logger) {
logger->append(LOG_ROOT_DIR "/ERT.TXT");
logger->append(logs_dir / u"ERT.TXT");
}
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
ERTAppView::~ERTAppView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -157,6 +167,10 @@ void ERTAppView::on_packet(const ert::Packet& packet) {
entry.update(packet);
recent_entries_view.set_dirty();
}
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void ERTAppView::on_show_list() {
@@ -164,4 +178,8 @@ void ERTAppView::on_show_list() {
recent_entries_view.focus();
}
void ERTAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+11
View File
@@ -165,6 +165,9 @@ class ERTAppView : public View {
{21 * 8, 0, 6 * 8, 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_packet{
Message::ID::ERTPacket,
[this](Message* const p) {
@@ -173,6 +176,14 @@ class ERTAppView : public View {
this->on_packet(packet);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void on_packet(const ert::Packet& packet);
void on_show_list();
};
+27 -14
View File
@@ -27,6 +27,7 @@
#include "portapack_persistent_memory.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include "file_path.hpp"
using namespace portapack;
using namespace pocsag;
@@ -115,7 +116,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
: FILTER_DROP;
}
logger.append(LOG_ROOT_DIR "/POCSAG.TXT");
logger.append(logs_dir / u"POCSAG.TXT");
field_squelch.set_value(receiver_model.squelch_level());
field_squelch.on_change = [this](int32_t v) {
@@ -135,6 +136,10 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
};
refresh_ui();
if (pmem::beep_on_packets())
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.enable();
baseband::set_pocsag();
@@ -158,8 +163,8 @@ void POCSAGAppView::refresh_ui() {
// Set console font style.
console.set_style(
settings_.enable_small_font
? &Styles::white_small
: &Styles::white);
? Theme::getInstance()->bg_darkest_small
: Theme::getInstance()->bg_darkest);
// Update filter button text.
std::string btn_text = "Filter Last";
@@ -255,19 +260,19 @@ void POCSAGAppView::handle_decoded(Timestamp timestamp, const std::string& prefi
static Color get_status_color(const POCSAGState& state) {
if (state.out_type == IDLE)
return Color::white();
return Theme::getInstance()->bg_darkest->foreground;
switch (state.mode) {
case STATE_CLEAR:
return Color::cyan();
return Theme::getInstance()->fg_cyan->foreground;
case STATE_HAVE_ADDRESS:
return Color::yellow();
return Theme::getInstance()->fg_yellow->foreground;
case STATE_GETTING_MSG:
return Color::green();
return Theme::getInstance()->fg_green->foreground;
}
// Shouldn't get here...
return Color::red();
return Theme::getInstance()->fg_red->foreground;
}
void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
@@ -289,7 +294,7 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
last_address = 0;
} else {
// Set color before to be able to see if decode gets stuck.
image_status.set_foreground(Color::magenta());
image_status.set_foreground(Theme::getInstance()->fg_magenta->foreground);
pocsag_state.codeword_index = 0;
pocsag_state.errors = 0;
@@ -303,6 +308,10 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
// Set status icon color to indicate state machine state.
image_status.set_foreground(get_status_color(pocsag_state));
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) {
@@ -312,6 +321,10 @@ void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) {
widget_frames.set_sync(stats->has_sync);
}
void POCSAGAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void BaudIndicator::paint(Painter& painter) {
auto p = screen_pos();
char top = '-';
@@ -323,8 +336,8 @@ void BaudIndicator::paint(Painter& painter) {
bot = (r % 10) + '0';
}
painter.draw_char(p, Styles::white_small, top);
painter.draw_char({p.x(), p.y() + 8}, Styles::white_small, bot);
painter.draw_char(p, *Theme::getInstance()->bg_darkest_small, top);
painter.draw_char({p.x(), p.y() + 8}, *Theme::getInstance()->bg_darkest_small, bot);
}
void BitsIndicator::paint(Painter&) {
@@ -334,17 +347,17 @@ void BitsIndicator::paint(Painter&) {
int x = p.x() + (i / height);
int y = p.y() + (i % height);
display.draw_pixel({x, y}, is_set ? Color::white() : Color::black());
display.draw_pixel({x, y}, is_set ? Theme::getInstance()->bg_darkest->foreground : Theme::getInstance()->bg_darkest->background);
}
}
void FrameIndicator::paint(Painter& painter) {
auto p = screen_pos();
painter.draw_rectangle({p, {2, height}}, has_sync_ ? Color::green() : Color::grey());
painter.draw_rectangle({p, {2, height}}, has_sync_ ? Theme::getInstance()->fg_green->foreground : Theme::getInstance()->bg_medium->background);
for (size_t i = 0; i < height; ++i) {
auto p2 = p + Point{2, 15 - (int)i};
painter.draw_hline(p2, 2, i < frame_count_ ? Color::white() : Color::black());
painter.draw_hline(p2, 2, i < frame_count_ ? Theme::getInstance()->bg_darkest->foreground : Theme::getInstance()->bg_darkest->background);
}
}
+14 -6
View File
@@ -27,7 +27,6 @@
#include "ui_freq_field.hpp"
#include "ui_receiver.hpp"
#include "ui_rssi.hpp"
#include "ui_styles.hpp"
#include "app_settings.hpp"
#include "log_file.hpp"
@@ -39,7 +38,7 @@
class POCSAGLogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -141,8 +140,8 @@ class POCSAGSettingsView : public View {
POCSAGSettings& settings_;
Labels labels{
{{2 * 8, 12 * 16}, "Filter Mode:", Color::light_grey()},
{{2 * 8, 13 * 16}, "Filter Addr:", Color::light_grey()},
{{2 * 8, 12 * 16}, "Filter Mode:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 13 * 16}, "Filter Addr:", Theme::getInstance()->fg_light->foreground},
};
Checkbox check_log{
@@ -265,8 +264,8 @@ class POCSAGAppView : public View {
Image image_status{
{0 * 8 + 4, 1 * 16 + 2, 16, 16},
&bitmap_icon_pocsag,
Color::white(),
Color::black()};
Theme::getInstance()->bg_darkest->foreground,
Theme::getInstance()->bg_darkest->background};
Text text_packet_count{
{3 * 8, 1 * 16 + 2, 5 * 8, 16},
@@ -293,6 +292,15 @@ class POCSAGAppView : public View {
Console console{
{0, 2 * 16 + 6, screen_width, screen_height - 54}};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::POCSAGPacket,
[this](Message* const p) {
+1 -1
View File
@@ -44,7 +44,7 @@ void ReplayAppView::set_ready() {
}
void ReplayAppView::on_file_changed(const fs::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
File::Size file_size{};
{ // Get the size of the data file.
+2 -2
View File
@@ -112,8 +112,8 @@ class ReplayAppView : public View {
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Color::green(),
Color::black()};
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
spectrum::WaterfallView waterfall{};
+4 -3
View File
@@ -28,6 +28,7 @@
#include "tonesets.hpp"
#include "ui_tone_key.hpp"
#include "audio.hpp"
#include "file_path.hpp"
using namespace tonekey;
using namespace portapack;
@@ -164,7 +165,7 @@ void SoundBoardView::refresh_list() {
// List directories and files, put directories up top
uint32_t count = 0;
for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) {
for (const auto& entry : std::filesystem::directory_iterator(wav_dir, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (entry.path().string().length()) {
auto entry_extension = entry.path().extension().string();
@@ -173,7 +174,7 @@ void SoundBoardView::refresh_list() {
c = toupper(c);
if (entry_extension == ".WAV") {
if (reader->open(u"/WAV/" + entry.path().native())) {
if (reader->open(wav_dir / entry.path())) {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
// sounds[c].path = u"WAV/" + entry.path().native();
@@ -213,7 +214,7 @@ void SoundBoardView::refresh_list() {
for (size_t n = 0; n < file_list.size(); n++) {
menu_view.add_item({file_list[n].string().substr(0, 30),
ui::Color::dark_magenta(),
ui::Theme::getInstance()->fg_magenta->foreground,
nullptr,
[this](KeyEvent) {
on_select_entry();
+2 -2
View File
@@ -92,8 +92,8 @@ class SoundBoardView : public View {
void on_select_entry();
Labels labels{
{{24 * 8, 180}, "Vol:", Color::light_grey()},
{{0, 180}, "Key:", Color::light_grey()}};
{{24 * 8, 180}, "Vol:", Theme::getInstance()->fg_light->foreground},
{{0, 180}, "Key:", Theme::getInstance()->fg_light->foreground}};
Button button_next_page{
{30 * 7, 25 * 8, 10 * 3, 2 * 14},
+2 -2
View File
@@ -71,7 +71,7 @@ void CreditsWidget::new_row(
void CreditsWidget::clear() {
display.fill_rectangle(
screen_rect(),
Color::black());
Theme::getInstance()->bg_darkest->background);
}
void AboutView::update() {
@@ -109,7 +109,7 @@ void AboutView::update() {
const size_t start = (glyph.size().width() / 8) * render_line;
for (Dim c = 0; c < glyph.size().width(); c++) {
const auto pixel = glyph.pixels()[start + (c >> 3)] & (1U << (c & 0x7));
pixel_row[start_pos + i + c] = pixel ? Color::white() : Color::black();
pixel_row[start_pos + i + c] = pixel ? Theme::getInstance()->bg_darkest->foreground : Theme::getInstance()->bg_darkest->background;
}
const auto advance = glyph.advance();
+88 -83
View File
@@ -1,83 +1,88 @@
#include "ui_about_simple.hpp"
namespace ui {
AboutView::AboutView(NavigationView& nav) {
add_children({&console, &button_ok});
button_ok.on_select = [&nav](Button&) {
nav.pop();
};
console.writeln(STR_COLOR_LIGHT_GREY "List of contributors:");
console.writeln("");
}
void AboutView::update() {
if (++timer > 200) {
timer = 0;
switch (++frame) {
case 1:
// TODO: Generate this automatically from github
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c
console.writeln(STR_COLOR_DARK_YELLOW "Mayhem:");
console.writeln("eried,euquiq,gregoryfenton");
console.writeln("johnelder,jwetzell,nnemanjan00");
console.writeln("N0vaPixel,klockee,GullCode");
console.writeln("jamesshao8,ITAxReal,rascafr");
console.writeln("mcules,dqs105,strijar");
console.writeln("zhang00963,RedFox-Fr,aldude999");
console.writeln("East2West,fossum,ArjanOnwezen");
console.writeln("vXxOinvizioNxX,teixeluis");
console.writeln("Brumi-2021,texasyojimbo");
console.writeln("heurist1,intoxsick,ckuethe");
console.writeln("notpike,jLynx,zigad");
console.writeln("MichalLeonBorsuk,jimilinuxguy");
console.writeln("kallanreed,bernd-herzog");
break;
case 2:
console.writeln("NotherNgineer,zxkmm,u-foka");
console.writeln("Netro,HTotoo");
console.writeln("");
break;
case 3:
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
console.writeln(STR_COLOR_DARK_YELLOW "Havoc:");
console.writeln("furrtek,mrmookie,NotPike");
console.writeln("mjwaxios,ImDroided,Giorgiofox");
console.writeln("F4GEV,z4ziggy,xmycroftx");
console.writeln("troussos,silascutler");
console.writeln("nickbouwhuis,msoose,leres");
console.writeln("joakar,dhoetger,clem-42");
console.writeln("brianlechthaler,ZeroChaos-...");
console.writeln("");
break;
case 4:
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c
console.writeln(STR_COLOR_DARK_YELLOW "PortaPack:");
console.writeln("jboone,argilo");
console.writeln("");
break;
case 5:
// https://github.com/mossmann/hackrf/graphs/contributors
console.writeln(STR_COLOR_DARK_YELLOW "HackRF:");
console.writeln("mossmann,dominicgs,bvernoux");
console.writeln("bgamari,schneider42,miek");
console.writeln("willcode,hessu,Sec42");
console.writeln("yhetti,ckuethe,smunaut");
console.writeln("wishi,mrbubble62,scateu...");
console.writeln("");
frame = 0; // Loop
break;
}
}
}
void AboutView::focus() {
button_ok.focus();
}
} /* namespace ui */
#include "ui_about_simple.hpp"
namespace ui {
// TODO: Generate this automatically from github
// Information: a line starting with a '#' will be yellow coloured
const std::string authors_list[] = {
"# * List of contributors * ",
" ",
"#Mayhem:",
"eried,euquiq,gregoryfenton",
"johnelder,jwetzell,nnemanjan00",
"N0vaPixel,klockee,gullradriel",
"jamesshao8,ITAxReal,rascafr",
"mcules,dqs105,strijar",
"zhang00963,RedFox-Fr,aldude999",
"East2West,fossum,ArjanOnwezen",
"vXxOinvizioNxX,teixeluis",
"Brumi-2021,texasyojimbo",
"heurist1,intoxsick,ckuethe",
"notpike,jLynx,zigad",
"MichalLeonBorsuk,jimilinuxguy",
"kallanreed,bernd-herzog",
"NotherNgineer,zxkmm,u-foka",
"Netro,HTotoo",
" ",
"#Havoc:",
"furrtek,mrmookie,NotPike",
"mjwaxios,ImDroided,Giorgiofox",
"F4GEV,z4ziggy,xmycroftx",
"troussos,silascutler",
"nickbouwhuis,msoose,leres",
"joakar,dhoetger,clem-42",
"brianlechthaler,ZeroChaos-...",
" ",
"#PortaPack:",
"jboone,argilo",
" ",
"#HackRF:",
"mossmann,dominicgs,bvernoux",
"bgamari,schneider42,miek",
"willcode,hessu,Sec42",
"yhetti,ckuethe,smunaut",
"wishi,mrbubble62,scateu..."};
AboutView::AboutView(NavigationView& nav) {
add_children({&menu_view,
&button_ok});
button_ok.on_select = [&nav](Button&) {
nav.pop();
};
menu_view.on_left = [this]() {
button_ok.focus();
};
menu_view.on_right = [this]() {
button_ok.focus();
};
for (const std::string& authors_line : authors_list) {
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
if (authors_line.size() > 0) {
if (authors_line[0] == '#') {
menu_view.add_item(
{authors_line.substr(1, authors_line.size() - 1),
ui::Theme::getInstance()->fg_yellow->foreground,
nullptr,
nullptr});
} else {
menu_view.add_item(
{authors_line,
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
}
}
}
void AboutView::focus() {
menu_view.focus();
// put focus on last text line to make it more obvious that list is scrollable
menu_view.set_highlighted(10);
}
} /* namespace ui */
+3 -12
View File
@@ -12,25 +12,16 @@ class AboutView : public View {
AboutView(NavigationView& nav);
void focus() override;
std::string title() const override { return "About"; };
int32_t timer{180};
short frame{0};
private:
void update();
Console console{
{0, 10, 240, 240}};
MenuView menu_view{
{0, 0, 240, 264},
true};
Button button_ok{
{240 / 3, 270, 240 / 3, 24},
"OK",
};
MessageHandlerRegistration message_handler_update{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update();
}};
};
} // namespace ui
+27 -9
View File
@@ -31,9 +31,13 @@
#include "portapack_persistent_memory.hpp"
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
#include "audio.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
namespace ui {
static std::string get_map_tag(const AircraftRecentEntry& entry) {
@@ -53,15 +57,15 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
case ADSBAgeState::Invalid:
case ADSBAgeState::Current:
entry_string = "";
target_color = Color::green();
target_color = Theme::getInstance()->fg_green->foreground;
break;
case ADSBAgeState::Recent:
entry_string = STR_COLOR_LIGHT_GREY;
target_color = Color::light_grey();
target_color = Theme::getInstance()->fg_light->foreground;
break;
default:
entry_string = STR_COLOR_DARK_GREY;
target_color = Color::grey();
target_color = Theme::getInstance()->fg_medium->foreground;
};
entry_string +=
@@ -103,7 +107,8 @@ void ADSBLogger::log(const ADSBLogEntry& log_entry) {
log_line += " Type:" + to_string_dec_uint(log_entry.vel_type) +
" Hdg:" + to_string_dec_uint(log_entry.vel.heading) +
" Spd: " + to_string_dec_int(log_entry.vel.speed);
if (log_entry.sil != 0)
log_line += " Sil:" + to_string_dec_uint(log_entry.sil);
log_file.write_entry(log_line);
}
@@ -350,11 +355,12 @@ void ADSBRxDetailsView::refresh_ui() {
text_callsign.set(entry_.callsign);
text_infos.set(entry_.info_string);
std::string str_sil = (entry_.sil > 0) ? " Sil:" + to_string_dec_uint(entry_.sil) : "";
if (entry_.velo.heading < 360 && entry_.velo.speed >= 0)
text_info2.set("Hdg:" + to_string_dec_uint(entry_.velo.heading) +
" Spd:" + to_string_dec_int(entry_.velo.speed));
" Spd:" + to_string_dec_int(entry_.velo.speed) + str_sil);
else
text_info2.set("");
text_info2.set(str_sil);
text_frame_pos_even.set(to_string_hex_array(entry_.frame_pos_even.get_raw_data(), 14));
text_frame_pos_odd.set(to_string_hex_array(entry_.frame_pos_odd.get_raw_data(), 14));
@@ -372,7 +378,8 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
&rssi,
&recent_entries_view,
&status_frame,
&status_good_frame});
&status_good_frame,
&field_volume});
recent_entries_view.set_parent_rect({0, 16, 240, 272});
recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) {
@@ -390,14 +397,20 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
};
logger = std::make_unique<ADSBLogger>();
logger->append(LOG_ROOT_DIR "/ADSB.TXT");
logger->append(logs_dir / u"ADSB.TXT");
receiver_model.enable();
baseband::set_adsb();
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
ADSBRxView::~ADSBRxView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -460,7 +473,6 @@ void ADSBRxView::on_frame(const ADSBFrameMessage* message) {
"Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_decimal(entry.pos.latitude, 2) +
" Lon:" + to_string_decimal(entry.pos.longitude, 2);
entry.set_info_string(std::move(str_info));
}
@@ -468,10 +480,16 @@ void ADSBRxView::on_frame(const ADSBFrameMessage* message) {
entry.set_frame_velo(frame);
log_entry.vel = entry.velo;
log_entry.vel_type = msg_sub;
} else if (msg_type == AIRBORNE_OP_STATUS) { // for ver 1
entry.sil = frame.get_sil_value();
}
}
logger->log(log_entry);
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void ADSBRxView::on_tick_second() {
+30 -22
View File
@@ -54,6 +54,8 @@ namespace ui {
#define AIRBORNE_POS_GPS_L 20 // airborne position (lowest type id)
#define AIRBORNE_POS_GPS_H 22 // airborne position (highest type id)
#define AIRBORNE_OP_STATUS 31 // Aircraft operation status
#define RESERVED_L 23 // reserved for other uses
#define RESERVED_H 31 // reserved for other uses
@@ -102,6 +104,8 @@ struct AircraftRecentEntry {
std::string callsign{};
std::string info_string{};
uint8_t sil{0}; // Surveillance integrity level
AircraftRecentEntry(const uint32_t ICAO_address)
: ICAO_address{ICAO_address} {
this->icao_str = to_string_hex(ICAO_address, 6);
@@ -173,6 +177,7 @@ struct ADSBLogEntry {
adsb_pos pos{};
adsb_vel vel{};
uint8_t vel_type{};
uint8_t sil{};
};
// TODO: Make logging optional.
@@ -200,15 +205,15 @@ class ADSBRxAircraftDetailsView : public View {
private:
Labels labels{
{{0 * 8, 1 * 16}, "ICAO:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Registration:", Color::light_grey()},
{{0 * 8, 3 * 16}, "Manufacturer:", Color::light_grey()},
{{0 * 8, 5 * 16}, "Model:", Color::light_grey()},
{{0 * 8, 7 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 8 * 16}, "Number of engines:", Color::light_grey()},
{{0 * 8, 9 * 16}, "Engine type:", Color::light_grey()},
{{0 * 8, 11 * 16}, "Owner:", Color::light_grey()},
{{0 * 8, 13 * 16}, "Operator:", Color::light_grey()}};
{{0 * 8, 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Registration:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Manufacturer:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Model:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Type:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Number of engines:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 9 * 16}, "Engine type:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 11 * 16}, "Owner:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, "Operator:", Theme::getInstance()->fg_light->foreground}};
Text text_icao_address{
{5 * 8, 1 * 16, 6 * 8, 16},
@@ -284,13 +289,13 @@ class ADSBRxDetailsView : public View {
bool airline_checked{false};
Labels labels{
{{0 * 8, 1 * 16}, "ICAO:", Color::light_grey()},
{{13 * 8, 1 * 16}, "Callsign:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Last seen:", Color::light_grey()},
{{0 * 8, 3 * 16}, "Airline:", Color::light_grey()},
{{0 * 8, 5 * 16}, "Country:", Color::light_grey()},
{{0 * 8, 13 * 16}, "Even position frame:", Color::light_grey()},
{{0 * 8, 15 * 16}, "Odd position frame:", Color::light_grey()}};
{{0 * 8, 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground},
{{13 * 8, 1 * 16}, "Callsign:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Last seen:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Airline:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Country:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, "Even position frame:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 15 * 16}, "Odd position frame:", Theme::getInstance()->fg_light->foreground}};
Text text_icao_address{
{5 * 8, 1 * 16, 6 * 8, 16},
@@ -409,7 +414,7 @@ class ADSBRxView : public View {
ADSBRxDetailsView* details_view{nullptr};
Labels labels{
{{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Color::light_grey()}};
{{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground}};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
@@ -421,19 +426,22 @@ class ADSBRxView : public View {
{18 * 8, 0 * 16}};
RSSI rssi{
{20 * 8, 4, 10 * 7, 8},
{20 * 8, 4, 7 * 8, 8},
};
ActivityDot status_frame{
{screen_width - 3, 5, 2, 2},
Color::white(),
{27 * 8 + 2, 5, 2, 2},
Theme::getInstance()->bg_darkest->foreground,
};
ActivityDot status_good_frame{
{screen_width - 3, 9, 2, 2},
Color::green(),
{27 * 8 + 2, 9, 2, 2},
Theme::getInstance()->fg_green->foreground,
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_frame{
Message::ID::ADSBFrame,
[this](Message* const p) {
+7 -2
View File
@@ -27,6 +27,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
using namespace portapack;
@@ -45,7 +46,7 @@ void RecentEntriesTable<APRSRecentEntries>::draw(
Color target_color;
// auto entry_age = entry.age;
target_color = Color::green();
target_color = Theme::getInstance()->fg_green->foreground;
std::string entry_string = "";
@@ -111,7 +112,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
logger = std::make_unique<APRSLogger>();
if (logger)
logger->append(LOG_ROOT_DIR "/APRS.TXT");
logger->append(logs_dir / u"APRS.TXT");
baseband::set_aprs(1200);
@@ -121,6 +122,10 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
receiver_model.enable();
}
void APRSRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void APRSRxView::on_packet(const APRSPacketMessage* message) {
std::string str_console = "\x1B";
+13 -4
View File
@@ -36,10 +36,11 @@
#include "log_file.hpp"
#include "utility.hpp"
#include "file_path.hpp"
class APRSLogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -184,6 +185,7 @@ class APRSRxView : public View {
std::string title() const override { return "APRS RX"; };
void on_packet(const APRSPacketMessage* message);
void on_freqchg(int64_t freq);
private:
void on_data(uint32_t value, bool is_data);
@@ -232,7 +234,7 @@ class APRSRxView : public View {
RecordView record_view{
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
u"AFS_????.WAV",
u"APRS",
aprs_dir,
RecordView::FileType::WAV,
4096,
4};
@@ -260,8 +262,8 @@ class APRSRXView : public View {
APRSTableView view_table{nav_, view_rect};
TabView tab_view{
{"Stream", Color::cyan(), &view_stream},
{"List", Color::yellow(), &view_table}};
{"Stream", Theme::getInstance()->fg_cyan->foreground, &view_stream},
{"List", Theme::getInstance()->fg_yellow->foreground, &view_table}};
MessageHandlerRegistration message_handler_packet{
Message::ID::APRSPacket,
@@ -270,6 +272,13 @@ class APRSRXView : public View {
this->view_stream.on_packet(message);
this->view_table.on_pkt(message);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->view_stream.on_freqchg(message->freq);
}};
};
} /* namespace ui */
+3 -3
View File
@@ -61,9 +61,9 @@ class APRSTXView : public View {
void on_tx_progress(const uint32_t progress, const bool done);
Labels labels{
{{0 * 8, 1 * 16}, "Source: SSID:", Color::light_grey()}, // 6 alphanum + SSID
{{0 * 8, 2 * 16}, " Dest.: SSID:", Color::light_grey()},
{{0 * 8, 4 * 16}, "Info field:", Color::light_grey()},
{{0 * 8, 1 * 16}, "Source: SSID:", Theme::getInstance()->fg_light->foreground}, // 6 alphanum + SSID
{{0 * 8, 2 * 16}, " Dest.: SSID:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Info field:", Theme::getInstance()->fg_light->foreground},
};
SymField sym_source{
+188
View File
@@ -0,0 +1,188 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 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_battinfo.hpp"
#include "event_m0.hpp"
#include "portapack.hpp"
#include "battery.hpp"
#include <cstring>
using namespace portapack;
namespace ui {
void BattinfoView::focus() {
button_exit.focus();
}
// called each 1/60th of second, so 6 = 100ms
void BattinfoView::on_timer() {
if (++timer_counter == timer_period) {
timer_counter = 0;
update_result();
}
}
void BattinfoView::update_result() {
if (!battery::BatteryManagement::isDetected()) {
text_percent.set("UNKNOWN");
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
text_cycles.set("-");
text_ttef.set("-");
text_method.set("-");
text_warn.set("");
return;
}
bool uichg = false;
uint8_t valid_mask = 0;
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current);
// update text fields
if (percent <= 100 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE)
text_percent.set(to_string_dec_uint(percent) + " %");
else
text_percent.set("UNKNOWN");
if (voltage > 1 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE) {
text_voltage.set(to_string_decimal(voltage / 1000.0, 3) + " V");
} else {
text_voltage.set("UNKNOWN");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CURRENT) == battery::BatteryManagement::BATT_VALID_CURRENT) {
if (labels_opt.hidden()) uichg = true;
labels_opt.hidden(false);
text_current.hidden(false);
text_charge.hidden(false);
text_current.set(to_string_dec_int(current) + " mA");
text_charge.set(current >= 0 ? "Charging" : "Discharging");
labels_opt.hidden(false);
text_ttef.hidden(false);
} else {
if (!labels_opt.hidden()) uichg = true;
labels_opt.hidden(true);
text_current.hidden(true);
text_charge.hidden(true);
text_cycles.hidden(true);
text_ttef.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
text_cycles.hidden(false);
uint16_t cycles = battery::BatteryManagement::get_cycles();
if (cycles < 2)
text_warn.set("SoC improves after 2 cycles");
else
text_warn.set("");
text_cycles.set(to_string_dec_uint(cycles));
} else {
text_cycles.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
text_ttef.hidden(false);
float ttef = 0;
if (current <= 0) {
ttef = battery::BatteryManagement::get_tte();
} else {
ttef = battery::BatteryManagement::get_ttf();
}
// Convert ttef to hours and minutes
uint8_t hours = static_cast<uint8_t>(ttef);
uint8_t minutes = static_cast<uint8_t>((ttef - hours) * 60 + 0.5); // +0.5 for rounding
// Create the formatted string
std::string formatted_time;
if (hours > 0) {
formatted_time += to_string_dec_uint(hours) + "h ";
}
formatted_time += to_string_dec_uint(minutes) + "m";
text_ttef.set(formatted_time);
} else {
text_ttef.hidden(true);
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_PERCENT) == battery::BatteryManagement::BATT_VALID_PERCENT) {
text_method.set("IC");
button_mode.set_text("Volt");
} else {
text_method.set("Voltage");
button_mode.set_text("IC");
}
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
BatteryStateMessage msg{valid_mask, percent, current >= 0, voltage};
EventDispatcher::send_message(msg);
}
BattinfoView::BattinfoView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
&labels_opt,
&text_percent,
&text_voltage,
&text_current,
&text_charge,
&text_method,
&button_mode,
&button_exit,
&text_cycles,
&text_warn,
&text_ttef});
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
button_mode.on_select = [this, &nav](Button&) {
if (button_mode.text() == "IC") {
battery::BatteryManagement::set_calc_override(false);
persistent_memory::set_ui_override_batt_calc(false);
button_mode.set_text("Volt");
} else {
battery::BatteryManagement::set_calc_override(true);
persistent_memory::set_ui_override_batt_calc(true);
button_mode.set_text("IC");
}
};
update_result();
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
}
msg_t BattinfoView::static_fn(void* arg) {
auto obj = static_cast<BattinfoView*>(arg);
while (!chThdShouldTerminate()) {
chThdSleepMilliseconds(16);
obj->on_timer();
}
return 0;
}
BattinfoView::~BattinfoView() {
if (thread) {
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
}
}
} // namespace ui
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 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_BATTINFO_H__
#define __UI_BATTINFO_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
namespace ui {
class BattinfoView : public View {
public:
~BattinfoView();
BattinfoView(NavigationView& nav);
BattinfoView(const BattinfoView&) = delete;
BattinfoView(BattinfoView&&) = delete;
BattinfoView& operator=(const BattinfoView&) = delete;
BattinfoView& operator=(BattinfoView&&) = delete;
void focus() override;
std::string title() const override { return "Battery"; };
private:
void update_result();
void on_timer();
NavigationView& nav_;
uint16_t timer_period = 60;
uint16_t timer_counter = 0;
uint8_t percent = 0;
uint16_t voltage = 0;
int32_t current = 0;
Labels labels{
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 3 * 16}, "Method:", Theme::getInstance()->fg_light->foreground},
};
Labels labels_opt{
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
"-"};
Text text_voltage{
{13 * 8, 2 * 16, 10 * 16, 16},
"-"};
Text text_method{
{13 * 8, 3 * 16, 10 * 16, 16},
"-"};
Text text_current{
{13 * 8, 4 * 16, 10 * 16, 16},
"-"};
Text text_charge{
{13 * 8, 5 * 16, 10 * 16, 16},
"-"};
Text text_ttef{
{13 * 8, 6 * 16, 10 * 16, 16},
"-"};
Text text_cycles{
{13 * 8, 7 * 16, 10 * 16, 16},
"-"};
Text text_warn{
{2 * 8, 8 * 16, 30 * 8, 2 * 16},
""};
Button button_mode{
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
"Volt"};
Button button_exit{
{72, 17 * 16, 96, 32},
"Back"};
static msg_t static_fn(void* arg);
Thread* thread{nullptr};
};
} /* namespace ui */
#endif /*__UI_BATTINFO__*/
+12 -12
View File
@@ -50,12 +50,12 @@ class XylosView : public View {
private:
Labels labels{
{{8 * 8, 1 * 8}, "Header:", Color::light_grey()},
{{4 * 8, 3 * 8}, "City code:", Color::light_grey()},
{{7 * 8, 5 * 8}, "Family:", Color::light_grey()},
{{2 * 8, 7 * 8 + 2}, "Subfamily:", Color::light_grey()},
{{2 * 8, 11 * 8}, "Receiver ID:", Color::light_grey()},
{{2 * 8, 14 * 8}, "Relay:", Color::light_grey()}};
{{8 * 8, 1 * 8}, "Header:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 3 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
{{7 * 8, 5 * 8}, "Family:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 8 + 2}, "Subfamily:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 11 * 8}, "Receiver ID:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 14 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
NumberField field_header_a{
{16 * 8, 1 * 8},
@@ -130,9 +130,9 @@ class EPARView : public View {
private:
Labels labels{
{{4 * 8, 1 * 8}, "City code:", Color::light_grey()},
{{8 * 8, 3 * 8}, "Group:", Color::light_grey()},
{{8 * 8, 7 * 8}, "Relay:", Color::light_grey()}};
{{4 * 8, 1 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 3 * 8}, "Group:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 7 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
NumberField field_city{
{16 * 8, 1 * 8},
@@ -195,11 +195,11 @@ class BHTView : public View {
EPARView view_EPAR{view_rect};
TabView tab_view{
{"Xylos", Color::cyan(), &view_xylos},
{"EPAR", Color::green(), &view_EPAR}};
{"Xylos", Theme::getInstance()->fg_cyan->foreground, &view_xylos},
{"EPAR", Theme::getInstance()->fg_green->foreground, &view_EPAR}};
Labels labels{
{{29 * 8, 14 * 16 + 4}, "s", Color::light_grey()}};
{{29 * 8, 14 * 16 + 4}, "s", Theme::getInstance()->fg_light->foreground}};
Checkbox checkbox_scan{
{1 * 8, 25 * 8},
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 HTotoo
*
* 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_bmp_file_viewer.hpp"
extern ui::SystemView* system_view_ptr;
using namespace portapack;
namespace fs = std::filesystem;
namespace ui {
BMPFileViewer::BMPFileViewer(
NavigationView& nav,
const std::filesystem::path& path)
: nav_{nav},
path_{path} {
add_children(
{&bmp});
bmp.set_enter_pass(true); // pass the enter key to me, so i can exit. this will disable zoom + pos reset
set_focusable(true);
system_view_ptr->set_app_fullscreen(true);
}
BMPFileViewer::~BMPFileViewer() {
system_view_ptr->set_app_fullscreen(false);
}
void BMPFileViewer::focus() {
bmp.focus();
}
bool BMPFileViewer::on_key(KeyEvent k) {
if (k == KeyEvent::Select) {
nav_.pop();
return true;
}
return false;
}
void BMPFileViewer::paint(Painter&) {
bmp.load_bmp(path_);
}
} // namespace ui
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 HTotoo
*
* 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_BMP_FILE_VIEWER_H__
#define __UI_BMP_FILE_VIEWER_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "file.hpp"
#include "ui_bmpview.hpp"
namespace ui {
class BMPFileViewer : public View {
public:
BMPFileViewer(NavigationView& nav, const std::filesystem::path& path);
~BMPFileViewer();
bool on_key(KeyEvent key) override;
void paint(Painter& painter) override;
void focus() override;
private:
NavigationView& nav_;
std::filesystem::path path_{};
BMPViewer bmp{{0, 0, 240, 320}};
};
} // namespace ui
#endif // __UI_BMP_FILE_VIEWER_H__
+62 -36
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
*
* This file is part of PortaPack.
*
@@ -33,9 +34,9 @@
#include "ui_sd_card_debug.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "ui_styles.hpp"
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_battery.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -79,7 +80,7 @@ void TemperatureWidget::paint(Painter& painter) {
const auto rect = screen_rect();
const Color color_background{0, 0, 64};
const Color color_foreground = Color::green();
const Color color_foreground = Theme::getInstance()->fg_green->foreground;
const Color color_reticle{128, 128, 128};
const auto graph_width = static_cast<int>(logger.capacity()) * bar_width;
@@ -225,6 +226,8 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
return radio::debug::second_if::register_read(register_number);
case CT_SI5351:
return portapack::clock_generator.read_register(register_number);
case CT_BATTERY:
return battery::BatteryManagement::read_register(register_number);
case CT_AUDIO:
return audio::debug::reg_read(register_number);
}
@@ -246,6 +249,9 @@ void RegistersWidget::reg_write(const uint32_t register_number, const uint32_t v
case CT_SI5351:
portapack::clock_generator.write_register(register_number, value);
break;
case CT_BATTERY:
battery::BatteryManagement::write_register(register_number, value);
break;
case CT_AUDIO:
audio::debug::reg_write(register_number, value);
break;
@@ -291,7 +297,7 @@ RegistersView::RegistersView(
const auto value = registers_widget.reg_read(0);
field_write_data_val.set_value(value);
button_write.set_style(&Styles::red);
button_write.set_style(Theme::getInstance()->fg_red);
button_write.on_select = [this](Button&) {
this->registers_widget.reg_write(field_write_reg_num.to_integer(), field_write_data_val.to_integer());
this->registers_widget.update();
@@ -314,7 +320,7 @@ bool RegistersView::on_encoder(const EncoderEvent delta) {
void ControlsSwitchesWidget::on_show() {
display.fill_rectangle(
screen_rect(),
Color::black());
Theme::getInstance()->bg_darkest->background);
}
bool ControlsSwitchesWidget::on_key(const KeyEvent key) {
@@ -344,11 +350,11 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
}};
for (const auto r : button_rects) {
painter.fill_rectangle(r + pos, Color::blue());
painter.fill_rectangle(r + pos, Theme::getInstance()->fg_blue->foreground);
}
if (get_touch_frame().touch)
painter.fill_rectangle(button_rects[8] + pos, Color::yellow());
painter.fill_rectangle(button_rects[8] + pos, Theme::getInstance()->fg_yellow->foreground);
const std::array<Rect, 8> raw_rects{{
{64 + 1, 32 + 1, 16 - 2, 16 - 2}, // Right
@@ -364,7 +370,7 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
auto switches_raw = control::debug::switches();
for (const auto r : raw_rects) {
if (switches_raw & 1)
painter.fill_rectangle(r + pos, Color::yellow());
painter.fill_rectangle(r + pos, Theme::getInstance()->fg_yellow->foreground);
switches_raw >>= 1;
}
@@ -381,7 +387,7 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
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());
painter.fill_rectangle(r + pos, Theme::getInstance()->fg_green->foreground);
switches_debounced >>= 1;
}
@@ -398,7 +404,7 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
auto switches_event = key_event_mask;
for (const auto r : events_rects) {
if (switches_event & 1)
painter.fill_rectangle(r + pos, Color::red());
painter.fill_rectangle(r + pos, Theme::getInstance()->fg_red->foreground);
switches_event >>= 1;
}
@@ -406,12 +412,12 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
switches_event = long_press_key_event_mask;
for (const auto r : events_rects) {
if (switches_event & 1)
painter.fill_rectangle(r + pos, Color::cyan());
painter.fill_rectangle(r + pos, Theme::getInstance()->fg_cyan->foreground);
switches_event >>= 1;
}
painter.draw_string({5 * 8, 12 * 16}, Styles::light_grey, to_string_dec_int(last_delta, 3));
painter.draw_string({5 * 8, 12 * 16}, *Theme::getInstance()->fg_light, to_string_dec_int(last_delta, 3));
}
void ControlsSwitchesWidget::on_frame_sync() {
@@ -445,15 +451,24 @@ void DebugControlsView::focus() {
/* DebugPeripheralsMenuView **********************************************/
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav)
: nav_(nav) {
set_max_rows(2); // allow wider buttons
}
void DebugPeripheralsMenuView::on_populate() {
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{CT_RFFC5072, 31, 31, 16}); }},
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
{"RFFC5072", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 31, 16}); }},
{max283x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, max283x]() { nav_.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
{si5351x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
add_item(
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_BATTERY, 256, 16, 16}); }});
}
set_max_rows(2); // allow wider buttons
}
@@ -468,32 +483,43 @@ DebugReboot::DebugReboot(NavigationView& nav) {
__WFE();
}
void DebugReboot::on_populate() {
}
/* DebugMenuView *********************************************************/
DebugMenuView::DebugMenuView(NavigationView& nav) {
DebugMenuView::DebugMenuView(NavigationView& nav)
: nav_(nav) {
set_max_rows(2); // allow wider buttons
}
void DebugMenuView::on_populate() {
if (portapack::persistent_memory::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
add_items({{"..", ui::Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_items({
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }},
{"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { portapack::persistent_memory::debug_dump(); }},
{"M0 Stack Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { stack_dump(); }},
{"Memory Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugMemoryDumpView>(); }},
{"Memory Usage", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugMemoryView>(); }},
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }},
{"Pers. Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }},
//{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push<NotImplementedView>(); } },
{"Reboot", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<DebugReboot>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SDCardDebugView>(); }},
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
{"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<DebugScreenTest>(); }},
{"Buttons Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_controls, [this]() { nav_.push<DebugControlsView>(); }},
{"Debug Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { portapack::persistent_memory::debug_dump(); }},
{"M0 Stack Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { stack_dump(); }},
{"Memory Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryDumpView>(); }},
//{"Memory Usage", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryView>(); }},
{"Peripherals", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
{"Pers. Memory", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugPmemView>(); }},
//{ "Radio State", ui::Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ nav_.push<NotImplementedView>(); } },
{"SD Card", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_sdcard, [this]() { nav_.push<SDCardDebugView>(); }},
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
});
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav)) {
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
add_item(
{"Battery", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_batt_icon, [this]() { nav_.push<BatteryCapacityView>(); }});
}
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
add_item(gridItem);
};
set_max_rows(2); // allow wider buttons
}
/* DebugMemoryDumpView *********************************************************/
@@ -523,7 +549,7 @@ DebugMemoryDumpView::DebugMemoryDumpView(NavigationView& nav) {
field_data_value.set_dirty();
};
button_write.set_style(&Styles::red);
button_write.set_style(Theme::getInstance()->fg_red);
button_write.on_select = [this](Button&) {
*(uint32_t*)field_rw_address.to_integer() = (uint32_t)field_data_value.to_integer();
};
@@ -606,8 +632,8 @@ bool DebugScreenTest::on_touch(const TouchEvent event) {
}
void DebugScreenTest::paint(Painter& painter) {
painter.fill_rectangle({0, 16, screen_width, screen_height - 16}, Color::white());
painter.draw_string({10 * 8, screen_height / 2}, Styles::white, "Use Stylus");
painter.fill_rectangle({0, 16, screen_width, screen_height - 16}, Theme::getInstance()->bg_darkest->foreground);
painter.draw_string({10 * 8, screen_height / 2}, *Theme::getInstance()->bg_darkest, "Use Stylus");
pen_color = std::rand();
}
+24 -11
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
*
* This file is part of PortaPack.
*
@@ -138,6 +139,7 @@ typedef enum {
CT_MAX283X,
CT_SI5351,
CT_AUDIO,
CT_BATTERY,
} chip_type_t;
struct RegistersWidgetConfig {
@@ -232,8 +234,8 @@ class RegistersView : public View {
"Write"};
Labels labels{
{{1 * 8, 248}, "Reg:", Color::light_grey()},
{{8 * 8, 248}, "Data:", Color::light_grey()}};
{{1 * 8, 248}, "Reg:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 248}, "Data:", Theme::getInstance()->fg_light->foreground}};
SymField field_write_reg_num{
{5 * 8, 248},
@@ -287,9 +289,9 @@ class DebugControlsView : public View {
private:
Labels labels{
{{8 * 8, 1 * 16}, "Controls State", Color::white()},
{{0 * 8, 11 * 16}, "Dial:", Color::grey()},
{{0 * 8, 14 * 16}, "Long-Press Mode:", Color::grey()}};
{{8 * 8, 1 * 16}, "Controls State", Theme::getInstance()->bg_darkest->foreground},
{{0 * 8, 11 * 16}, "Dial:", Theme::getInstance()->fg_medium->foreground},
{{0 * 8, 14 * 16}, "Long-Press Mode:", Theme::getInstance()->fg_medium->foreground}};
ControlsSwitchesWidget switches_widget{
{80, 80, 80, 112},
@@ -332,12 +334,12 @@ class DebugMemoryDumpView : public View {
"Done"};
Labels labels{
{{5 * 8, 1 * 16}, "Dump Range to File", Color::yellow()},
{{0 * 8, 2 * 16}, "Starting Address: 0x", Color::light_grey()},
{{0 * 8, 3 * 16}, "Byte Count: 0x", Color::light_grey()},
{{3 * 8, 8 * 16}, "Read/Write Single Word", Color::yellow()},
{{0 * 8, 9 * 16}, "Memory Address: 0x", Color::light_grey()},
{{0 * 8, 10 * 16}, "Data Value: 0x", Color::light_grey()}};
{{5 * 8, 1 * 16}, "Dump Range to File", Theme::getInstance()->fg_yellow->foreground},
{{0 * 8, 2 * 16}, "Starting Address: 0x", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Byte Count: 0x", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 8 * 16}, "Read/Write Single Word", Theme::getInstance()->fg_yellow->foreground},
{{0 * 8, 9 * 16}, "Memory Address: 0x", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "Data Value: 0x", Theme::getInstance()->fg_light->foreground}};
SymField field_starting_address{
{20 * 8, 2 * 16},
@@ -422,17 +424,28 @@ class DebugPeripheralsMenuView : public BtnGridView {
public:
DebugPeripheralsMenuView(NavigationView& nav);
std::string title() const override { return "Peripherals"; };
private:
NavigationView& nav_;
void on_populate() override;
};
class DebugReboot : public BtnGridView {
public:
DebugReboot(NavigationView& nav);
private:
void on_populate() override;
};
class DebugMenuView : public BtnGridView {
public:
DebugMenuView(NavigationView& nav);
std::string title() const override { return "Debug"; };
private:
NavigationView& nav_;
void on_populate() override;
};
} /* namespace ui */
@@ -0,0 +1,104 @@
#include "ui_debug_battery.hpp"
#include "string_format.hpp"
namespace ui {
BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) {
if (index < battery::max17055::MAX17055::entries_count) {
return battery::max17055::MAX17055::entries[index];
}
return {"", 0, "", 0, false, "", false, 0, false, false, false, 0, false};
}
BatteryCapacityView::BatteryCapacityView(NavigationView& nav) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
name_texts[i].set_parent_rect({0 * 8, static_cast<int>((i + 1) * 16), 8 * 8, 16});
addr_texts[i].set_parent_rect({9 * 8, static_cast<int>((i + 1) * 16), 4 * 8, 16});
hex_texts[i].set_parent_rect({14 * 8, static_cast<int>((i + 1) * 16), 6 * 8, 16});
value_texts[i].set_parent_rect({21 * 8, static_cast<int>((i + 1) * 16), 10 * 8, 16});
add_child(&name_texts[i]);
add_child(&addr_texts[i]);
add_child(&hex_texts[i]);
add_child(&value_texts[i]);
}
add_children({&labels, &page_text, &button_done});
button_done.on_select = [&nav](Button&) { nav.pop(); };
populate_page(0);
update_page_text();
}
void BatteryCapacityView::focus() {
button_done.focus();
}
bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
int32_t new_page = current_page + delta;
if (new_page >= 0 && new_page < ((int32_t)battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
current_page = new_page;
populate_page(current_page * ENTRIES_PER_PAGE);
update_page_text();
}
return true;
}
void BatteryCapacityView::update_values() {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = current_page * ENTRIES_PER_PAGE + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
uint16_t raw_value = battery::BatteryManagement::read_register(entry.address);
hex_texts[i].set("0x" + to_string_hex(raw_value, 4));
float scaled_value;
if (entry.is_signed) {
int16_t signed_value = static_cast<int16_t>(raw_value);
scaled_value = signed_value * entry.scalar;
} else {
scaled_value = raw_value * entry.scalar;
}
// Format the value with appropriate decimal places
std::string formatted_value;
if (entry.resolution > 0) {
formatted_value = to_string_decimal(scaled_value, std::min(entry.resolution, 3));
} else {
formatted_value = to_string_dec_int(scaled_value); // Show up to 3 decimal places
}
value_texts[i].set(formatted_value + " " + entry.unit);
}
}
}
void BatteryCapacityView::populate_page(int start_index) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = start_index + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
name_texts[i].set(entry.name);
addr_texts[i].set("0x" + to_string_hex(entry.address, 2));
name_texts[i].hidden(false);
addr_texts[i].hidden(false);
hex_texts[i].hidden(false);
value_texts[i].hidden(false);
} else {
name_texts[i].hidden(true);
addr_texts[i].hidden(true);
hex_texts[i].hidden(true);
value_texts[i].hidden(true);
}
}
update_values();
}
void BatteryCapacityView::update_page_text() {
int total_pages = (battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
page_text.set("Page " + to_string_dec_uint(current_page + 1) + "/" + to_string_dec_uint(total_pages));
}
} // namespace ui
@@ -0,0 +1,48 @@
#ifndef __UI_DEBUG_BATTERY_HPP__
#define __UI_DEBUG_BATTERY_HPP__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "battery.hpp"
#include "max17055.hpp"
namespace ui {
class BatteryCapacityView : public View {
public:
BatteryCapacityView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery Registers"; }
bool on_encoder(const EncoderEvent delta) override;
using RegisterEntry = battery::max17055::RegisterEntry;
private:
static RegisterEntry get_entry(size_t index);
Labels labels{
{{0 * 8, 0 * 16}, "Reg", Theme::getInstance()->fg_yellow->foreground},
{{9 * 8, 0 * 16}, "Addr", Theme::getInstance()->fg_yellow->foreground},
{{14 * 8, 0 * 16}, "Hex", Theme::getInstance()->fg_yellow->foreground},
{{21 * 8, 0 * 16}, "Value", Theme::getInstance()->fg_yellow->foreground},
};
std::array<Text, 16> name_texts = {};
std::array<Text, 16> addr_texts = {};
std::array<Text, 16> hex_texts = {};
std::array<Text, 16> value_texts = {};
Text page_text{{144, 284, 80, 16}, "Page 1/1"};
Button button_done{{16, 280, 96, 24}, "Done"};
void update_values();
void populate_page(int start_index);
void update_page_text();
int current_page = 0;
static constexpr int ENTRIES_PER_PAGE = 16;
};
} // namespace ui
#endif // __UI_DEBUG_BATTERY_HPP__
+34 -19
View File
@@ -38,48 +38,63 @@ DfuMenu::DfuMenu(NavigationView& nav)
&text_info_line_5,
&text_info_line_6,
&text_info_line_7,
&text_info_line_8});
&text_info_line_8,
&text_info_line_9,
&text_info_line_10});
if (battery::BatteryManagement::isDetected()) {
add_child(&voltage_label);
add_child(&text_info_line_11);
}
}
void DfuMenu::paint(Painter& painter) {
auto utilisation = get_cpu_utilisation_in_percent();
size_t m0_fragmented_free_space = 0;
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
auto lines = (battery::BatteryManagement::isDetected() ? 11 : 10) + 2;
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_performance_counter, 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));
text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6));
text_info_line_3.set(to_string_dec_uint(m0_fragments, 6));
text_info_line_4.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6));
text_info_line_5.set(to_string_dec_uint(utilisation, 6));
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
if (battery::BatteryManagement::isDetected()) {
text_info_line_11.set(to_string_decimal_padding((float)battery::BatteryManagement::getVoltage() / 1000.0, 3, 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());
Theme::getInstance()->bg_darkest->background);
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{21 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8},
{17 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, (lines + 3) * LINE_HEIGHT + margin},
{17 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
}
DfuMenu2::DfuMenu2(NavigationView& nav)
@@ -118,27 +133,27 @@ void DfuMenu2::paint(Painter& painter) {
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{19 * CHARACTER_WIDTH + margin * 2, lines * LINE_HEIGHT + margin * 2}},
ui::Color::black());
Theme::getInstance()->bg_darkest->background);
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{24 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8},
{21 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, (lines + 3) * LINE_HEIGHT + margin},
{21 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
ui::Theme::getInstance()->fg_darkcyan->foreground);
}
} /* namespace ui */
+26 -19
View File
@@ -49,14 +49,18 @@ class DfuMenu : public View {
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()}};
{{6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "M0 core:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "M0 heap:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "M0 frags:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "M0 stack:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "M0 cpu %:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "M4 heap:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 stack:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "M4 cpu %:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "Uptime:", Theme::getInstance()->fg_darkcyan->foreground}};
Labels voltage_label{{{6 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "Voltage:", Theme::getInstance()->fg_darkcyan->foreground}};
Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@@ -66,6 +70,9 @@ class DfuMenu : public View {
Text text_info_line_6{{15 * CHARACTER_WIDTH, 10 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_7{{15 * CHARACTER_WIDTH, 11 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_9{{15 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_10{{15 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_11{{15 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
class DfuMenu2 : public View {
@@ -81,17 +88,17 @@ class DfuMenu2 : public View {
Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 14 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Radio Settings"};
Labels labels{
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "RX Freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "RX BW:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "RX SampR:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "RX Satu%:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "RX Freq:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "RX BW:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "RX SampR:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "RX Satu%:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "Modulatn:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "AM cfg:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "NBFM cfg:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "WFM cfg:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX Freq:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX BW:", Theme::getInstance()->fg_darkcyan->foreground},
{{5 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "TX SampR:", Theme::getInstance()->fg_darkcyan->foreground},
};
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
+16 -16
View File
@@ -64,16 +64,16 @@ class EncodersConfigView : public View {
void on_type_change(size_t index);
Labels labels{
{{1 * 8, 0}, "Type:", Color::light_grey()},
{{17 * 8, 0}, "Repeat:", Color::light_grey()},
{{1 * 8, 2 * 8}, "Clk:", Color::light_grey()},
{{10 * 8, 2 * 8}, "kHz", Color::light_grey()},
{{17 * 8, 2 * 8}, "Step:", Color::light_grey()},
{{1 * 8, 4 * 8}, "Frame:", Color::light_grey()},
{{13 * 8, 4 * 8}, "us", Color::light_grey()},
{{17 * 8, 4 * 8}, "Step:", Color::light_grey()},
{{2 * 8, 7 * 8}, "Symbols:", Color::light_grey()},
{{1 * 8, 14 * 8}, "Waveform:", Color::light_grey()}};
{{1 * 8, 0}, "Type:", Theme::getInstance()->fg_light->foreground},
{{17 * 8, 0}, "Repeat:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 8}, "Clk:", Theme::getInstance()->fg_light->foreground},
{{10 * 8, 2 * 8}, "kHz", Theme::getInstance()->fg_light->foreground},
{{17 * 8, 2 * 8}, "Step:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 4 * 8}, "Frame:", Theme::getInstance()->fg_light->foreground},
{{13 * 8, 4 * 8}, "us", Theme::getInstance()->fg_light->foreground},
{{17 * 8, 4 * 8}, "Step:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 8}, "Symbols:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 14 * 8}, "Waveform:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_enctype{// Options are loaded at runtime
{6 * 8, 0},
@@ -128,7 +128,7 @@ class EncodersConfigView : public View {
0,
0,
true,
Color::yellow()};
Theme::getInstance()->fg_yellow->foreground};
};
class EncodersScanView : public View {
@@ -160,9 +160,9 @@ class EncodersScanView : public View {
private:
Labels labels{
{{1 * 8, 0 * 8}, "Length:", Color::light_grey()},
{{1 * 8, 2 * 8}, "Bit length:", Color::light_grey()},
{{16 * 8, 2 * 8}, "us", Color::light_grey()},
{{1 * 8, 0 * 8}, "Length:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 8}, "Bit length:", Theme::getInstance()->fg_light->foreground},
{{16 * 8, 2 * 8}, "us", Theme::getInstance()->fg_light->foreground},
};
};
@@ -206,8 +206,8 @@ class EncodersView : public View {
EncodersScanView view_scan{nav_, view_rect};
TabView tab_view{
{"Config", Color::cyan(), &view_config},
{"de Bruijn", Color::green(), &view_scan},
{"Config", Theme::getInstance()->fg_cyan->foreground, &view_config},
{"de Bruijn", Theme::getInstance()->fg_green->foreground, &view_scan},
};
Text text_status{
+249 -53
View File
@@ -29,11 +29,13 @@
#include "ui_playlist.hpp"
#include "ui_remote.hpp"
#include "ui_ss_viewer.hpp"
#include "ui_bmp_file_viewer.hpp"
#include "ui_text_editor.hpp"
#include "ui_iq_trim.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
#include "file_path.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -62,7 +64,7 @@ std::string truncate(const fs::path& path, size_t max_length) {
}
// 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) {
void insert_sorted(std::list<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) {
@@ -146,9 +148,82 @@ namespace ui {
/* FileManBaseView ***********************************************************/
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
void FileManBaseView::load_directory_contents_unordered(const fs::path& dir_path, size_t file_cnt) {
current_path = dir_path;
entry_list.clear();
menu_view.clear();
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
nb_pages = 1 + (file_cnt / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (file_cnt < stop) stop = file_cnt;
if (start > file_cnt) start = 0; // shouldn't hapen but check against it won't hurt
size_t curr = 0;
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
if (entry_list.size() >= items_per_page) {
break;
}
// Hide files starting with '.' (hidden / tmp).
if (!show_hidden_files && is_hidden_file(entry.path()))
continue;
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path()))) {
curr++;
if (curr >= start) insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
}
} else if (fs::is_directory(entry.status())) {
curr++;
if (curr >= start) insert_sorted(entry_list, {entry.path().string(), 0, true});
}
}
// Add "parent" directory if not at the root.
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (file_cnt > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
}
}
int FileManBaseView::file_count_filtered(const fs::path& directory) {
int count{0};
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
for (auto& entry : std::filesystem::directory_iterator(directory, (const TCHAR*)u"*")) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
++count;
} else
++count;
}
return count;
}
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
size_t file_cnt = file_count_filtered(dir_path);
if (file_cnt >= max_items_loaded) {
load_directory_contents_unordered(dir_path, file_cnt);
return;
}
current_path = dir_path;
entry_list.clear();
menu_view.clear();
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
@@ -161,19 +236,41 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path(), 0, true});
insert_sorted(entry_list, {entry.path().string(), 0, true});
}
}
// paginating
auto list_size = entry_list.size();
nb_pages = 1 + (list_size / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (list_size > start) {
if (list_size < stop)
stop = list_size;
entry_list.erase(std::next(entry_list.begin(), stop), entry_list.end());
entry_list.erase(entry_list.begin(), std::next(entry_list.begin(), start));
}
// Add "parent" directory if not at the root.
if (!dir_path.empty())
entry_list.insert(entry_list.begin(), {parent_dir_path, 0, true});
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (list_size > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
}
}
fs::path FileManBaseView::get_selected_full_path() const {
if (get_selected_entry().path == parent_dir_path)
if (get_selected_entry().path == parent_dir_path.string())
return current_path.parent_path();
return current_path / get_selected_entry().path;
@@ -181,7 +278,10 @@ fs::path FileManBaseView::get_selected_full_path() const {
const fileman_entry& FileManBaseView::get_selected_entry() const {
// TODO: return reference to an "empty" entry on OOB?
return entry_list[menu_view.highlighted_index()];
auto it = entry_list.begin();
if (menu_view.highlighted_index() >= 1) std::advance(it, menu_view.highlighted_index());
return *it;
// return entry_list[menu_view.highlighted_index()];
}
FileManBaseView::FileManBaseView(
@@ -230,7 +330,7 @@ void FileManBaseView::push_dir(const fs::path& path) {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current();
reload_current(true);
}
}
@@ -239,30 +339,57 @@ void FileManBaseView::pop_dir() {
return;
current_path = current_path.parent_path();
reload_current();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
}
std::string get_extension(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return {};
} else {
return t.substr(index);
}
}
std::string get_stem(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return t;
} else {
return t.substr(0, index);
}
}
std::string get_filename(std::string _s) {
const auto index = _s.find_last_of("/");
if (index == _s.npos) {
return _s;
} else {
return _s.substr(index + 1);
}
}
void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
auto prev_highlight = menu_view.highlighted_index();
prev_highlight = menu_view.highlighted_index();
menu_view.clear();
for (const auto& entry : entry_list) {
auto entry_name = truncate(entry.path, 20);
auto entry_name = std::string{entry.path.length() <= 20 ? entry.path : entry.path.substr(0, 20)};
if (entry.is_directory) {
auto size_str =
(entry.path == parent_dir_path)
? ""
: to_string_dec_uint(file_count(current_path / entry.path));
std::string size_str{};
if (entry.path == str_next || entry.path == str_back) {
size_str = to_string_dec_uint(1 + entry.size) + "/" + to_string_dec_uint(nb_pages); // show computed number of pages
} else {
size_str = (entry.path == parent_dir_path.string()) ? "" : to_string_dec_uint(file_count(current_path / entry.path));
}
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
ui::Color::yellow(),
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
Theme::getInstance()->fg_yellow->foreground,
&bitmap_icon_dir,
[this](KeyEvent key) {
if (on_select_entry)
@@ -270,11 +397,11 @@ void FileManBaseView::refresh_list() {
}});
} else {
const auto& assoc = get_assoc(entry.path.extension());
const auto& assoc = get_assoc(get_extension(entry.path));
auto size_str = to_string_file_size(entry.size);
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
assoc.color,
assoc.icon,
[this](KeyEvent key) {
@@ -282,16 +409,13 @@ void FileManBaseView::refresh_list() {
on_select_entry(key);
}});
}
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
}
menu_view.set_highlighted(prev_highlight);
}
void FileManBaseView::reload_current() {
void FileManBaseView::reload_current(bool reset_pagination) {
if (reset_pagination) pagination = 0;
load_directory_contents(current_path);
refresh_list();
}
@@ -327,6 +451,21 @@ FileLoadView::FileLoadView(
on_select_entry = [this](KeyEvent) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
} else {
if (on_changed)
@@ -402,6 +541,7 @@ void FileSaveView::refresh_widgets() {
void FileManagerView::refresh_widgets(const bool v) {
button_rename.hidden(v);
button_delete.hidden(v);
button_clean.hidden(v);
button_cut.hidden(v);
button_copy.hidden(v);
button_paste.hidden(v);
@@ -415,10 +555,10 @@ void FileManagerView::on_rename(std::string_view hint) {
auto& entry = get_selected_entry();
// Append the hint to the filename stem as a rename suggestion.
name_buffer = entry.path.stem().string();
name_buffer = get_stem(entry.path);
if (!hint.empty())
name_buffer += "_" + std::string{hint};
name_buffer += entry.path.extension().string();
name_buffer += get_extension(entry.path);
// Set the rename cursor to before the extension to make renaming simpler.
uint32_t cursor_pos = (uint32_t)name_buffer.length();
@@ -439,21 +579,21 @@ void FileManagerView::on_rename(std::string_view hint) {
auto new_name = renamed_path.replace_extension(partner.extension());
rename_file(partner, current_path / new_name);
}
reload_current();
reload_current(false);
});
if (!has_partner)
reload_current();
reload_current(false);
});
}
void FileManagerView::on_delete() {
if (is_directory(get_selected_full_path()) && !is_empty_directory(get_selected_full_path())) {
nav_.display_modal("Delete", "Directory not empty!");
nav_.display_modal("Delete", " Folder is not empty;\n Use \"Clean\" button to\n empty it first.");
return;
}
auto name = get_selected_entry().path.filename().string();
auto name = get_filename(get_selected_entry().path);
nav_.push<ModalMessageView>(
"Delete", "Delete " + name + "\nAre you sure?", YESNO,
[this](bool choice) {
@@ -465,11 +605,36 @@ void FileManagerView::on_delete() {
[this](const fs::path& partner, bool should_delete) {
if (should_delete)
delete_file(partner);
reload_current();
reload_current(true);
});
if (!has_partner)
reload_current();
reload_current(true);
}
});
}
void FileManagerView::on_clean() {
if (is_empty_directory(get_selected_full_path())) {
nav_.display_modal("Clean", "Folder is Empty;\nUse \"Delete\" button instead\nof \"Clean\" button to delete\nit.");
return;
}
auto path_name = is_directory(get_selected_full_path()) ? get_selected_full_path() : get_selected_full_path().parent_path();
// selected either a single file (delete files in this directory) or a directory that is not empty (del sub files)
nav_.push<ModalMessageView>(
"Clean", " ALL FILES in this folder\n (excluding sub-folders)\n will be DELETED!\n\n Delete all files?", YESNO,
[this, path_name](bool choice) {
if (choice) {
std::vector<std::filesystem::path> file_list;
file_list = scan_root_files(path_name, u"*");
for (const auto& file_name : file_list) {
std::filesystem::path current_full_path = path_name / file_name;
delete_file(current_full_path);
}
reload_current(true);
}
});
}
@@ -478,7 +643,7 @@ 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();
reload_current(true);
});
}
@@ -488,7 +653,10 @@ void FileManagerView::on_paste() {
fs::filesystem_error result;
if (clipboard_mode == ClipboardMode::Cut)
result = rename_file(clipboard_path, current_path / new_name);
if ((current_path / clipboard_path.filename()) == clipboard_path)
result = FR_OK; // Skip paste to avoid renaming if path is unchanged
else
result = rename_file(clipboard_path, current_path / new_name);
else if (clipboard_mode == ClipboardMode::Copy)
result = copy_file(clipboard_path, current_path / new_name);
@@ -499,14 +667,14 @@ void FileManagerView::on_paste() {
clipboard_path = fs::path{};
clipboard_mode = ClipboardMode::None;
menu_view.focus();
reload_current();
reload_current(true);
}
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current();
reload_current(true);
});
}
@@ -528,8 +696,13 @@ bool FileManagerView::handle_file_open() {
nav_.push<ScreenshotViewer>(path);
return true;
} else if (path_iequal(bmp_ext, ext)) {
nav_.push<SplashViewer>(path);
reload_current();
if (path_iequal(current_path, u"/" + splash_dir)) {
nav_.push<SplashViewer>(path); // splash, so load that viewer
} else {
nav_.push<BMPFileViewer>(path); // any other bmp
}
reload_current(false);
return true;
} else if (path_iequal(rem_ext, ext)) {
nav_.push<RemoteView>(path);
@@ -541,7 +714,7 @@ bool FileManagerView::handle_file_open() {
bool FileManagerView::selected_is_valid() const {
return !entry_list.empty() &&
get_selected_entry().path != parent_dir_path;
get_selected_entry().path != parent_dir_path.string();
}
FileManagerView::FileManagerView(
@@ -560,6 +733,7 @@ FileManagerView::FileManagerView(
&text_date,
&button_rename,
&button_delete,
&button_clean,
&button_cut,
&button_copy,
&button_paste,
@@ -572,11 +746,11 @@ FileManagerView::FileManagerView(
});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= max_items_shown - 1) {
text_date.set_style(&Styles::red);
if (menu_view.highlighted_index() >= max_items_loaded - 1) { // todo check this if correct
text_date.set_style(Theme::getInstance()->fg_red);
text_date.set("Too many files!");
} else {
text_date.set_style(&Styles::grey);
text_date.set_style(Theme::getInstance()->fg_medium);
if (selected_is_valid())
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") + to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
@@ -587,13 +761,30 @@ FileManagerView::FileManagerView(
refresh_list();
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else if (key == KeyEvent::Select && handle_file_open()) {
return;
} else {
button_rename.focus();
if (key == KeyEvent::Select) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
return;
} else if (handle_file_open()) {
return;
}
}
button_rename.focus();
};
button_rename.on_select = [this]() {
@@ -606,8 +797,13 @@ FileManagerView::FileManagerView(
on_delete();
};
button_clean.on_select = [this]() {
if (selected_is_valid())
on_clean();
};
button_cut.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
if (selected_is_valid()) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Cut;
} else
@@ -626,7 +822,7 @@ FileManagerView::FileManagerView(
if (clipboard_mode != ClipboardMode::None)
on_paste();
else
nav_.display_modal("Paste", "Cut or copy a file first.");
nav_.display_modal("Paste", " Cut or copy a file,\n or cut a folder, first.");
};
button_new_dir.on_select = [this]() {
@@ -646,7 +842,7 @@ FileManagerView::FileManagerView(
};
button_rename_timestamp.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
if (selected_is_valid()) {
on_rename(::truncate(to_string_timestamp(rtc_time::now()), 8));
} else
nav_.display_modal("Timestamp Rename", "Can't rename that.");
@@ -662,7 +858,7 @@ FileManagerView::FileManagerView(
button_show_hidden_files.on_select = [this]() {
show_hidden_files = !show_hidden_files;
button_show_hidden_files.set_color(show_hidden_files ? Color::green() : Color::dark_grey());
button_show_hidden_files.set_color(show_hidden_files ? *Theme::getInstance()->status_active : Theme::getInstance()->bg_dark->background);
reload_current();
};
}
+44 -26
View File
@@ -31,7 +31,7 @@
namespace ui {
struct fileman_entry {
std::filesystem::path path{};
std::string path{};
uint32_t size{};
bool is_directory{};
};
@@ -61,8 +61,12 @@ class FileManBaseView : public View {
void push_dir(const std::filesystem::path& path);
protected:
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 100;
uint32_t prev_highlight = 0;
uint8_t pagination = 0;
uint8_t nb_pages = 1;
static constexpr size_t max_filename_length = 20;
static constexpr size_t max_items_loaded = 75; // too memory hungry, so won't sort it
static constexpr size_t items_per_page = 20;
struct file_assoc_t {
std::filesystem::path extension;
@@ -77,18 +81,20 @@ class FileManBaseView : public View {
{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".PPL", &bitmap_icon_file_iq, ui::Color::white()}, // Playlist/Replay
{u".REM", &bitmap_icon_remote, ui::Color::orange()}, // Remote
{u"", &bitmap_icon_file, ui::Color::light_grey()} // NB: Must be last.
{u".PPL", &bitmap_icon_file_iq, ui::Color::white()}, // Playlist/Replay
{u".REM", &bitmap_icon_remote, ui::Color::orange()}, // Remote
{u"", &bitmap_icon_file, Theme::getInstance()->fg_light->foreground} // NB: Must be last.
};
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
int file_count_filtered(const std::filesystem::path& directory);
void pop_dir();
void refresh_list();
void reload_current();
void reload_current(bool reset_pagination = false);
void load_directory_contents(const std::filesystem::path& dir_path);
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
NavigationView& nav_;
@@ -97,17 +103,20 @@ class FileManBaseView : public View {
std::function<void(KeyEvent)> on_select_entry{nullptr};
std::function<void(bool)> on_refresh_widgets{nullptr};
const std::string str_back{"<--"};
const std::string str_next{"-->"};
const std::string str_full{"Can't load more.."};
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::list<fileman_entry> entry_list{};
std::vector<uint32_t> saved_index_stack{};
bool show_hidden_files{false};
Labels labels{
{{0, 0}, "Path:", Color::light_grey()}};
{{0, 0}, "Path:", Theme::getInstance()->fg_light->foreground}};
Text text_current{
{6 * 8, 0 * 8, 24 * 8, 16},
@@ -157,8 +166,8 @@ private:
std::string buffer_ { };
Labels labels {
{ { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() },
{ { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() },
{ { 0 * 8, 1 * 16 }, "Path:", Theme::getInstance()->fg_light->foreground },
{ { 0 * 8, 6 * 16 }, "Filename:",Theme::getInstance()->fg_light->foreground },
};
Text text_path {
@@ -207,6 +216,7 @@ class FileManagerView : public FileManBaseView {
void refresh_widgets(const bool v);
void on_rename(std::string_view hint);
void on_delete();
void on_clean();
void on_paste();
void on_new_dir();
void on_new_file();
@@ -224,68 +234,76 @@ class FileManagerView : public FileManBaseView {
{0 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_rename,
Color::dark_blue()};
Theme::getInstance()->fg_blue->foreground};
NewButton button_delete{
{4 * 8, 29 * 8, 4 * 8, 32},
{9 * 8, 34 * 8, 4 * 8, 32},
{},
&bitmap_icon_trash,
Color::red()};
Theme::getInstance()->fg_red->foreground};
NewButton button_clean{
{13 * 8, 34 * 8, 4 * 8, 32},
{},
&bitmap_icon_clean,
Theme::getInstance()->fg_red->foreground};
NewButton button_cut{
{9 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_cut,
Color::dark_grey()};
Theme::getInstance()->fg_dark->foreground};
NewButton button_copy{
{13 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_copy,
Color::dark_grey()};
Theme::getInstance()->fg_dark->foreground};
NewButton button_paste{
{17 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_paste,
Color::dark_grey()};
Theme::getInstance()->fg_dark->foreground};
NewButton button_new_dir{
{22 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_new_dir,
Color::green()};
Theme::getInstance()->fg_green->foreground};
NewButton button_new_file{
{26 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_new_file,
Color::green()};
Theme::getInstance()->fg_green->foreground};
NewButton button_open_notepad{
{0 * 8, 34 * 8, 4 * 8, 32},
{},
&bitmap_icon_notepad,
Color::orange()};
Theme::getInstance()->fg_orange->foreground};
NewButton button_rename_timestamp{
{4 * 8, 34 * 8, 4 * 8, 32},
{4 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_options_datetime,
Color::orange(),
Theme::getInstance()->fg_blue->foreground,
/*vcenter*/ true};
NewButton button_open_iq_trim{
{9 * 8, 34 * 8, 4 * 8, 32},
{4 * 8, 34 * 8, 4 * 8, 32},
{},
&bitmap_icon_trim,
Color::orange()};
Theme::getInstance()->fg_orange->foreground};
NewButton button_show_hidden_files{
{13 * 8, 34 * 8, 4 * 8, 32},
{17 * 8, 34 * 8, 4 * 8, 32},
{},
&bitmap_icon_hide,
Color::dark_grey()};
Theme::getInstance()->fg_dark->foreground};
};
} /* namespace ui */
+56 -36
View File
@@ -21,34 +21,54 @@
*/
#include "ui_flash_utility.hpp"
#include "ui_styles.hpp"
#include "portapack_shared_memory.hpp"
#include "file_path.hpp"
namespace ui {
static const char16_t* firmware_folder = u"/FIRMWARE";
// Firmware image validation
static const char* hackrf_magic = "HACKRFFW";
#define FIRMWARE_INFO_AREA_OFFSET 0x400
#define FIRST_CHECKSUM_NIGHTLY 240125
Thread* FlashUtilityView::thread{nullptr};
static constexpr size_t max_filename_length = 26;
bool valid_firmware_file(std::filesystem::path::string_type path) {
File firmware_file;
bool require_checksum{false};
uint32_t read_buffer[128];
uint32_t checksum{(uint32_t)~FLASH_EXPECTED_CHECKSUM}; // initializing to invalid checksum in case file can't be read
uint32_t checksum{FLASH_CHECKSUM_ERROR}; // initializing to invalid checksum in case file can't be read
static_assert((FIRMWARE_INFO_AREA_OFFSET % sizeof(read_buffer)) == 0, "Read buffer size must divide evenly into FIRMWARE_INFO_AREA_OFFSET");
// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
auto result = firmware_file.open(path.c_str());
if (!result.is_valid()) {
checksum = 0;
for (uint32_t i = 0; i < FLASH_ROM_SIZE / sizeof(read_buffer); i++) {
for (uint32_t offset = 0; offset < FLASH_ROM_SIZE; offset += sizeof(read_buffer)) {
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
// if file is smaller than 1MB, assume it's a downgrade to an old FW version and ignore the checksum
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
checksum = FLASH_EXPECTED_CHECKSUM;
// File was smaller than 1MB:
// If version is such that the file SHOULD have been 1MB, call it a checksum error (otherwise say it's OK).
checksum = (require_checksum) ? FLASH_CHECKSUM_ERROR : FLASH_EXPECTED_CHECKSUM;
break;
}
// Did we just read the firmware info area?
if (offset == FIRMWARE_INFO_AREA_OFFSET) {
// If there's no info area (missing HACKRFFW signature), it could be an ancient FW version (so skipping check)
if (memcmp(read_buffer, hackrf_magic, 8) == 0) {
char* version_string = (char*)&read_buffer[4];
// Require a 1MB firmware image with a valid checksum if release version >=v2.x or nightly >n_240125
if (((version_string[0] == 'v') && (std::atoi(&version_string[1]) >= 2)) ||
((version_string[0] == 'n') && (version_string[1] == '_') && (std::atoi(&version_string[2]) >= FIRST_CHECKSUM_NIGHTLY)))
require_checksum = true;
}
}
checksum += simple_checksum((uint32_t)read_buffer, sizeof(read_buffer));
}
}
@@ -62,30 +82,30 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
ensure_directory(firmware_folder);
ensure_directory(apps_dir);
ensure_directory(firmware_dir);
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.bin")) {
auto filename = entry.path().filename();
auto path = entry.path().native();
auto add_firmware_items = [&](
const std::filesystem::path& folder_path,
const std::filesystem::path& wild,
ui::Color color) {
for (const auto& entry : std::filesystem::directory_iterator(folder_path, wild)) {
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);
}});
}
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.tar")) {
auto filename = entry.path().filename();
auto path = entry.path().native();
menu_view.add_item({filename.string().substr(0, max_filename_length),
color,
&bitmap_icon_temperature,
[this, path](KeyEvent) {
this->firmware_selected(path);
}});
}
};
menu_view.add_item({filename.string().substr(0, max_filename_length),
ui::Color::purple(),
&bitmap_icon_temperature,
[this, path](KeyEvent) {
this->firmware_selected(path);
}});
}
add_firmware_items(firmware_dir, u"*.bin", ui::Theme::getInstance()->fg_red->foreground);
add_firmware_items(firmware_dir, u"*.tar", ui::Theme::getInstance()->fg_cyan->foreground);
// add_firmware_items(user_firmware_folder,u"*.bin", ui::Theme::getInstance()->fg_cyan->foreground);
}
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
@@ -95,7 +115,7 @@ void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path
YESNO,
[this, path](bool choice) {
if (choice) {
std::u16string full_path = std::u16string(u"FIRMWARE/") + path;
std::filesystem::path::string_type full_path = firmware_dir.native() + u"/" + path;
this->flash_firmware(full_path);
}
});
@@ -114,12 +134,12 @@ std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::strin
//
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
ui::Color::black());
Theme::getInstance()->bg_darkest->background);
painter.draw_string({12, 24}, this->nav_.style(), "Unpacking TAR file...");
auto res = UnTar::untar(path, [this](const std::string fileName) {
ui::Painter painter;
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, Theme::getInstance()->bg_darkest->background);
painter.draw_string({0, 60}, this->nav_.style(), fileName);
});
return res;
@@ -133,18 +153,18 @@ bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
}
if (path.empty() || !valid_firmware_file(path.c_str())) {
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
painter.draw_string({0, 60}, Styles::red, "BAD FIRMWARE FILE");
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, Theme::getInstance()->bg_darkest->background);
painter.draw_string({0, 60}, *Theme::getInstance()->fg_red, "BAD FIRMWARE FILE OR W/R ERR");
chThdSleepMilliseconds(5000);
return false;
}
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
ui::Color::black());
Theme::getInstance()->bg_darkest->background);
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, 64}, this->nav_.style(), "Please wait while LED RX");
painter.draw_string({12, 84}, this->nav_.style(), "is on and TX is flashing.");
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
@@ -157,4 +177,4 @@ void FlashUtilityView::focus() {
menu_view.focus();
}
} /* namespace ui */
} /* namespace ui */
@@ -35,6 +35,7 @@
#define FLASH_ROM_SIZE 1048576
#define FLASH_STARTING_ADDRESS 0x00000000
#define FLASH_EXPECTED_CHECKSUM 0x00000000
#define FLASH_CHECKSUM_ERROR 0xFFFFFFFF
namespace ui {
@@ -56,7 +57,7 @@ class FlashUtilityView : public View {
static Thread* thread;
Labels labels{
{{4, 4}, "Select firmware to flash:", Color::white()}};
{{4, 4}, "Select firmware to flash:", Theme::getInstance()->bg_darkest->foreground}};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
+7 -7
View File
@@ -29,8 +29,8 @@
#include "rtc_time.hpp"
#include "tone_key.hpp"
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include <memory>
@@ -228,7 +228,7 @@ void FrequencyManagerView::on_edit_freq() {
void FrequencyManagerView::on_edit_desc() {
temp_buffer_ = current_entry().description;
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
auto entry = current_entry();
entry.description = std::move(new_desc);
db_.replace_entry(current_index(), entry);
@@ -415,16 +415,16 @@ void FrequencyEditView::refresh_ui() {
auto is_repeater = entry_.type == freqman_type::Repeater;
auto has_freq_b = is_range || is_ham || is_repeater;
field_freq_b.set_style(has_freq_b ? &Styles::white : &Styles::grey);
field_step.set_style(is_range ? &Styles::white : &Styles::grey);
field_tone.set_style(is_ham ? &Styles::white : &Styles::grey);
field_freq_b.set_style(has_freq_b ? Theme::getInstance()->bg_darkest : Theme::getInstance()->fg_medium);
field_step.set_style(is_range ? Theme::getInstance()->bg_darkest : Theme::getInstance()->fg_medium);
field_tone.set_style(is_ham ? Theme::getInstance()->bg_darkest : Theme::getInstance()->fg_medium);
if (is_valid(entry_)) {
text_validation.set("Valid");
text_validation.set_style(&Styles::green);
text_validation.set_style(Theme::getInstance()->fg_green);
} else {
text_validation.set("Error");
text_validation.set_style(&Styles::red);
text_validation.set_style(Theme::getInstance()->fg_red);
}
}
+18 -20
View File
@@ -40,8 +40,6 @@ class FreqManBaseView : public View {
void focus() override;
static constexpr size_t desc_edit_max = 0x80;
protected:
using options_t = OptionsField::options_t;
@@ -63,11 +61,11 @@ class FreqManBaseView : public View {
/* The top section (category) is 20px tall. */
Labels label_category{
{{0, 2}, "Category:", Color::light_grey()}};
{{0, 2}, "F:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_category{
{9 * 8, 2},
14 /* length */,
{3 * 8, 2},
20 /* length */,
{}};
FreqManUIList freqlist_view{
@@ -99,7 +97,7 @@ class FrequencySaveView : public FreqManBaseView {
0};
Labels labels{
{{0 * 8, 6 * 16}, "Description:", Color::white()}};
{{0 * 8, 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}};
TextField field_description{
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
@@ -139,14 +137,14 @@ class FrequencyManagerView : public FreqManBaseView {
{23 * 8, 0 * 16, 7 * 4, 20},
{},
&bitmap_icon_new_file,
Color::white(),
Theme::getInstance()->bg_darkest->foreground,
true};
NewButton button_del_category{
{26 * 8 + 4, 0 * 16, 7 * 4, 20},
{},
&bitmap_icon_trash,
Color::red(),
Theme::getInstance()->fg_red->foreground,
true};
Button button_edit_entry{
@@ -155,7 +153,7 @@ class FrequencyManagerView : public FreqManBaseView {
Rectangle rect_padding{
{15 * 8, 14 * 16 - 4, 15 * 8, 1 * 16 + 4},
Color::grey()};
Theme::getInstance()->fg_medium->background};
Button button_edit_freq{
{0 * 8, 15 * 16, 15 * 8, 2 * 16},
@@ -169,14 +167,14 @@ class FrequencyManagerView : public FreqManBaseView {
{15 * 8, 15 * 16, 7 * 8 + 4, 2 * 16},
{},
&bitmap_icon_add,
Color::white(),
Theme::getInstance()->bg_darkest->foreground,
true};
NewButton button_del_entry{
{22 * 8 + 4, 15 * 16, 7 * 8 + 4, 2 * 16},
{},
&bitmap_icon_delete,
Color::red(),
Theme::getInstance()->fg_red->foreground,
true};
};
@@ -202,15 +200,15 @@ class FrequencyEditView : public View {
void populate_tone_options();
Labels labels{
{{5 * 8, 1 * 16}, "Edit Frequency Entry", Color::white()},
{{0 * 8, 3 * 16}, "Entry Type :", Color::light_grey()},
{{0 * 8, 4 * 16}, "Frequency A:", Color::light_grey()},
{{0 * 8, 5 * 16}, "Frequency B:", Color::light_grey()},
{{0 * 8, 6 * 16}, "Modulation :", Color::light_grey()},
{{0 * 8, 7 * 16}, "Bandwidth :", Color::light_grey()},
{{0 * 8, 8 * 16}, "Step :", Color::light_grey()},
{{0 * 8, 9 * 16}, "Tone Freq :", Color::light_grey()},
{{0 * 8, 10 * 16}, "Description:", Color::light_grey()},
{{5 * 8, 1 * 16}, "Edit Frequency Entry", Theme::getInstance()->bg_darkest->foreground},
{{0 * 8, 3 * 16}, "Entry Type :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Frequency A:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Frequency B:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Modulation :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Bandwidth :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Step :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 9 * 16}, "Tone Freq :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "Description:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_type{
+2 -1
View File
@@ -27,6 +27,7 @@
#include "portapack_persistent_memory.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include "ui_freqman.hpp"
@@ -134,7 +135,7 @@ FskxRxMainView::FskxRxMainView(NavigationView& nav)
field_frequency.set_value(initial_target_frequency);
deviation_frequency.set_value(initial_deviation);
logger.append(LOG_ROOT_DIR "/FSKRX.TXT");
logger.append(logs_dir / u"FSKRX.TXT");
baseband::set_fsk(initial_deviation);
+4 -5
View File
@@ -30,7 +30,6 @@
#include "ui_record_view.hpp"
#include "ui_rssi.hpp"
#include "ui_spectrum.hpp"
#include "ui_styles.hpp"
#include "ui_tabview.hpp"
#include "app_settings.hpp"
@@ -42,7 +41,7 @@
class FskRxLogger {
public:
Optional<File::Error> append(const std::string& filename) {
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
@@ -117,8 +116,8 @@ class FskxRxMainView : public View {
FskRxAppConsoleView view_data{nav_, view_rect};
TabView tab_view{
{"Data", Color::yellow(), &view_data},
{"Stream", Color::cyan(), &view_stream}};
{"Data", Theme::getInstance()->fg_yellow->foreground, &view_data},
{"Stream", Theme::getInstance()->fg_cyan->foreground, &view_stream}};
void refresh_ui(rf::Frequency f);
void on_packet(uint32_t value, bool is_data);
@@ -148,7 +147,7 @@ class FskxRxMainView : public View {
{19 * 8 - 4, 40, 6 * 8, 4}};
Labels labels{
{{0 * 8, 3 * 16}, "Deviation:", Color::light_grey()},
{{0 * 8, 3 * 16}, "Deviation:", Theme::getInstance()->fg_light->foreground},
};
FrequencyField deviation_frequency{
+6 -5
View File
@@ -25,6 +25,7 @@
#include "complex.hpp"
#include "portapack.hpp"
#include "ui_fileman.hpp"
#include "file_path.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -47,14 +48,14 @@ IQTrimView::IQTrimView(NavigationView& nav)
field_path.on_select = [this](TextField&) {
auto open_view = nav_.push<FileLoadView>(".C*");
open_view->push_dir(u"CAPTURES");
open_view->push_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
open_file(path);
};
};
text_samples.set_style(&Styles::light_grey);
text_max.set_style(&Styles::light_grey);
text_samples.set_style(Theme::getInstance()->fg_light);
text_max.set_style(Theme::getInstance()->fg_light);
field_start.on_change = [this](int32_t v) {
if (field_end.value() < v)
@@ -148,9 +149,9 @@ void IQTrimView::refresh_ui() {
// show max power in red if amplification is too high, causing clipping
uint32_t clipping_limit = (fs::capture_file_sample_size(path_) == sizeof(complex8_t)) ? 0x80 : 0x8000;
if ((field_amplify.value() * info_->max_iq) > clipping_limit)
text_max.set_style(&Styles::red);
text_max.set_style(Theme::getInstance()->fg_red);
else
text_max.set_style(&Styles::light_grey);
text_max.set_style(Theme::getInstance()->fg_light);
set_dirty();
}
+13 -14
View File
@@ -28,7 +28,6 @@
#include "optional.hpp"
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_styles.hpp"
#include "ui_widget.hpp"
#include <array>
@@ -41,21 +40,21 @@ class TrimProgressUI {
public:
void show_reading() {
clear();
p.draw_string({6 * 8, 5 * 16}, Styles::yellow, "Reading Capture...");
p.draw_string({6 * 8, 5 * 16}, *Theme::getInstance()->fg_yellow, "Reading Capture...");
}
void show_trimming() {
clear();
p.draw_string({5 * 8, 5 * 16}, Styles::yellow, "Trimming Capture...");
p.draw_string({5 * 8, 5 * 16}, *Theme::getInstance()->fg_yellow, "Trimming Capture...");
}
void show_progress(uint8_t percent) {
auto width = percent * screen_width / 100;
p.draw_hline({0, 6 * 16 + 2}, width, Color::yellow());
p.draw_hline({0, 6 * 16 + 2}, width, Theme::getInstance()->fg_yellow->foreground);
}
void clear() {
p.fill_rectangle({0 * 8, 4 * 16, screen_width, 3 * 16}, Color::black());
p.fill_rectangle({0 * 8, 4 * 16, screen_width, 3 * 16}, Theme::getInstance()->bg_darkest->background);
}
auto get_callback() {
@@ -101,15 +100,15 @@ class IQTrimView : public View {
TrimProgressUI progress_ui{};
Labels labels{
{{0 * 8, 0 * 16}, "Capture File:", Color::light_grey()},
{{0 * 8, 6 * 16}, "Start :", Color::light_grey()},
{{0 * 8, 7 * 16}, "End :", Color::light_grey()},
{{0 * 8, 8 * 16}, "Samples:", Color::light_grey()},
{{0 * 8, 9 * 16}, "Max Pwr:", Color::light_grey()},
{{0 * 8, 10 * 16}, "Cutoff :", Color::light_grey()},
{{12 * 8, 10 * 16}, "%", Color::light_grey()},
{{0 * 8, 12 * 16}, "Amplify:", Color::light_grey()},
{{10 * 8, 12 * 16}, "x", Color::light_grey()},
{{0 * 8, 0 * 16}, "Capture File:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Start :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "End :", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Samples:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 9 * 16}, "Max Pwr:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "Cutoff :", Theme::getInstance()->fg_light->foreground},
{{12 * 8, 10 * 16}, "%", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 12 * 16}, "Amplify:", Theme::getInstance()->fg_light->foreground},
{{10 * 8, 12 * 16}, "x", Theme::getInstance()->fg_light->foreground},
};
TextField field_path{
+128 -29
View File
@@ -27,6 +27,7 @@
#include "baseband_api.hpp"
#include "file.hpp"
#include "oversample.hpp"
#include "ui_font_fixed_8x16.hpp"
using namespace portapack;
using namespace tonekey;
@@ -34,11 +35,37 @@ using portapack::memory::map::backup_ram;
namespace ui {
// Function to map the value from one range to another
int32_t LevelView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
}
void LevelView::m4_manage_stat_update() {
if (audio_mode) {
if (radio_mode == WFM_MODULATION || radio_mode == SPEC_MODULATION) {
shared_memory.request_m4_performance_counter = 1;
} else {
shared_memory.request_m4_performance_counter = 2;
}
if (radio_mode == SPEC_MODULATION) {
beep = true;
}
} else {
shared_memory.request_m4_performance_counter = 2;
if (radio_mode == SPEC_MODULATION) {
beep = false;
baseband::request_beep_stop();
}
}
}
void LevelView::focus() {
button_frequency.focus();
}
LevelView::~LevelView() {
// reset performance counters request to default
shared_memory.request_m4_performance_counter = 0;
receiver_model.disable();
baseband::shutdown();
}
@@ -58,16 +85,17 @@ LevelView::LevelView(NavigationView& nav)
&text_ctcss,
&freq_stats_rssi,
&freq_stats_db,
&audio_mode,
&freq_stats_rx,
&text_beep_squelch,
&field_beep_squelch,
&field_audio_mode,
&peak_mode,
&rssi,
&rssi_graph});
// activate vertical bar mode
rssi.set_vertical_rssi(true);
change_mode(NFM_MODULATION); // Start on AM
field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector
freq_ = receiver_model.target_frequency();
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
@@ -80,6 +108,11 @@ LevelView::LevelView(NavigationView& nav)
};
};
field_beep_squelch.set_value(beep_squelch);
field_beep_squelch.on_change = [this](int32_t v) {
beep_squelch = v;
};
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);
@@ -95,17 +128,14 @@ LevelView::LevelView(NavigationView& nav)
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
};
freqman_set_modulation_option(field_mode);
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();
}
};
field_mode.set_by_value(radio_mode); // Reflect the mode into the manual selector
field_bw.set_selected_index(radio_bw);
rssi_resolution.on_change = [this](size_t, OptionsField::value_t v) {
if (v != -1) {
@@ -113,15 +143,18 @@ LevelView::LevelView(NavigationView& nav)
}
};
audio_mode.on_change = [this](size_t, OptionsField::value_t v) {
field_audio_mode.on_change = [this](size_t, OptionsField::value_t v) {
audio_mode = v;
if (v == 0) {
audio::output::stop();
} else if (v == 1) {
audio::set_rate(audio_sampling_rate);
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
} else {
}
m4_manage_stat_update(); // rx_sat hack
};
field_audio_mode.set_selected_index(audio_mode);
peak_mode.on_change = [this](size_t, OptionsField::value_t v) {
if (v == 0) {
@@ -135,17 +168,18 @@ LevelView::LevelView(NavigationView& nav)
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(&Styles::white);
freq_stats_db.set_style(&Styles::white);
freq_stats_rssi.set_style(Theme::getInstance()->bg_darkest);
freq_stats_db.set_style(Theme::getInstance()->bg_darkest);
freq_stats_rx.set_style(Theme::getInstance()->bg_darkest);
}
void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
static int16_t last_max_db = -1000;
static int16_t last_min_rssi = -1000;
static int16_t last_avg_rssi = -1000;
static int16_t last_max_rssi = -1000;
static int16_t last_max_db = 0;
static uint8_t last_min_rssi = 0;
static uint8_t last_avg_rssi = 0;
static uint8_t last_max_rssi = 0;
static uint8_t last_rx_sat = 0;
rssi_graph.add_values(rssi.get_min(), rssi.get_avg(), rssi.get_max(), statistics.max_db);
@@ -159,51 +193,99 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
last_min_rssi = rssi_graph.get_graph_min();
last_avg_rssi = rssi_graph.get_graph_avg();
last_max_rssi = rssi_graph.get_graph_max();
freq_stats_rssi.set("RSSI: " + to_string_dec_int(last_min_rssi) + "/" + to_string_dec_int(last_avg_rssi) + "/" + to_string_dec_int(last_max_rssi) + ",dt: " + to_string_dec_int(rssi_graph.get_graph_delta()));
freq_stats_rssi.set("RSSI: " + to_string_dec_uint(last_min_rssi) + "/" + to_string_dec_uint(last_avg_rssi) + "/" + to_string_dec_uint(last_max_rssi));
}
if (beep && statistics.max_db > beep_squelch) {
baseband::request_audio_beep(map(statistics.max_db, -100, 20, 400, 2600), 24000, 150);
}
// refresh sat
if (radio_mode == SPEC_MODULATION || (radio_mode == WFM_MODULATION && audio_mode == 1)) {
Style style_freq_stats_rx{
.font = font::fixed_8x16,
.background = {55, 55, 55},
.foreground = {155, 155, 155},
};
freq_stats_rx.set_style(&style_freq_stats_rx);
freq_stats_rx.set("RxSat off");
return;
}
uint8_t rx_sat = ((uint32_t)shared_memory.m4_performance_counter) * 100 / 127;
if (last_rx_sat != rx_sat) {
last_rx_sat = rx_sat;
uint8_t br = 0;
uint8_t bg = 0;
uint8_t bb = 0;
if (rx_sat <= 80) {
bg = (255 * rx_sat) / 80;
bb = 255 - bg;
} else if (rx_sat > 80) {
br = (255 * (rx_sat - 80)) / 20;
bg = 255 - br;
}
Style style_freq_stats_rx{
.font = font::fixed_8x16,
.background = {br, bg, bb},
.foreground = {255, 255, 255},
};
freq_stats_rx.set_style(&style_freq_stats_rx);
freq_stats_rx.set("RxSat: " + to_string_dec_uint(rx_sat) + "%");
}
} /* 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; };
radio_mode = new_mod;
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
switch (new_mod) {
case AM_MODULATION:
audio_sampling_rate = audio::Rate::Hz_12000;
freqman_set_bandwidth_option(new_mod, field_bw);
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_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); };
// bw DSB (0) default
field_bw.set_by_value(0);
text_ctcss.set(" ");
receiver_model.set_am_configuration(0);
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_am_configuration(n); };
break;
case NFM_MODULATION:
audio_sampling_rate = audio::Rate::Hz_24000;
freqman_set_bandwidth_option(new_mod, field_bw);
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_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); };
// bw 16k (2) default
field_bw.set_by_value(2);
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_nbfm_configuration(n); };
break;
case WFM_MODULATION:
audio_sampling_rate = audio::Rate::Hz_48000;
freqman_set_bandwidth_option(new_mod, field_bw);
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_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
// bw 200k (0) only/default
// bw 200k (0) default
field_bw.set_by_value(0);
text_ctcss.set(" ");
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_wfm_configuration(n); };
break;
case SPEC_MODULATION:
audio_sampling_rate = audio::Rate::Hz_24000;
freqman_set_bandwidth_option(new_mod, field_bw);
baseband::run_image(portapack::spi_flash::image_tag_capture);
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
// 12k5 (0) default
field_bw.on_change = [this](size_t index, OptionsField::value_t sampling_rate) {
radio_bw = index;
// Baseband needs to know the desired sampling and oversampling rates.
baseband::set_sample_rate(sampling_rate, get_oversample_rate(sampling_rate));
// The radio needs to know the effective sampling rate.
auto actual_sampling_rate = get_actual_sample_rate(sampling_rate);
receiver_model.set_sampling_rate(actual_sampling_rate);
@@ -218,6 +300,18 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
}
if (new_mod != NFM_MODULATION) {
text_ctcss.set(" ");
}
m4_manage_stat_update(); // rx_sat hack
if (audio_mode) {
audio::set_rate(audio_sampling_rate);
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
}
receiver_model.enable();
return step_mode.selected_index();
}
@@ -229,4 +323,9 @@ void LevelView::handle_coded_squelch(const uint32_t value) {
text_ctcss.set(" ");
}
void LevelView::on_freqchg(int64_t freq) {
receiver_model.set_target_frequency(freq);
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
}
} /* namespace ui */
+58 -22
View File
@@ -38,7 +38,6 @@
#include "ui_mictx.hpp"
#include "ui_receiver.hpp"
#include "ui_spectrum.hpp"
#include "ui_styles.hpp"
namespace ui {
@@ -55,20 +54,34 @@ class LevelView : public View {
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_level", app_settings::Mode::RX};
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
size_t change_mode(freqman_index_t mod_type);
void on_statistics_update(const ChannelStatistics& statistics);
void set_display_freq(int64_t freq);
void m4_manage_stat_update(); // to finely adjust the RxSaturation usage
// TODO: needed?
int32_t db{0};
rf::Frequency freq_ = {0};
bool beep = false;
uint8_t radio_mode = 0;
uint8_t radio_bw = 0;
uint8_t audio_mode = 0;
int32_t beep_squelch = 0;
audio::Rate audio_sampling_rate = audio::Rate::Hz_48000;
app_settings::SettingsManager settings_{
"rx_level",
app_settings::Mode::RX,
{
{"beep_squelch"sv, &beep_squelch},
{"audio_mode"sv, &audio_mode},
{"radio_mode"sv, &radio_mode},
{"radio_bw"sv, &radio_bw},
}};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
{{0 * 8, 1 * 16}, "BW: MODE: S: ", Color::light_grey()},
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "BW: MODE: S: ", Theme::getInstance()->fg_light->foreground},
};
LNAGainField field_lna{
@@ -102,28 +115,32 @@ class LevelView : public View {
{0 * 8, 2 * 16 + 8, 15 * 8, 1 * 8},
""};
OptionsField audio_mode{
OptionsField field_audio_mode{
{21 * 8, 1 * 16},
9,
{
{"audio off", 0},
{"audio on", 1}
//{"tone on", 2},
//{"tone off", 3},
}};
{{"audio off", 0},
{"audio on", 1}}};
Text text_ctcss{
{22 * 8, 3 * 16 + 4, 8 * 8, 1 * 8},
""};
Text text_beep_squelch{
{21 * 8, 3 * 16 + 4, 4 * 8, 1 * 8},
"Bip>"};
// RSSI: XX/XX/XXX,dt: XX
NumberField field_beep_squelch{
{25 * 8, 3 * 16 + 4},
4,
{-100, 20},
1,
' ',
};
// RSSI: XX/XX/XXX
Text freq_stats_rssi{
{0 * 8, 3 * 16 + 4, 22 * 8, 14},
{0 * 8, 3 * 16 + 4, 15 * 8, 1 * 16},
};
// Power: -XXX db
Text freq_stats_db{
{0 * 8, 4 * 16 + 4, 14 * 8, 14},
{0 * 8, 4 * 16 + 4, 15 * 8, 1 * 16},
};
OptionsField peak_mode{
@@ -138,6 +155,7 @@ class LevelView : public View {
{"peak:5s", 5000},
{"peak:10s", 10000},
}};
OptionsField rssi_resolution{
{44 + 20 * 8, 4 * 16 + 4},
4,
@@ -149,18 +167,36 @@ class LevelView : public View {
{"240x", 240},
}};
// RxSat: XX%
Text freq_stats_rx{
{0 * 8, 5 * 16 + 4, 10 * 8, 1 * 16},
};
Text text_ctcss{
{12 * 8, 5 * 16 + 4, 8 * 8, 1 * 8},
""};
RSSIGraph rssi_graph{
// 240x320 =>
{0, 5 * 16 + 4, 240 - 5 * 8, 320 - (5 * 16 + 4)},
{0, 6 * 16 + 8, 240 - 5 * 8, 320 - (6 * 16)},
};
RSSI rssi{
// 240x320 =>
{240 - 5 * 8, 5 * 16 + 4, 5 * 8, 320 - (5 * 16 + 4)},
{240 - 5 * 8, 6 * 16 + 8, 5 * 8, 320 - (6 * 16)},
};
void handle_coded_squelch(const uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
@@ -25,6 +25,8 @@
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include "audio.hpp"
#include "file_path.hpp"
using namespace portapack;
@@ -34,11 +36,39 @@ void GlassView::focus() {
}
GlassView::~GlassView() {
audio::output::stop();
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
receiver_model.disable();
baseband::shutdown();
}
// Function to map the value from one range to another
int32_t GlassView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
}
void GlassView::update_display_beep() {
if (beep_enabled) {
button_beep_squelch.set_style(Theme::getInstance()->fg_green);
// bip-XXdb
button_beep_squelch.set_text("bip" + to_string_dec_int(beep_squelch, 3) + "db");
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
} else {
button_beep_squelch.set_style(Theme::getInstance()->bg_darkest);
button_beep_squelch.set_text("bip OFF ");
}
}
void GlassView::manage_beep_audio() {
if (beep_enabled) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
} else {
baseband::request_beep_stop();
audio::output::stop();
}
}
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
if (mode == LOOKING_GLASS_SINGLEPASS) {
// <20MHz spectrum mode
@@ -173,6 +203,8 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
// we actually need SCREEN_W (240) of those bins
for (uint8_t bin = 0; bin < bin_length; bin++) {
get_max_power(spectrum, bin, max_power);
if (max_power > range_max_power)
range_max_power = max_power;
// process dc spike if enable
if (bin == 119) {
uint8_t next_max_power = 0;
@@ -184,14 +216,21 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
}
}
// process actual bin
if (process_bins(&max_power) == true)
if (process_bins(&max_power)) {
int8_t power = map(range_max_power, 0, 255, -100, 20);
if (power >= beep_squelch) {
baseband::request_audio_beep(map(range_max_power, 0, 256, 400, 2600), 24000, 250);
}
range_max_power = 0;
return; // new line signaled, return
}
}
if (mode != LOOKING_GLASS_SINGLEPASS) {
f_center += looking_glass_step;
retune();
} else
} else {
baseband::spectrum_streaming_start();
}
}
void GlassView::on_hide() {
@@ -266,10 +305,10 @@ void GlassView::plot_marker(uint8_t pos) {
{
shift_y = 16;
}
portapack::display.fill_rectangle({0, 100 + shift_y, SCREEN_W, 8}, Color::black()); // Clear old marker and whole marker rectangle btw
portapack::display.fill_rectangle({pos - 2, 100 + shift_y, 5, 3}, Color::red()); // Red marker top
portapack::display.fill_rectangle({pos - 1, 103 + shift_y, 3, 3}, Color::red()); // Red marker middle
portapack::display.fill_rectangle({pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom
portapack::display.fill_rectangle({0, 100 + shift_y, SCREEN_W, 8}, Theme::getInstance()->bg_darkest->background); // Clear old marker and whole marker rectangle btw
portapack::display.fill_rectangle({pos - 2, 100 + shift_y, 5, 3}, Theme::getInstance()->fg_red->foreground); // Red marker top
portapack::display.fill_rectangle({pos - 1, 103 + shift_y, 3, 3}, Theme::getInstance()->fg_red->foreground); // Red marker middle
portapack::display.fill_rectangle({pos, 106 + shift_y, 1, 2}, Theme::getInstance()->fg_red->foreground); // Red marker bottom
}
void GlassView::update_min(int32_t v) {
@@ -308,10 +347,10 @@ void GlassView::update_max(int32_t v) {
void GlassView::update_range_field() {
if (!locked_range) {
field_range.set_style(&Styles::white);
field_range.set_style(Theme::getInstance()->bg_darkest);
field_range.set_text(" " + to_string_dec_uint(search_span) + " ");
} else {
field_range.set_style(&Styles::red);
field_range.set_style(Theme::getInstance()->fg_red);
field_range.set_text(">" + to_string_dec_uint(search_span) + "<");
}
}
@@ -327,17 +366,20 @@ GlassView::GlassView(
&field_lna,
&field_vga,
&field_range,
&steps_config,
//&steps_config,
&scan_type,
&view_config,
&level_integration,
&field_volume,
&filter_config,
&field_rf_amp,
&range_presets,
&button_beep_squelch,
&field_marker,
&field_trigger,
&button_jump,
&button_rst,
&field_rx_iq_phase_cal,
&freq_stats});
load_presets(); // Load available presets from TXT files (or default).
@@ -369,12 +411,12 @@ GlassView::GlassView(
};
};
steps_config.on_change = [this](size_t, OptionsField::value_t v) {
/*steps_config.on_change = [this](size_t, OptionsField::value_t v) {
field_frequency_min.set_step(v);
field_frequency_max.set_step(v);
steps = v;
};
steps_config.set_selected_index(0); // 1 Mhz step.
steps_config.set_selected_index(0); // 1 Mhz step.*/
scan_type.on_change = [this](size_t, OptionsField::value_t v) {
mode = v;
@@ -474,6 +516,13 @@ GlassView::GlassView(
reset_live_view();
};
field_rx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
field_rx_iq_phase_cal.set_value(get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini
field_rx_iq_phase_cal.on_change = [this](int32_t v) {
set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini
};
set_spec_iq_phase_calibration_value(get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
display.scroll_set_area(109, 319);
// trigger:
@@ -489,11 +538,52 @@ GlassView::GlassView(
receiver_model.set_baseband_bandwidth(looking_glass_bandwidth); // 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();
button_beep_squelch.on_select = [this](ButtonWithEncoder& button) {
(void)button;
beep_enabled = 1 - beep_enabled;
manage_beep_audio();
update_display_beep();
};
button_beep_squelch.on_change = [this]() {
int new_beep_squelch = beep_squelch + button_beep_squelch.get_encoder_delta();
if (new_beep_squelch < -99)
new_beep_squelch = -99;
if (new_beep_squelch > 20)
new_beep_squelch = 20;
beep_squelch = new_beep_squelch;
button_beep_squelch.set_encoder_delta(0);
update_display_beep();
};
manage_beep_audio();
update_display_beep();
}
void GlassView::on_freqchg(int64_t freq) {
int64_t half_range = abs(field_frequency_max.value() - field_frequency_max.value()) / 2;
if (half_range < 1) {
half_range = 1;
}
range_presets.set_selected_index(0); // Manual
update_min(freq - half_range);
update_max(freq + half_range);
on_range_changed();
}
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
return iq_phase_calibration_value;
}
void GlassView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
iq_phase_calibration_value = cal_value;
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
}
void GlassView::load_presets() {
File presets_file;
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
auto error = presets_file.open(looking_glass_dir / u"PRESETS.TXT");
presets_db.clear();
// Add the "Manual" entry.
@@ -33,7 +33,6 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
@@ -69,6 +68,9 @@ class GlassView : public View {
void on_hide() override;
void focus() override;
uint8_t get_spec_iq_phase_calibration_value();
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
private:
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
@@ -79,8 +81,11 @@ class GlassView : public View {
uint8_t filter_index = 0; // OFF
uint8_t trigger = 32;
uint8_t mode = LOOKING_GLASS_FASTSCAN;
uint8_t live_frequency_view = 0; // Spectrum
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
uint8_t live_frequency_view = 0; // Spectrum
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
int32_t beep_squelch = 20; // range from -100 to +20, >=20 disabled
bool beep_enabled = false; // activate on bip button click
app_settings::SettingsManager settings_{
"rx_glass"sv,
app_settings::Mode::RX,
@@ -93,6 +98,9 @@ class GlassView : public View {
{"scan_mode"sv, &mode},
{"freq_view"sv, &live_frequency_view},
{"freq_integrate"sv, &live_frequency_integrate},
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
{"beep_squelch"sv, &beep_squelch},
{"beep_enabled"sv, &beep_enabled},
}};
struct preset_entry {
@@ -101,7 +109,11 @@ class GlassView : public View {
std::string label{};
};
void on_freqchg(int64_t freq);
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
std::vector<preset_entry> presets_db{};
void manage_beep_audio();
void update_display_beep();
void update_min(int32_t v);
void update_max(int32_t v);
void update_range_field();
@@ -148,6 +160,8 @@ class GlassView : public View {
int32_t steps = 1;
bool locked_range = false;
uint8_t range_max_power = 0;
uint8_t range_max_power_counter = 0;
uint8_t max_power = 0;
rf::Frequency max_freq_hold = 0;
rf::Frequency last_max_freq = 0;
@@ -157,11 +171,12 @@ class GlassView : public View {
uint8_t ignore_dc = 0;
Labels labels{
{{0, 0 * 16}, "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()}};
{{0, 0 * 16}, "MIN: MAX: LNA VGA ", Theme::getInstance()->fg_light->foreground},
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Theme::getInstance()->fg_light->foreground},
{{0, 2 * 16}, "P:", Theme::getInstance()->fg_light->foreground},
{{0, 3 * 16}, "MARKER: MHz RXIQCAL", Theme::getInstance()->fg_light->foreground},
//{{0, 4 * 16}, "RES: STEPS:", Theme::getInstance()->fg_light->foreground}};
{{0, 4 * 16}, "RES: VOL:", Theme::getInstance()->fg_light->foreground}};
NumberField field_frequency_min{
{4 * 8, 0 * 16},
@@ -200,14 +215,26 @@ class GlassView : public View {
{28 * 8, 1 * 16}};
OptionsField range_presets{
{7 * 8, 2 * 16},
{2 * 8, 2 * 16},
20,
{}};
ButtonWithEncoder button_beep_squelch{
{240 - 8 * 8, 2 * 16 + 4, 8 * 8, 1 * 8},
""};
TextField field_marker{
{7 * 8, 3 * 16, 9 * 8, 16},
""};
NumberField field_rx_iq_phase_cal{
{28 * 8, 3 * 16},
2,
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
1,
' ',
};
NumberField field_trigger{
{4 * 8, 4 * 16},
3,
@@ -215,7 +242,10 @@ class GlassView : public View {
2,
' '};
OptionsField steps_config{
AudioVolumeField field_volume{
{13 * 8, 4 * 16}};
/*OptionsField steps_config{
{13 * 8, 4 * 16},
3,
{
@@ -225,7 +255,7 @@ class GlassView : public View {
{"100", 100},
{"250", 250},
{"500", 500},
}};
}};*/
OptionsField scan_type{
{17 * 8, 4 * 16},
@@ -278,6 +308,7 @@ class GlassView : public View {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
@@ -288,6 +319,13 @@ class GlassView : public View {
}
}
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} // namespace ui
#endif
+46 -39
View File
@@ -63,8 +63,8 @@ void MicTXView::update_vumeter() {
}
void MicTXView::update_tx_icon() {
tx_icon.set_foreground(transmitting ? Color::red() : Color::black());
tx_icon.set_background(transmitting ? Color::yellow() : Color::black());
tx_icon.set_foreground(transmitting ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->bg_darkest->background);
tx_icon.set_background(transmitting ? Theme::getInstance()->fg_yellow->foreground : Theme::getInstance()->bg_darkest->background);
}
void MicTXView::on_tx_progress(const bool done) {
@@ -130,9 +130,9 @@ void MicTXView::set_tx(bool enable) {
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
} else {
if (transmitting && rogerbeep_enabled) {
baseband::request_beep(); // Transmit the roger beep
transmitting = false; // And flag the end of the transmission so ...
} else { // (if roger beep was enabled, this will be executed after the beep ends transmitting.
baseband::request_roger_beep(); // Transmit the roger beep
transmitting = false; // Flag the end of the transmission (transmitter will be disabled after the beep)
} else {
transmitting = false;
configure_baseband();
transmitter_model.disable();
@@ -269,16 +269,24 @@ void MicTXView::set_rxbw_options(void) {
}
}
void MicTXView::set_rxbw_defaults(bool use_app_settings) {
void MicTXView::set_rxbw_defaults(bool use_app_settings) { // Initially in that function we set up rxbw, but now also txbw.
if (use_app_settings) {
field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
field_rxbw.set_by_value(rxbw_index);
} else if (mic_mod_index == MIC_MOD_NFM) {
field_bw.set_value(10); // NFM TX bw 10k, RX bw 16k (2) default
field_rxbw.set_by_value(2);
field_bw.set_value(10); // NFM TX bw 10k, RX bw 16k (index 2) default
field_bw.set_range(1, 60); // In NFM , FM , we are limitting index modulation range (0.08 ..5) ; (Ex max dev 60khz/12k = 5)
field_bw.set_step(1);
field_rxbw.set_by_value(2); // 16k from the three options (8k5,11k,16k)
} else if (mic_mod_index == MIC_MOD_WFM) {
field_bw.set_value(75); // WFM TX bw 75K, RX bw 200k (0) default
field_bw.set_value(75); // WFM TX bw 75K, RX bw 200k (index 0) default
field_bw.set_range(1, 150); // In our case Mod. Index range (1,67 ...12,5) ; 150k/12k=12,5
field_bw.set_step(1);
field_rxbw.set_by_value(0);
} else if ((mic_mod_index == MIC_MOD_USB) | (mic_mod_index == MIC_MOD_LSB)) {
field_bw.set_value(3); // In SSB by default let's limit TX_BW to 3kHz.
field_bw.set_range(2, 3); // User TXBW GUI range to modify that SSB TX_BW to limit SSB radiated spectrum.
field_bw.set_step(1);
}
// field_bw is hidden in other modulation cases
}
@@ -338,7 +346,7 @@ MicTXView::MicTXView(
&field_rxlna,
&field_rxvga,
&field_rxamp,
hackrf_r9 ? &field_tx_iq_phase_cal_2839 : &field_tx_iq_phase_cal_2837,
&field_tx_iq_phase_cal,
&tx_button,
&tx_icon});
@@ -372,19 +380,12 @@ MicTXView::MicTXView(
};
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
if (hackrf_r9) { // MAX2839 has 6 bits IQ CAL phasse adjustment.
field_tx_iq_phase_cal_2839.set_value(iq_phase_calibration_value);
field_tx_iq_phase_cal_2839.on_change = [this](int32_t v) {
iq_phase_calibration_value = v;
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
};
} else { // MAX2837 has 5 bits IQ CAL phase adjustment.
field_tx_iq_phase_cal_2837.set_value(iq_phase_calibration_value);
field_tx_iq_phase_cal_2837.on_change = [this](int32_t v) {
iq_phase_calibration_value = v;
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
};
}
field_tx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
field_tx_iq_phase_cal.set_value(iq_phase_calibration_value);
field_tx_iq_phase_cal.on_change = [this](int32_t v) {
iq_phase_calibration_value = v;
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
};
options_gain.on_change = [this](size_t, int32_t v) {
mic_gain_x10 = v;
@@ -489,7 +490,11 @@ MicTXView::MicTXView(
field_bw.hidden(false);
options_tone_key.hidden(false);
} else {
field_bw.hidden(true);
if ((mic_mod_index == MIC_MOD_USB) || (mic_mod_index == MIC_MOD_LSB)) {
field_bw.hidden(false);
} else {
field_bw.hidden(true);
}
options_tone_key.set_selected_index(0);
options_tone_key.hidden(true);
}
@@ -530,21 +535,6 @@ MicTXView::MicTXView(
};
check_mic_to_HP.set_value(mic_to_HP_enabled);
check_rxactive.on_select = [this](Checkbox&, bool v) {
// vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
check_mic_to_HP.hidden(rx_enabled); // Toggle Hide / show "Hear Mic" checkbox depending if we activate or not the receiver. (if RX on => no visible "Mic Hear" option)
if ((rx_enabled) && (transmitting))
check_mic_to_HP.set_value(transmitting); // Once we activate the "Rx audio" in reception time we disable "Hear Mic", but we allow it again in TX time.
if (rx_enabled)
check_va.set_value(false); // Disallow voice activation during RX audio (for now) - Future TODO: Should allow VOX during RX audio
rxaudio(v); // Activate-Deactivate audio RX (receiver) accordingly
set_dirty(); // Refresh interface
};
check_rxactive.set_value(rx_enabled);
tx_button.on_select = [this](Button&) {
if (!transmitting) {
set_tx(true);
@@ -597,12 +587,28 @@ MicTXView::MicTXView(
// Trigger receiver to update modulation.
if (rx_enabled)
receiver_model.set_squelch_level(receiver_model.squelch_level());
check_rxactive.on_select = [this](Checkbox&, bool v) {
// vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
check_mic_to_HP.hidden(rx_enabled); // Toggle Hide / show "Hear Mic" checkbox depending if we activate or not the receiver. (if RX on => no visible "Mic Hear" option)
if ((rx_enabled) && (transmitting))
check_mic_to_HP.set_value(transmitting); // Once we activate the "Rx audio" in reception time we disable "Hear Mic", but we allow it again in TX time.
if (rx_enabled)
check_va.set_value(false); // Disallow voice activation during RX audio (for now) - Future TODO: Should allow VOX during RX audio
rxaudio(v); // Activate-Deactivate audio RX (receiver) accordingly
set_dirty(); // Refresh interface
};
check_rxactive.set_value(rx_enabled);
}
MicTXView::MicTXView(
NavigationView& nav,
ReceiverModel::settings_t override)
: MicTXView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
// Try to use the modulation/bandwidth from RX settings.
// TODO: These concepts should be merged so there's only one.
switch (override.mode) {
@@ -623,6 +629,7 @@ MicTXView::MicTXView(
break;
}
field_frequency.set_value(override.frequency_app_override);
check_common_freq_tx_rx.set_value(true); // freq passed from other app is in tx_frequency, so set rx_frequency=tx_frequency
// TODO: bandwidth selection is tied too tightly to the UI
+23 -32
View File
@@ -118,7 +118,6 @@ class MicTXView : public View {
app_settings::SettingsManager settings_{
"tx_mic",
app_settings::Mode::RX_TX,
app_settings::Options::UseGlobalTargetFrequency,
{
{"mic_mod_index"sv, &mic_mod_index},
{"rxbw_index"sv, &rxbw_index},
@@ -148,27 +147,27 @@ class MicTXView : public View {
bool button_touch{false};
Labels labels_both{
{{3 * 8, 1 * 8}, "MIC-GAIN:", Color::light_grey()},
{{3 * 8, 3 * 8}, "F:", Color::light_grey()},
{{15 * 8, 3 * 8}, "FM TXBW: kHz", Color::light_grey()}, // to be more symetric and consistent to the below FM RXBW
{{18 * 8, (5 * 8)}, "Mode:", Color::light_grey()}, // now, no need to handle GAIN, Amp here It is handled by ui_transmitter.cpp
{{4 * 8, 10 * 8}, "LVL:", Color::light_grey()}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{{12 * 8, 10 * 8}, "ATT:", Color::light_grey()},
{{20 * 8, 10 * 8}, "DEC:", Color::light_grey()},
{{3 * 8, (13 * 8) - 5}, "TONE KEY:", Color::light_grey()},
{{3 * 8, (18 * 8) - 1}, "======== Receiver ========", Color::green()},
{{5 * 8, (23 * 8) + 2}, "VOL:", Color::light_grey()},
{{14 * 8, (23 * 8) + 2}, "RXBW:", Color::light_grey()}, // we remove the label "FM" because we will display all MOD types RX_BW.
{{20 * 8, (25 * 8) + 2}, "SQ:", Color::light_grey()},
{{5 * 8, (25 * 8) + 2}, "F_RX:", Color::light_grey()},
{{5 * 8, (27 * 8) + 2}, "LNA:", Color::light_grey()},
{{12 * 8, (27 * 8) + 2}, "VGA:", Color::light_grey()},
{{19 * 8, (27 * 8) + 2}, "AMP:", Color::light_grey()},
{{21 * 8, (31 * 8)}, "TX-IQ-CAL:", Color::light_grey()}};
{{3 * 8, 1 * 8}, "MIC-GAIN:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 3 * 8}, "F: MHz", Theme::getInstance()->fg_light->foreground},
{{18 * 8, 3 * 8}, "TXBW: kHz", Theme::getInstance()->fg_light->foreground}, // to be more symetric and consistent to the below FM RXBW
{{18 * 8, (5 * 8)}, "Mode:", Theme::getInstance()->fg_light->foreground}, // now, no need to handle GAIN, Amp here It is handled by ui_transmitter.cpp
{{4 * 8, 10 * 8}, "LVL:", Theme::getInstance()->fg_light->foreground}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Theme::getInstance()->fg_light->foreground },
{{12 * 8, 10 * 8}, "ATT:", Theme::getInstance()->fg_light->foreground},
{{20 * 8, 10 * 8}, "DEC:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, (13 * 8) - 5}, "TONE KEY:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, (18 * 8) - 1}, "======== Receiver ========", Theme::getInstance()->fg_green->foreground},
{{5 * 8, (23 * 8) + 2}, "VOL:", Theme::getInstance()->fg_light->foreground},
{{14 * 8, (23 * 8) + 2}, "RXBW:", Theme::getInstance()->fg_light->foreground}, // we remove the label "FM" because we will display all MOD types RX_BW.
{{20 * 8, (25 * 8) + 2}, "SQ:", Theme::getInstance()->fg_light->foreground},
{{5 * 8, (25 * 8) + 2}, "F_RX:", Theme::getInstance()->fg_light->foreground},
{{5 * 8, (27 * 8) + 2}, "LNA:", Theme::getInstance()->fg_light->foreground},
{{12 * 8, (27 * 8) + 2}, "VGA:", Theme::getInstance()->fg_light->foreground},
{{19 * 8, (27 * 8) + 2}, "AMP:", Theme::getInstance()->fg_light->foreground},
{{21 * 8, (31 * 8)}, "TX-IQ-CAL:", Theme::getInstance()->fg_light->foreground}};
Labels labels_WM8731{
{{17 * 8, 1 * 8}, "Boost", Color::light_grey()}};
{{17 * 8, 1 * 8}, "Boost", Theme::getInstance()->fg_light->foreground}};
Labels labels_AK4951{
{{17 * 8, 1 * 8}, "ALC", Color::light_grey()}};
{{17 * 8, 1 * 8}, "ALC", Theme::getInstance()->fg_light->foreground}};
VuMeter vumeter{
{0 * 8, 1 * 8, 2 * 8, 33 * 8},
@@ -342,18 +341,10 @@ class MicTXView : public View {
' ',
};
NumberField field_tx_iq_phase_cal_2837{
NumberField field_tx_iq_phase_cal{
{24 * 8, (33 * 8)},
2,
{0, 31}, // 5 bits IQ CAL phase adjustment.
1,
' ',
};
NumberField field_tx_iq_phase_cal_2839{
{24 * 8, (33 * 8)},
2,
{0, 63}, // 6 bits IQ CAL phasse adjustment.
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
1,
' ',
};
@@ -366,8 +357,8 @@ class MicTXView : public View {
Image tx_icon{
{6 * 8, 31 * 8 + 4, 16, 16},
&bitmap_icon_microphone,
Color::black(),
Color::black()};
Theme::getInstance()->bg_darkest->background,
Theme::getInstance()->bg_darkest->background};
MessageHandlerRegistration message_handler_lcd_sync{
Message::ID::DisplayFrameSync,
+6 -6
View File
@@ -39,12 +39,12 @@ class ModemSetupView : public View {
private:
Labels labels{
{{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()},
{{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()},
{{2 * 8, 15 * 8}, "Space: Hz", Color::light_grey()},
{{140, 15 * 8}, "Repeat:", Color::light_grey()},
{{1 * 8, 6 * 8}, "Modem preset:", Color::light_grey()},
{{2 * 8, 22 * 8}, "Serial format:", Color::light_grey()}};
{{2 * 8, 11 * 8}, "Baudrate:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 13 * 8}, "Mark: Hz", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 15 * 8}, "Space: Hz", Theme::getInstance()->fg_light->foreground},
{{140, 15 * 8}, "Repeat:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 6 * 8}, "Modem preset:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 22 * 8}, "Serial format:", Theme::getInstance()->fg_light->foreground}};
NumberField field_baudrate{
{11 * 8, 11 * 8},
+1 -2
View File
@@ -21,7 +21,6 @@
*/
#include "ui_numbers.hpp"
#include "ui_styles.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
@@ -145,7 +144,7 @@ void NumbersStationView::on_tick_second() {
armed_blink = not armed_blink;
if (armed_blink)
check_armed.set_style(&Styles::red);
check_armed.set_style(Theme::getInstance()->fg_red);
else
check_armed.set_style(&style());
+2 -2
View File
@@ -129,8 +129,8 @@ class NumbersStationView : public View {
void start_tx();
Labels labels{
{{2 * 8, 5 * 8}, "Voice: Flags:", Color::light_grey()},
{{1 * 8, 8 * 8}, "Code:", Color::light_grey()}};
{{2 * 8, 5 * 8}, "Voice: Flags:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 8 * 8}, "Code:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_voices{
{8 * 8, 1 * 8},
+5 -4
View File
@@ -36,6 +36,7 @@
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include <unistd.h>
#include <fstream>
@@ -130,7 +131,7 @@ void PlaylistView::open_file(bool prompt_save) {
}
auto open_view = nav_.push<FileLoadView>(".PPL");
open_view->push_dir(u"PLAYLIST");
open_view->push_dir(playlist_dir);
open_view->on_changed = [this](fs::path new_file_path) {
on_file_changed(new_file_path);
};
@@ -169,7 +170,7 @@ void PlaylistView::save_file(bool show_dialogs) {
void PlaylistView::add_entry(fs::path&& path) {
if (playlist_path_.empty()) {
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
playlist_path_ = next_filename_matching_pattern(playlist_dir / u"PLAY_????.PPL");
// Hack around focus getting called by ctor before parent is set.
if (parent())
@@ -387,7 +388,7 @@ PlaylistView::PlaylistView(
&waterfall,
});
ensure_directory(u"PLAYLIST");
ensure_directory(playlist_dir);
waterfall.show_audio_spectrum_view(false);
field_frequency.set_value(transmitter_model.target_frequency());
@@ -410,7 +411,7 @@ PlaylistView::PlaylistView(
if (is_active())
return;
auto open_view = nav_.push<FileLoadView>(".C*");
open_view->push_dir(u"CAPTURES");
open_view->push_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
add_entry(std::move(path));
};
+8 -8
View File
@@ -141,8 +141,8 @@ class PlaylistView : public View {
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Color::green(),
Color::black()};
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Text text_track{
{0 * 8, 3 * 16, 30 * 8, 16}};
@@ -151,37 +151,37 @@ class PlaylistView : public View {
{2 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_arrow_left,
Color::dark_grey()};
Theme::getInstance()->bg_dark->background};
NewButton button_next{
{6 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_arrow_right,
Color::dark_grey()};
Theme::getInstance()->bg_dark->background};
NewButton button_add{
{11 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_new_file,
Color::orange()};
Theme::getInstance()->fg_orange->foreground};
NewButton button_delete{
{15 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_delete,
Color::orange()};
Theme::getInstance()->fg_orange->foreground};
NewButton button_open{
{20 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_load,
Color::dark_blue()};
Theme::getInstance()->fg_blue->foreground};
NewButton button_save{
{24 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_save,
Color::dark_blue()};
Theme::getInstance()->fg_blue->foreground};
spectrum::WaterfallView waterfall{};
@@ -42,6 +42,32 @@ POCSAGTXView::~POCSAGTXView() {
baseband::shutdown();
}
void POCSAGTXView::on_remote(const PocsagTosendMessage data) {
// check if still sending or not
size_t tmp = 0;
if (data.baud == 1200) tmp = 1;
if (data.baud == 2400) tmp = 2;
options_bitrate.set_selected_index(tmp);
tmp = 0;
options_type.set_selected_index(data.type);
if (data.function == 'B') tmp = 1;
if (data.function == 'C') tmp = 2;
if (data.function == 'D') tmp = 3;
options_function.set_selected_index(tmp);
options_phase.set_selected_index(data.phase == 'P' ? 0 : 1);
field_address.set_value(data.addr);
message = (char*)data.msg;
text_message.set(message);
options_bitrate.dirty();
options_type.dirty();
options_function.dirty();
options_phase.dirty();
field_address.dirty();
text_message.dirty();
tx_view.focus();
start_tx();
}
void POCSAGTXView::on_tx_progress(const uint32_t progress, const bool done) {
if (done) {
transmitter_model.disable();
+14 -6
View File
@@ -76,15 +76,16 @@ class POCSAGTXView : public View {
void on_set_text(NavigationView& nav);
void on_tx_progress(const uint32_t progress, const bool done);
void on_remote(const PocsagTosendMessage data);
bool start_tx();
Labels labels{
{{3 * 8, 4 * 8}, "Bitrate:", Color::light_grey()},
{{3 * 8, 6 * 8}, "Address:", Color::light_grey()},
{{6 * 8, 8 * 8}, "Type:", Color::light_grey()},
{{2 * 8, 10 * 8}, "Function:", Color::light_grey()},
{{5 * 8, 12 * 8}, "Phase:", Color::light_grey()},
{{0 * 8, 14 * 8}, "Message:", Color::light_grey()}};
{{3 * 8, 4 * 8}, "Bitrate:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 6 * 8}, "Address:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 8 * 8}, "Type:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 8}, "Function:", Theme::getInstance()->fg_light->foreground},
{{5 * 8, 12 * 8}, "Phase:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 14 * 8}, "Message:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_bitrate{
{11 * 8, 4 * 8},
@@ -142,6 +143,13 @@ class POCSAGTXView : public View {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress, message.done);
}};
MessageHandlerRegistration message_handler_tx_remote{
Message::ID::PocsagTosend,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const PocsagTosendMessage*>(p);
this->on_remote(message);
}};
};
} /* namespace ui */
+14 -14
View File
@@ -43,8 +43,8 @@ class RDSPSNView : public OptionTabView {
private:
Labels labels{
{{1 * 8, 3 * 8}, "Program Service Name", Color::light_grey()},
{{2 * 8, 7 * 8}, "PSN:", Color::light_grey()}};
{{1 * 8, 3 * 8}, "Program Service Name", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 8}, "PSN:", Theme::getInstance()->fg_light->foreground}};
Button button_set{
{18 * 8, 3 * 16, 80, 32},
@@ -75,8 +75,8 @@ class RDSRadioTextView : public OptionTabView {
private:
Labels labels{
{{2 * 8, 3 * 8}, "Radiotext", Color::light_grey()},
{{1 * 8, 6 * 8}, "Text:", Color::light_grey()}};
{{2 * 8, 3 * 8}, "Radiotext", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 6 * 8}, "Text:", Theme::getInstance()->fg_light->foreground}};
Text text_radiotext{
{1 * 8, 4 * 16, 28 * 8, 16},
@@ -92,7 +92,7 @@ class RDSDateTimeView : public OptionTabView {
private:
Labels labels{
{{44, 5 * 16}, "Not yet implemented", Color::red()}};
{{44, 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}};
};
class RDSAudioView : public OptionTabView {
@@ -101,7 +101,7 @@ class RDSAudioView : public OptionTabView {
private:
Labels labels{
{{44, 5 * 16}, "Not yet implemented", Color::red()}};
{{44, 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}};
};
class RDSThread {
@@ -168,16 +168,16 @@ class RDSView : public View {
RDSAudioView view_audio{view_rect};
TabView tab_view{
{"Name", Color::cyan(), &view_PSN},
{"Text", Color::green(), &view_radiotext},
{"Time", Color::yellow(), &view_datetime},
{"Audio", Color::orange(), &view_audio}};
{"Name", Theme::getInstance()->fg_cyan->foreground, &view_PSN},
{"Text", Theme::getInstance()->fg_green->foreground, &view_radiotext},
{"Time", Theme::getInstance()->fg_yellow->foreground, &view_datetime},
{"Audio", Theme::getInstance()->fg_orange->foreground, &view_audio}};
Labels labels{
{{0 * 8, 28}, "Program type:", Color::light_grey()},
//{ { 14 * 8, 16 + 8 }, "CC:", Color::light_grey() },
{{2 * 8, 28 + 16}, "Program ID:", Color::light_grey()},
//{ { 13 * 8, 32 + 8 }, "Cov:", Color::light_grey() },
{{0 * 8, 28}, "Program type:", Theme::getInstance()->fg_light->foreground},
//{ { 14 * 8, 16 + 8 }, "CC:", Theme::getInstance()->fg_light->foreground },
{{2 * 8, 28 + 16}, "Program ID:", Theme::getInstance()->fg_light->foreground},
//{ { 13 * 8, 32 + 8 }, "Cov:",Theme::getInstance()->fg_light->foreground },
};
OptionsField options_pty{
+80 -65
View File
@@ -49,7 +49,13 @@ namespace fs = std::filesystem;
namespace ui {
void ReconView::reload_restart_recon() {
// force reload of current
change_mode(field_mode.selected_index_value());
uint8_t previous_index = current_index;
reset_indexes();
frequency_file_load();
current_index = previous_index;
handle_retune();
if (frequency_list.size() > 0) {
if (fwd) {
button_dir.set_text("FW>");
@@ -59,16 +65,16 @@ void ReconView::reload_restart_recon() {
recon_resume();
}
if (scanner_mode) {
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
file_name.set_style(Theme::getInstance()->fg_red);
button_scanner_mode.set_style(Theme::getInstance()->fg_red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
file_name.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_text("RECON");
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
file_name.set_style(Theme::getInstance()->fg_yellow);
}
}
@@ -98,24 +104,25 @@ freqman_entry& ReconView::current_entry() {
void ReconView::set_loop_config(bool v) {
continuous = v;
button_loop_config.set_style(v ? &Styles::green : &Styles::white);
button_loop_config.set_style(v ? Theme::getInstance()->fg_green : Theme::getInstance()->bg_darkest);
persistent_memory::set_recon_continuous(continuous);
}
void ReconView::recon_stop_recording(bool exiting) {
if (is_recording) {
if (field_mode.selected_index_value() == SPEC_MODULATION)
button_audio_app.set_text("RAW");
else
button_audio_app.set_text("AUDIO");
button_audio_app.set_style(&Styles::white);
record_view->stop();
button_config.set_style(&Styles::white);
is_recording = false;
// repeater mode
if (!exiting && persistent_memory::recon_repeat_recorded()) {
start_repeat();
if (field_mode.selected_index_value() == SPEC_MODULATION) {
button_audio_app.set_text("RAW");
// repeater mode
if (!exiting && persistent_memory::recon_repeat_recorded()) {
start_repeat();
}
} else {
button_audio_app.set_text("AUDIO");
}
button_audio_app.set_style(Theme::getInstance()->bg_darkest);
button_config.set_style(Theme::getInstance()->bg_darkest);
}
}
@@ -164,23 +171,23 @@ void ReconView::update_description() {
void ReconView::colorize_waits() {
// colorize wait on match
if (wait == 0) {
field_wait.set_style(&Styles::blue);
field_wait.set_style(Theme::getInstance()->fg_blue);
} else if (wait >= 500) {
field_wait.set_style(&Styles::white);
field_wait.set_style(Theme::getInstance()->bg_darkest);
} else if (wait > -500 && wait < 500) {
field_wait.set_style(&Styles::red);
field_wait.set_style(Theme::getInstance()->fg_red);
} else if (wait <= -500) {
field_wait.set_style(&Styles::green);
field_wait.set_style(Theme::getInstance()->fg_green);
}
// colorize lock time if in SPARSE mode as in continuous the lock_wait time is disarmed at first lock count
if (recon_match_mode == RECON_MATCH_SPARSE) {
if ((recon_lock_duration / STATS_UPDATE_INTERVAL) <= recon_lock_nb_match) {
field_lock_wait.set_style(&Styles::yellow);
field_lock_wait.set_style(Theme::getInstance()->fg_yellow);
} else {
field_lock_wait.set_style(&Styles::white);
field_lock_wait.set_style(Theme::getInstance()->bg_darkest);
}
} else {
field_lock_wait.set_style(&Styles::white);
field_lock_wait.set_style(Theme::getInstance()->bg_darkest);
}
}
@@ -252,17 +259,17 @@ void ReconView::recon_redraw() {
text_nb_locks.set(to_string_dec_uint(freq_lock) + "/" + to_string_dec_uint(recon_lock_nb_match));
if (freq_lock == 0) {
// NO FREQ LOCK, ONGOING STANDARD SCANNING
big_display.set_style(&Styles::white);
big_display.set_style(Theme::getInstance()->bg_darkest);
if (recon)
button_pause.set_text("<PAUSE>");
else
button_pause.set_text("<RESUME>");
} else if (freq_lock == 1 && recon_lock_nb_match != 1) {
// STARTING LOCK FREQ
big_display.set_style(&Styles::yellow);
big_display.set_style(Theme::getInstance()->fg_yellow);
button_pause.set_text("<SKPLCK>");
} else if (freq_lock >= recon_lock_nb_match) {
big_display.set_style(&Styles::green);
big_display.set_style(Theme::getInstance()->fg_green);
button_pause.set_text("<UNLOCK>");
}
}
@@ -330,7 +337,7 @@ ReconView::ReconView(NavigationView& nav)
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO", u"AUDIO",
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
record_view->set_auto_trim(false);
@@ -501,7 +508,7 @@ ReconView::ReconView(NavigationView& nav)
auto settings = receiver_model.settings();
settings.frequency_step = step_mode.selected_index_value();
if (field_mode.selected_index_value() == SPEC_MODULATION)
nav_.replace<CaptureAppView>();
nav_.replace<CaptureAppView>(settings);
else
nav_.replace<AnalogAudioView>(settings);
};
@@ -532,7 +539,7 @@ ReconView::ReconView(NavigationView& nav)
}
}
// MicTX wants Modulation and Bandwidth overrides, but that's only stored on the RX model.
// MicTX wants Frequency, Modulation and Bandwidth overrides, but that's only stored on the RX model.
nav_.replace<MicTXView>(receiver_model.settings());
};
@@ -584,18 +591,18 @@ ReconView::ReconView(NavigationView& nav)
current_entry().bandwidth = freqman_invalid_index;
current_entry().step = def_step;
big_display.set_style(&Styles::white); // Back to white color
big_display.set_style(Theme::getInstance()->bg_darkest); // Back to white color
freq_stats.set_style(&Styles::white);
freq_stats.set_style(Theme::getInstance()->bg_darkest);
freq_stats.set("0/0/0");
text_cycle.set_text("1");
text_max.set("/1");
button_scanner_mode.set_style(&Styles::white);
button_scanner_mode.set_style(Theme::getInstance()->bg_darkest);
button_scanner_mode.set_text("MANUAL");
file_name.set_style(&Styles::white);
file_name.set_style(Theme::getInstance()->bg_darkest);
file_name.set("MANUAL => " + output_file);
desc_cycle.set_style(&Styles::white);
desc_cycle.set_style(Theme::getInstance()->bg_darkest);
last_entry.modulation = freqman_invalid_index;
last_entry.bandwidth = freqman_invalid_index;
@@ -641,12 +648,12 @@ ReconView::ReconView(NavigationView& nav)
manual_mode = false;
if (scanner_mode) {
scanner_mode = false;
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_text("RECON");
button_remove.set_text("<REMOVE>");
} else {
scanner_mode = true;
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_style(Theme::getInstance()->fg_red);
button_scanner_mode.set_text("SCAN");
button_remove.set_text("<DELETE>");
}
@@ -719,7 +726,7 @@ ReconView::ReconView(NavigationView& nav)
};
// PRE-CONFIGURATION:
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_text("RECON");
file_name.set("=>");
@@ -765,14 +772,14 @@ void ReconView::frequency_file_load() {
std::string file_input = input_file; // default recon mode
if (scanner_mode) {
file_input = output_file;
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
desc_cycle.set_style(&Styles::red);
file_name.set_style(Theme::getInstance()->fg_red);
button_scanner_mode.set_style(Theme::getInstance()->fg_red);
desc_cycle.set_style(Theme::getInstance()->fg_red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
desc_cycle.set_style(&Styles::blue);
file_name.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_style(Theme::getInstance()->fg_blue);
desc_cycle.set_style(Theme::getInstance()->fg_blue);
button_scanner_mode.set_text("RECON");
}
@@ -784,7 +791,7 @@ void ReconView::frequency_file_load() {
.load_hamradios = load_hamradios,
.load_repeaters = load_repeaters};
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
file_name.set_style(&Styles::red);
file_name.set_style(Theme::getInstance()->fg_red);
desc_cycle.set("...empty file...");
frequency_list.clear();
text_cycle.set_text(" ");
@@ -792,7 +799,7 @@ void ReconView::frequency_file_load() {
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
file_name.set_style(Theme::getInstance()->fg_yellow);
}
reset_indexes();
@@ -881,13 +888,13 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
audio_output_start();
// contents of a possible recon_start_recording(), but not yet since it's only called once
if (auto_record_locked && !is_recording) {
button_audio_app.set_style(&Styles::red);
button_audio_app.set_style(Theme::getInstance()->fg_red);
if (field_mode.selected_index_value() == SPEC_MODULATION) {
button_audio_app.set_text("RAW REC");
} else
button_audio_app.set_text("WAV REC");
record_view->start();
button_config.set_style(&Styles::light_grey); // disable config while recording as it's causing an IO error pop up at exit
button_config.set_style(Theme::getInstance()->fg_light); // disable config while recording as it's causing an IO error pop up at exit
is_recording = true;
}
// FREQ IS STRONG: GREEN and recon will pause when on_statistics_update()
@@ -1099,7 +1106,7 @@ void ReconView::recon_pause() {
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio_output_start();
big_display.set_style(&Styles::white);
big_display.set_style(Theme::getInstance()->bg_darkest);
button_pause.set_text("<RESUME>"); // PAUSED, show resume
}
@@ -1111,7 +1118,7 @@ void ReconView::recon_resume() {
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
big_display.set_style(&Styles::white);
big_display.set_style(Theme::getInstance()->bg_darkest);
button_pause.set_text("<PAUSE>");
}
@@ -1148,7 +1155,7 @@ void ReconView::on_stepper_delta(int32_t v) {
}
size_t ReconView::change_mode(freqman_index_t new_mod) {
if (recon_tx || is_repeat_active())
if (recon_tx || is_repeat_active() || is_recording)
return 0;
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
@@ -1157,19 +1164,24 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
remove_child(record_view.get());
record_view.reset();
}
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"RECON_REPEAT.C16", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else if (new_mod == SPEC_MODULATION) {
if (field_mode.selected_index_value() != SPEC_MODULATION) {
audio::output::stop();
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
}
if (new_mod == SPEC_MODULATION) {
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"RECON_REPEAT.C16", captures_dir,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW", captures_dir,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_date_frequency(true);
}
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO", u"AUDIO",
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
}
@@ -1314,9 +1326,6 @@ bool ReconView::is_repeat_active() const {
void ReconView::start_repeat() {
// Prepare to send a file.
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
if (recon_tx == false) {
recon_tx = true;
@@ -1392,8 +1401,8 @@ void ReconView::start_repeat() {
std::string delay_message = "TX DELAY: " + to_string_dec_uint(delay) + "s";
// update display information
p.fill_rectangle({0, (SCREEN_H / 2) - 16, SCREEN_W, 64}, Color::light_grey());
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, Styles::red, delay_message);
p.fill_rectangle({0, (SCREEN_H / 2) - 16, SCREEN_W, 64}, Theme::getInstance()->fg_light->foreground);
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, *Theme::getInstance()->fg_red, delay_message);
// sleep 1 second
chThdSleepMilliseconds(1000);
@@ -1434,6 +1443,12 @@ void ReconView::stop_repeat(const bool do_loop) {
} else {
repeat_cur_rep = 0;
recon_tx = false;
if (persistent_memory::recon_repeat_recorded_file_mode() == RECON_REPEAT_AND_KEEP) {
// rename file here to keep
std::filesystem::path base_path = next_filename_matching_pattern(repeat_rec_path / u"REC_????.*");
rename_file(rawfile, base_path.replace_extension(u".C16"));
rename_file(rawmeta, base_path.replace_extension(u".TXT"));
}
reload_restart_recon();
progressbar.hidden(true);
set_dirty(); // fix progressbar no hiding
@@ -1445,7 +1460,7 @@ void ReconView::handle_repeat_thread_done(const uint32_t return_code) {
stop_repeat(true);
} else if (return_code == ReplayThread::READ_ERROR) {
stop_repeat(false);
repeat_file_error(u"/" + repeat_rec_path + u"/" + repeat_rec_file, "Can't open file to send.");
repeat_file_error(rawfile, "Can't open file to send.");
}
}
+9 -7
View File
@@ -27,7 +27,6 @@
#include "ui.hpp"
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
#include "freqman.hpp"
#include "analog_audio_app.hpp"
#include "audio.hpp"
@@ -38,6 +37,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "file.hpp"
#include "file_path.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_recon_settings.hpp"
@@ -181,7 +181,6 @@ class ReconView : public View {
const std::filesystem::path repeat_rec_file = u"RECON_REPEAT.C16";
const std::filesystem::path repeat_rec_meta = u"RECON_REPEAT.TXT";
const std::filesystem::path repeat_rec_path = u"CAPTURES";
const size_t repeat_read_size{16384};
const size_t repeat_buffer_count{3};
int8_t repeat_cur_rep = 0;
@@ -198,6 +197,9 @@ class ReconView : public View {
bool repeat_ready_signal{false};
bool recon_tx{false};
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
// Persisted settings.
SettingsStore ui_settings{
"recon"sv,
@@ -216,11 +218,11 @@ class ReconView : public View {
std::unique_ptr<RecordView> record_view{};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
{{3 * 8, 8 * 16}, "START END", Color::light_grey()},
{{0 * 8, (22 * 8)}, " S: ", Color::light_grey()},
{{0 * 8, (24 * 8) + 4}, "NBLCKS:x W,L: , ", Color::light_grey()},
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Color::light_grey()}};
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 8 * 16}, "START END", Theme::getInstance()->fg_light->foreground},
{{0 * 8, (22 * 8)}, " S: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, (24 * 8) + 4}, "NBLCKS:x W,L: , ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Theme::getInstance()->fg_light->foreground}};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
+15 -10
View File
@@ -30,6 +30,7 @@
#include "freqman_db.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
using namespace std;
using namespace portapack;
@@ -58,7 +59,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
_input_file = new_file_path.stem().string();
text_input_file.set(_input_file);
} else {
@@ -71,7 +72,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
_output_file = new_file_path.stem().string();
button_choose_output_name.set_text(_output_file);
} else {
@@ -103,6 +104,7 @@ void ReconSetupViewMore::save() {
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
persistent_memory::set_recon_repeat_recorded(checkbox_repeat_recorded.value());
persistent_memory::set_recon_repeat_recorded_file_mode(field_repeat_file_mode.selected_index_value());
persistent_memory::set_recon_repeat_nb(field_repeat_nb.value());
persistent_memory::set_recon_repeat_amp(checkbox_repeat_amp.value());
persistent_memory::set_recon_repeat_gain(field_repeat_gain.value());
@@ -125,6 +127,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
&checkbox_update_ranges_when_recon,
&checkbox_auto_record_locked,
&checkbox_repeat_recorded,
&field_repeat_file_mode,
&text_repeat_nb,
&field_repeat_nb,
&checkbox_repeat_amp,
@@ -134,14 +137,15 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
&field_repeat_delay});
// tx options have to be in yellow to inform the users that activating them will make the device transmit
checkbox_repeat_recorded.set_style(&Styles::yellow);
text_repeat_nb.set_style(&Styles::yellow);
field_repeat_nb.set_style(&Styles::yellow);
checkbox_repeat_amp.set_style(&Styles::yellow);
text_repeat_gain.set_style(&Styles::yellow);
field_repeat_gain.set_style(&Styles::yellow);
text_repeat_delay.set_style(&Styles::yellow);
field_repeat_delay.set_style(&Styles::yellow);
checkbox_repeat_recorded.set_style(Theme::getInstance()->fg_yellow);
field_repeat_file_mode.set_style(Theme::getInstance()->fg_yellow);
text_repeat_nb.set_style(Theme::getInstance()->fg_yellow);
field_repeat_nb.set_style(Theme::getInstance()->fg_yellow);
checkbox_repeat_amp.set_style(Theme::getInstance()->fg_yellow);
text_repeat_gain.set_style(Theme::getInstance()->fg_yellow);
field_repeat_gain.set_style(Theme::getInstance()->fg_yellow);
text_repeat_delay.set_style(Theme::getInstance()->fg_yellow);
field_repeat_delay.set_style(Theme::getInstance()->fg_yellow);
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
checkbox_load_repeaters.set_value(persistent_memory::recon_load_repeaters());
@@ -150,6 +154,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
checkbox_repeat_recorded.set_value(persistent_memory::recon_repeat_recorded());
field_repeat_file_mode.set_selected_index(persistent_memory::recon_repeat_recorded_file_mode());
checkbox_repeat_amp.set_value(persistent_memory::recon_repeat_amp());
field_repeat_nb.set_value(persistent_memory::recon_repeat_nb());
field_repeat_gain.set_value(persistent_memory::recon_repeat_gain());
+18 -10
View File
@@ -30,7 +30,6 @@
#include "ui_tabview.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "ui_styles.hpp"
// 1Mhz helper
#ifdef OneMHz
@@ -38,16 +37,19 @@
#endif
#define OneMHz 1000000
// modes
// main app mode
#define RECON_MATCH_CONTINUOUS 0
#define RECON_MATCH_SPARSE 1
// repeater mode
#define RECON_REPEAT_AND_DELETE 0
#define RECON_REPEAT_AND_KEEP 1
// statistics update interval in ms (change here if the statistics API is changing it's pace)
#define STATS_UPDATE_INTERVAL 100
// maximum lock duration
#define RECON_MAX_LOCK_DURATION 9900
#define RECON_DEF_SQUELCH -14
// default number of match to have a lock
@@ -57,7 +59,7 @@
// screen size helper
#define SCREEN_W 240
//#define SCREEN_H 320
// #define SCREEN_H 320
// recon settings nb params
#define RECON_SETTINGS_NB_PARAMS 7
@@ -150,15 +152,21 @@ class ReconSetupViewMore : public View {
Checkbox checkbox_repeat_recorded{
{1 * 8, 162},
3,
"repeater,"};
0,
""};
OptionsField field_repeat_file_mode{
{4 * 8 + 3, 165},
13,
{{"repeat,delete", RECON_REPEAT_AND_DELETE},
{"repeat,keep ", RECON_REPEAT_AND_KEEP}}};
Text text_repeat_nb{
{14 * 8, 165, 3 * 8, 22},
{20 * 8, 165, 3 * 8, 22},
"nb:"};
NumberField field_repeat_nb{
{17 * 8, 165},
{23 * 8, 165},
2,
{1, 99},
1,
@@ -217,8 +225,8 @@ class ReconSetupView : public View {
ReconSetupViewMore viewMore{nav_, view_rect};
TabView tab_view{
{"Main", Color::cyan(), &viewMain},
{"More", Color::green(), &viewMore}};
{"Main", Theme::getInstance()->fg_cyan->foreground, &viewMain},
{"More", Theme::getInstance()->fg_green->foreground, &viewMore}};
Button button_save{
{9 * 8, 255, 14 * 8, 40},
+7 -6
View File
@@ -32,6 +32,7 @@
#include "ui_receiver.hpp"
#include "ui_textentry.hpp"
#include "utility.hpp"
#include "file_path.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -176,11 +177,11 @@ void RemoteButton::paint(Painter& painter) {
// Add a border on the highlighted button.
if (has_focus() || highlighted()) {
auto r = screen_rect();
painter.draw_rectangle(r, Color::white());
painter.draw_rectangle(r, Theme::getInstance()->bg_darkest->foreground);
auto p = r.location() + Point{1, 1};
auto s = Size{r.size().width() - 2, r.size().height() - 2};
painter.draw_rectangle({p, s}, Color::light_grey());
painter.draw_rectangle({p, s}, Theme::getInstance()->fg_light->foreground);
}
};
@@ -246,7 +247,7 @@ RemoteEntryEditView::RemoteEntryEditView(
field_path.on_select = [this, &nav](TextField&) {
auto open_view = nav.push<FileLoadView>(".C*");
open_view->push_dir(u"CAPTURES");
open_view->push_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
load_path(std::move(path));
refresh_ui();
@@ -355,7 +356,7 @@ RemoteView::RemoteView(
Dim waterfall_height = waterfall_bottom - waterfall_top;
waterfall.set_parent_rect({0, waterfall_top, screen_width, waterfall_height});
ensure_directory(u"REMOTES");
ensure_directory(remotes_dir);
// Load the previously loaded remote if exists.
if (!load_remote(settings_.remote_path))
@@ -527,7 +528,7 @@ void RemoteView::new_remote() {
void RemoteView::open_remote() {
auto open_view = nav_.push<FileLoadView>(".REM");
open_view->push_dir(u"REMOTES");
open_view->push_dir(remotes_dir);
open_view->on_changed = [this](fs::path path) {
save_remote();
load_remote(std::move(path));
@@ -538,7 +539,7 @@ void RemoteView::open_remote() {
void RemoteView::init_remote() {
model_ = {"<Unnamed Remote>", {}};
reset_buttons();
set_remote_path(next_filename_matching_pattern(u"/REMOTES/REMOTE_????.REM"));
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
set_needs_save(false);
if (remote_path_.empty())
+9 -9
View File
@@ -200,15 +200,15 @@ class RemoteEntryEditView : public View {
void load_path(std::filesystem::path&& path);
Labels labels{
{{2 * 8, 1 * 16}, "Name:", Color::light_grey()},
{{2 * 8, 2 * 16}, "Path:", Color::light_grey()},
{{2 * 8, 3 * 16}, "Freq:", Color::light_grey()},
{{17 * 8, 3 * 16}, "MHz", Color::light_grey()},
{{2 * 8, 4 * 16}, "Rate:", Color::light_grey()},
{{2 * 8, 5 * 16}, "Icon:", Color::light_grey()},
{{2 * 8, 6 * 16}, "FG Color:", Color::light_grey()},
{{2 * 8, 7 * 16}, "BG Color:", Color::light_grey()},
{{8 * 8, 9 * 16}, "Button preview", Color::light_grey()},
{{2 * 8, 1 * 16}, "Name:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "Path:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 3 * 16}, "Freq:", Theme::getInstance()->fg_light->foreground},
{{17 * 8, 3 * 16}, "MHz", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 4 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Icon:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "FG Color:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "BG Color:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 9 * 16}, "Button preview", Theme::getInstance()->fg_light->foreground},
};
TextField field_name{{8 * 8, 1 * 16, 20 * 8, 1 * 16}, {}};
+9 -8
View File
@@ -26,6 +26,7 @@
#include "optional.hpp"
#include "ui_fileman.hpp"
#include "ui_freqman.hpp"
#include "file_path.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -184,16 +185,16 @@ void ScannerView::bigdisplay_update(int32_t v) {
switch (bigdisplay_current_color) {
case BDC_GREY:
big_display.set_style(&Styles::grey);
big_display.set_style(Theme::getInstance()->fg_medium);
break;
case BDC_YELLOW:
big_display.set_style(&Styles::yellow);
big_display.set_style(Theme::getInstance()->fg_yellow);
break;
case BDC_GREEN:
big_display.set_style(&Styles::green);
big_display.set_style(Theme::getInstance()->fg_green);
break;
case BDC_RED:
big_display.set_style(&Styles::red);
big_display.set_style(Theme::getInstance()->fg_red);
break;
default:
break;
@@ -279,10 +280,10 @@ void ScannerView::show_max_index() { // show total number of freqs to scan
field_current_index.set_text("<->");
if (entries.size() == FREQMAN_MAX_PER_FILE) {
text_max_index.set_style(&Styles::red);
text_max_index.set_style(Theme::getInstance()->fg_red);
text_max_index.set("/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE) + " (DB MAX!)");
} else {
text_max_index.set_style(&Styles::grey);
text_max_index.set_style(Theme::getInstance()->fg_medium);
text_max_index.set("/ " + to_string_dec_uint(entries.size()));
}
}
@@ -337,7 +338,7 @@ ScannerView::ScannerView(
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
scan_pause();
frequency_file_load(new_file_path);
} else {
@@ -412,7 +413,7 @@ ScannerView::ScannerView(
button_mic_app.on_select = [this](Button&) {
if (scan_thread)
scan_thread->stop();
// MicTX wants Modulation and Bandwidth overrides, but that's only stored on the RX model.
// MicTX wants Frequency, Modulation and Bandwidth overrides, but that's only stored on the RX model.
nav_.replace<MicTXView>(receiver_model.settings());
};
+6 -7
View File
@@ -34,11 +34,10 @@
#include "ui.hpp"
#include "ui_mictx.hpp"
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
#define STATISTICS_UPDATES_PER_SEC 10
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
#define MAX_FREQ_LOCK 10 // # of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
namespace ui {
@@ -172,12 +171,12 @@ class ScannerView : public View {
};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL:", Color::light_grey()},
{{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Color::light_grey()},
{{0 * 8, 10 * 16}, "SRCH START SEARCH END SWITCH", Color::light_grey()},
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "SRCH START SEARCH END SWITCH", Theme::getInstance()->fg_light->foreground},
{{0 * 8, (26 * 8) + 4}, "MODE:", Color::light_grey()},
{{11 * 8, (26 * 8) + 4}, "STEP:", Color::light_grey()},
{{0 * 8, (26 * 8) + 4}, "MODE:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, (26 * 8) + 4}, "STEP:", Theme::getInstance()->fg_light->foreground},
};
LNAGainField field_lna{
+1 -1
View File
@@ -56,7 +56,7 @@ void ScriptView::setup_list() {
menu_view.clear();
/*for (n = 0; n < frequencies.size(); n++) {
menu_view.add_item({ freqman_item_string(frequencies[n]), ui::Color::white(), nullptr, [this](){ on_frequency_select(); } });
menu_view.add_item({ freqman_item_string(frequencies[n]), Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ on_frequency_select(); } });
}*/
menu_view.set_parent_rect({0, 0, 240, 168});
+3 -3
View File
@@ -34,13 +34,13 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav)
ui::Painter painter;
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
ui::Color::black());
Theme::getInstance()->bg_darkest->background);
painter.draw_bitmap(
{portapack::display.width() / 2 - 8, portapack::display.height() / 2 - 8},
bitmap_icon_hackrf,
ui::Color::yellow(),
ui::Color::black());
Theme::getInstance()->bg_darkest->foreground,
Theme::getInstance()->bg_darkest->background);
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
+5 -5
View File
@@ -46,11 +46,11 @@ class SdOverUsbView : public View {
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()},
{{3 * 8, 2 * 16}, "Click Run to start the", Theme::getInstance()->bg_darkest->foreground},
{{3 * 8, 3 * 16}, "USB Mass Storage Mode.", Theme::getInstance()->bg_darkest->foreground},
{{3 * 8, 5 * 16}, "It can take up to 20s", Theme::getInstance()->bg_darkest->foreground},
{{3 * 8, 6 * 16}, "for the drive to be", Theme::getInstance()->bg_darkest->foreground},
{{3 * 8, 7 * 16}, "available.", Theme::getInstance()->bg_darkest->foreground},
};
Button button_run{
+9 -9
View File
@@ -80,11 +80,11 @@ SearchView::SearchView(
nav.push<FrequencySaveView>(entry.frequency);
};
text_mean.set_style(&Styles::grey);
text_slices.set_style(&Styles::grey);
text_rate.set_style(&Styles::grey);
progress_timers.set_style(&Styles::grey);
big_display.set_style(&Styles::grey);
text_mean.set_style(Theme::getInstance()->fg_medium);
text_slices.set_style(Theme::getInstance()->fg_medium);
text_rate.set_style(Theme::getInstance()->fg_medium);
progress_timers.set_style(Theme::getInstance()->fg_medium);
big_display.set_style(Theme::getInstance()->fg_medium);
field_frequency_min.set_step(100'000);
bind(field_frequency_min, settings_.freq_min, nav, [this](auto) {
@@ -184,7 +184,7 @@ void SearchView::do_detection() {
recent_entries_view.set_dirty();
text_infos.set("Locked ! ");
big_display.set_style(&Styles::green);
big_display.set_style(Theme::getInstance()->fg_green);
locked = true;
locked_bin = bin_max;
@@ -209,7 +209,7 @@ void SearchView::do_detection() {
recent_entries_view.set_dirty();
text_infos.set("Listening");
big_display.set_style(&Styles::grey);
big_display.set_style(Theme::getInstance()->fg_medium);
}
}
}
@@ -219,10 +219,10 @@ void SearchView::do_detection() {
search_counter++;
// Refresh red tick
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::black());
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Theme::getInstance()->fg_red->background);
if (bin_max > -1) {
last_tick_pos = (Coord)(bin_max / slices_nb);
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::red());
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Theme::getInstance()->fg_red->foreground);
}
}
+6 -7
View File
@@ -26,7 +26,6 @@
#include "radio_state.hpp"
#include "spectrum_color_lut.hpp"
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
namespace ui {
@@ -162,12 +161,12 @@ class SearchView : public View {
RecentEntriesView<RecentEntries<SearchRecentEntry>> recent_entries_view{columns, recent};
Labels labels{
{{1 * 8, 0}, "Min: Max: LNA VGA", Color::light_grey()},
{{1 * 8, 4 * 8}, "Trig: /255 Mean: /255", Color::light_grey()},
{{1 * 8, 6 * 8}, "Slices: /32 Rate: Hz", Color::light_grey()},
{{6 * 8, 10 * 8}, "Timer Status", Color::light_grey()},
{{1 * 8, 25 * 8}, "Accuracy +/-4.9kHz", Color::light_grey()},
{{26 * 8, 25 * 8}, "MHz", Color::light_grey()}};
{{1 * 8, 0}, "Min: Max: LNA VGA", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 4 * 8}, "Trig: /255 Mean: /255", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 6 * 8}, "Slices: /32 Rate: Hz", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 10 * 8}, "Timer Status", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 25 * 8}, "Accuracy +/-4.9kHz", Theme::getInstance()->fg_light->foreground},
{{26 * 8, 25 * 8}, "MHz", Theme::getInstance()->fg_light->foreground}};
FrequencyField field_frequency_min{
{1 * 8, 1 * 16}};
+206 -36
View File
@@ -4,6 +4,7 @@
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
*
* This file is part of PortaPack.
@@ -30,6 +31,7 @@
#include "ui_receiver.hpp"
#include "ui_touch_calibration.hpp"
#include "ui_text_editor.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
@@ -40,14 +42,16 @@ using namespace lpc43xx;
using namespace portapack;
#include "file.hpp"
#include "file_path.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
#include "ui_styles.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "cpld_update.hpp"
#include "config_mode.hpp"
extern ui::SystemView* system_view_ptr;
namespace pmem = portapack::persistent_memory;
namespace ui {
@@ -225,9 +229,9 @@ SetRadioView::SetRadioView(
value_source_frequency.set(clock_manager.get_freq());
// Make these Text controls look like Labels.
label_source.set_style(&Styles::light_grey);
value_source.set_style(&Styles::light_grey);
value_source_frequency.set_style(&Styles::light_grey);
label_source.set_style(Theme::getInstance()->fg_light);
value_source.set_style(Theme::getInstance()->fg_light);
value_source_frequency.set_style(Theme::getInstance()->fg_light);
SetFrequencyCorrectionModel model{
static_cast<int8_t>(pmem::correction_ppb() / 1000), 0};
@@ -326,6 +330,10 @@ SetUIView::SetUIView(NavigationView& nav) {
if (audio::speaker_disable_supported()) {
add_child(&toggle_speaker);
}
if (battery::BatteryManagement::isDetected()) {
add_child(&toggle_battery_icon);
add_child(&toggle_battery_text);
}
checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen());
checkbox_showsplash.set_value(pmem::config_splash());
@@ -352,6 +360,8 @@ SetUIView::SetUIView(NavigationView& nav) {
toggle_speaker.set_value(!pmem::ui_hide_speaker());
toggle_mute.set_value(!pmem::ui_hide_mute());
toggle_fake_brightness.set_value(!pmem::ui_hide_fake_brightness());
toggle_battery_icon.set_value(!pmem::ui_hide_battery_icon());
toggle_battery_text.set_value(!pmem::ui_hide_numeric_battery());
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
button_save.on_select = [&nav, this](Button&) {
@@ -379,6 +389,8 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_ui_hide_speaker(!toggle_speaker.value());
pmem::set_ui_hide_mute(!toggle_mute.value());
pmem::set_ui_hide_fake_brightness(!toggle_fake_brightness.value());
pmem::set_ui_hide_battery_icon(!toggle_battery_icon.value());
pmem::set_ui_hide_numeric_battery(!toggle_battery_text.value());
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
send_system_refresh();
@@ -564,23 +576,23 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
&button_return,
});
text_pmem_status.set_style(&Styles::yellow);
text_pmem_status.set_style(Theme::getInstance()->fg_yellow);
check_use_sdcard_for_pmem.set_value(pmem::should_use_sdcard_for_pmem());
check_use_sdcard_for_pmem.on_select = [this](Checkbox&, bool v) {
File pmem_flag_file_handle;
if (v) {
if (fs::file_exists(PMEM_FILEFLAG)) {
if (fs::file_exists(settings_dir / PMEM_FILEFLAG)) {
text_pmem_status.set("P.Mem flag file present.");
} else {
auto error = pmem_flag_file_handle.create(PMEM_FILEFLAG);
auto error = pmem_flag_file_handle.create(settings_dir / PMEM_FILEFLAG);
if (error)
text_pmem_status.set("Error creating P.Mem File!");
else
text_pmem_status.set("P.Mem flag file created.");
}
} else {
auto result = delete_file(PMEM_FILEFLAG);
auto result = delete_file(settings_dir / PMEM_FILEFLAG);
if (result.code() != FR_OK)
text_pmem_status.set("Error deleting P.Mem flag!");
else
@@ -633,13 +645,17 @@ void SetPersistentMemoryView::focus() {
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({&labels,
&field_tone_mix,
&checkbox_beep_on_packets,
&button_save,
&button_cancel});
field_tone_mix.set_value(pmem::tone_mix());
checkbox_beep_on_packets.set_value(pmem::beep_on_packets());
button_save.on_select = [&nav, this](Button&) {
pmem::set_tone_mix(field_tone_mix.value());
pmem::set_beep_on_packets(checkbox_beep_on_packets.value());
audio::output::update_audio_mute();
nav.pop();
};
@@ -716,13 +732,13 @@ AppSettingsView::AppSettingsView(
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
ensure_directory(SETTINGS_DIR);
ensure_directory(settings_dir);
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
auto path = (std::filesystem::path)SETTINGS_DIR / entry.path();
for (const auto& entry : std::filesystem::directory_iterator(settings_dir, u"*.ini")) {
auto path = settings_dir / entry.path();
menu_view.add_item({path.filename().string().substr(0, 26),
ui::Color::dark_cyan(),
ui::Theme::getInstance()->fg_darkcyan->foreground,
&bitmap_icon_file_text,
[this, path](KeyEvent) {
nav_.push<TextEditorView>(path);
@@ -758,31 +774,44 @@ void SetConfigModeView::focus() {
button_save.focus();
}
/* SetFakeBrightnessView ************************************/
/* SetDisplayView ************************************/
SetFakeBrightnessView::SetFakeBrightnessView(NavigationView& nav) {
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetFakeBrightnessView::focus() {
void SetDisplayView::focus() {
button_save.focus();
}
@@ -800,6 +829,7 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
&field_green_level,
&field_blue_level,
&button_save,
&button_reset,
&button_cancel});
button_sample.set_focusable(false);
@@ -817,6 +847,13 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
field_green_level.on_change = color_changed_fn;
field_blue_level.on_change = color_changed_fn;
button_reset.on_select = [&nav, this](Button&) {
field_red_level.set_value(127);
field_green_level.set_value(127);
field_blue_level.set_value(127);
set_dirty();
};
button_save.on_select = [&nav, this](Button&) {
Color c = Color(field_red_level.value(), field_green_level.value(), field_blue_level.value());
pmem::set_menu_color(c);
@@ -833,30 +870,163 @@ void SetMenuColorView::focus() {
button_save.focus();
}
/* SetAutoStartView ************************************/
SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&options});
button_save.on_select = [&nav, this](Button&) {
autostart_app = "";
if (selected != 0) {
auto it = full_app_list.find(selected);
if (it != full_app_list.end())
autostart_app = it->second;
}
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
// options
i = 0;
OptionsField::option_t o{"-none-", i};
opts.emplace_back(o);
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
i++;
o = {app.displayName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.id);
if (autostart_app == app.id) selected = i;
}
ExternalItemsMenuLoader::load_all_external_items_callback([this](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
i++;
OptionsField::option_t o = {app.appFriendlyName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.appCallName);
if (autostart_app == app.appCallName) selected = i;
});
options.set_options(opts);
options.on_change = [this](size_t, OptionsField::value_t v) {
selected = v;
};
options.set_selected_index(selected);
}
void SetAutostartView::focus() {
options.focus();
}
/* SetThemeView ************************************/
SetThemeView::SetThemeView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&options,
&checkbox_menuset});
button_save.on_select = [&nav, this](Button&) {
if (selected < Theme::ThemeId::MAX && (uint8_t)selected != portapack::persistent_memory::ui_theme_id()) {
portapack::persistent_memory::set_ui_theme_id((uint8_t)selected);
Theme::SetTheme((Theme::ThemeId)selected);
if (checkbox_menuset.value()) {
pmem::set_menu_color(Theme::getInstance()->bg_medium->background);
}
send_system_refresh();
}
nav.pop();
};
checkbox_menuset.set_value(true);
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
options.on_change = [this](size_t, OptionsField::value_t v) {
selected = v;
};
options.set_selected_index(portapack::persistent_memory::ui_theme_id());
}
void SetThemeView::focus() {
options.focus();
}
/* SetBatteryView ************************************/
SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BATT_MAX17055) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
send_system_refresh();
nav.pop();
};
button_reset.on_select = [&nav, this](Button&) {
if (battery::BatteryManagement::reset_learned())
nav.display_modal("Reset", "Battery parameters reset");
else
nav.display_modal("Error", "Error parameter reset");
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetBatteryView::focus() {
button_cancel.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
if (pmem::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
}
add_items({
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<AppSettingsView>(); }},
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push<SetAudioView>(); }},
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push<TouchCalibrationView>(); }},
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [&nav]() { nav.push<SetConfigModeView>(); }},
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetConverterSettingsView>(); }},
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav]() { nav.push<SetDateTimeView>(); }},
{"Encoder Dial", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SetEncoderDialView>(); }},
{"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetFrequencyCorrectionView>(); }},
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<SetPersistentMemoryView>(); }},
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetRadioView>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push<SetUIView>(); }},
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push<SetQRCodeView>(); }},
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetFakeBrightnessView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetMenuColorView>(); }},
});
SettingsMenuView::SettingsMenuView(NavigationView& nav)
: nav_(nav) {
set_max_rows(2); // allow wider buttons
}
void SettingsMenuView::on_populate() {
if (pmem::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_items({
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [this]() { nav_.push<AppSettingsView>(); }},
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [this]() { nav_.push<SetAudioView>(); }},
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [this]() { nav_.push<TouchCalibrationView>(); }},
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [this]() { nav_.push<SetConfigModeView>(); }},
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetConverterSettingsView>(); }},
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [this]() { nav_.push<SetDateTimeView>(); }},
{"Encoder Dial", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetEncoderDialView>(); }},
{"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetFrequencyCorrectionView>(); }},
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<SetPersistentMemoryView>(); }},
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetRadioView>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
//{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
}
} /* namespace ui */
+213 -71
View File
@@ -4,6 +4,7 @@
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
*
* This file is part of PortaPack.
@@ -64,14 +65,14 @@ class SetDateTimeView : public View {
std::vector<option_t> month_options = {{"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct", 10}, {"Nov", 11}, {"Dec", 12}};
Labels labels{
{{1 * 8, 1 * 16}, "Adjust the RTC clock date &", Color::light_grey()},
{{1 * 8, 2 * 16}, "time. If clock resets after", Color::light_grey()},
{{1 * 8, 3 * 16}, "reboot, coin batt. is dead. ", Color::light_grey()},
{{1 * 8, 5 * 16 - 2}, "YYYY-MM-DD HH:MM:SS DoW DoY", Color::grey()},
{{5 * 8, 6 * 16}, "- - : :", Color::light_grey()},
{{1 * 8, 11 * 16}, "DST adds 1 hour to RTC time.", Color::light_grey()},
{{0 * 8, 12 * 16}, "Start: 0:00 on Nth DDD in", Color::light_grey()},
{{0 * 8, 13 * 16}, "End: 1:00 on Nth DDD in", Color::light_grey()}};
{{1 * 8, 1 * 16}, "Adjust the RTC clock date &", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "time. If clock resets after", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "reboot, coin batt. is dead. ", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 5 * 16 - 2}, "YYYY-MM-DD HH:MM:SS DoW DoY", Theme::getInstance()->fg_medium->foreground},
{{5 * 8, 6 * 16}, "- - : :", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 11 * 16}, "DST adds 1 hour to RTC time.", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 12 * 16}, "Start: 0:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, "End: 1:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}};
NumberField field_year{
{1 * 8, 6 * 16},
@@ -209,8 +210,8 @@ class SetRadioView : public View {
""};
Labels labels_correction{
{{2 * 8, 3 * 16}, "Frequency correction:", Color::light_grey()},
{{6 * 8, 4 * 16}, "PPM", Color::light_grey()},
{{2 * 8, 3 * 16}, "Frequency correction:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 4 * 16}, "PPM", Theme::getInstance()->fg_light->foreground},
};
NumberField field_ppm{
@@ -232,13 +233,13 @@ class SetRadioView : public View {
SymField::Type::Dec};
Labels labels_clkout_khz{
{{26 * 8, 6 * 16}, "kHz", Color::light_grey()}};
{{26 * 8, 6 * 16}, "kHz", Theme::getInstance()->fg_light->foreground}};
Labels labels_bias{
{{4 * 8 + 4, 8 * 16}, "CAUTION: Ensure that all", Color::red()},
{{5 * 8 + 0, 9 * 16}, "devices attached to the", Color::red()},
{{6 * 8 + 0, 10 * 16}, "antenna connector can", Color::red()},
{{6 * 8 + 4, 11 * 16}, "accept a DC voltage!", Color::red()}};
{{4 * 8 + 4, 8 * 16}, "CAUTION: Ensure that all", Theme::getInstance()->error_dark->foreground},
{{5 * 8 + 0, 9 * 16}, "devices attached to the", Theme::getInstance()->error_dark->foreground},
{{6 * 8 + 0, 10 * 16}, "antenna connector can", Theme::getInstance()->error_dark->foreground},
{{6 * 8 + 4, 11 * 16}, "accept a DC voltage!", Theme::getInstance()->error_dark->foreground}};
Checkbox check_bias{
{18, 12 * 16},
@@ -318,41 +319,49 @@ class SetUIView : public View {
"Back button in menu"};
Labels labels{
{{3 * 8, 13 * 16}, "Show/Hide Status Icons", Color::light_grey()},
{{3 * 8, 13 * 16}, "Show/Hide Status Icons", Theme::getInstance()->fg_light->foreground},
};
ImageToggle toggle_camera{
{6 * 8, 14 * 16 + 2, 16, 16},
{2 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_camera};
ImageToggle toggle_sleep{
{8 * 8, 14 * 16 + 2, 16, 16},
{4 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_sleep};
ImageToggle toggle_stealth{
{10 * 8, 14 * 16 + 2, 16, 16},
{6 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_stealth};
ImageToggle toggle_converter{
{12 * 8, 14 * 16 + 2, 16, 16},
{8 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_upconvert};
ImageToggle toggle_bias_tee{
{14 * 8, 14 * 16 + 2, 16, 16},
{10 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_biast_off};
ImageToggle toggle_clock{
{16 * 8, 14 * 16 + 2, 8, 16},
{12 * 8, 14 * 16 + 2, 8, 16},
&bitmap_icon_clk_ext};
ImageToggle toggle_mute{
{17 * 8, 14 * 16 + 2, 16, 16},
{13 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_and_headphones_mute};
ImageToggle toggle_speaker{
{19 * 8, 14 * 16 + 2, 16, 16},
{15 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_mute};
ImageToggle toggle_battery_icon{
{17 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_icon};
ImageToggle toggle_battery_text{
{19 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_fake_brightness{
{21 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
@@ -381,8 +390,8 @@ class SetSDCardView : public View {
private:
Labels labels{
// 01234567890123456789012345678
{{1 * 8, 120 - 48}, " HIGH SPEED SDCARD IO ", Color::light_grey()},
{{1 * 8, 120 - 32}, " May or may not work !! ", Color::light_grey()}};
{{1 * 8, 120 - 48}, " HIGH SPEED SDCARD IO ", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 120 - 32}, " May or may not work !! ", Theme::getInstance()->fg_light->foreground}};
Checkbox checkbox_sdcard_speed{
{2 * 8, 120},
@@ -416,11 +425,11 @@ class SetConverterSettingsView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Options for working with", Color::light_grey()},
{{1 * 8, 2 * 16}, "up/down converter hardware", Color::light_grey()},
{{1 * 8, 3 * 16}, "like a Ham It Up.", Color::light_grey()},
{{2 * 8, 9 * 16 - 2}, "Conversion frequency:", Color::light_grey()},
{{18 * 8, 10 * 16}, "MHz", Color::light_grey()},
{{1 * 8, 1 * 16}, "Options for working with", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "up/down converter hardware", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "like a Ham It Up.", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 9 * 16 - 2}, "Conversion frequency:", Theme::getInstance()->fg_light->foreground},
{{18 * 8, 10 * 16}, "MHz", Theme::getInstance()->fg_light->foreground},
};
Checkbox check_show_converter{
@@ -460,13 +469,13 @@ class SetFrequencyCorrectionView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Frequency correction allows", Color::light_grey()},
{{1 * 8, 2 * 16}, "RX and TX frequencies to be", Color::light_grey()},
{{1 * 8, 3 * 16}, "adjusted for all apps.", Color::light_grey()},
{{2 * 8, 6 * 16}, "RX Adjustment Frequency", Color::light_grey()},
{{18 * 8, 7 * 16}, "MHz", Color::light_grey()},
{{2 * 8, 9 * 16}, "TX Adjustment Frequency", Color::light_grey()},
{{18 * 8, 10 * 16}, "MHz", Color::light_grey()},
{{1 * 8, 1 * 16}, "Frequency correction allows", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "RX and TX frequencies to be", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "adjusted for all apps.", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "RX Adjustment Frequency", Theme::getInstance()->fg_light->foreground},
{{18 * 8, 7 * 16}, "MHz", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 9 * 16}, "TX Adjustment Frequency", Theme::getInstance()->fg_light->foreground},
{{18 * 8, 10 * 16}, "MHz", Theme::getInstance()->fg_light->foreground},
};
OptionsField opt_rx_correction_mode{
@@ -503,10 +512,14 @@ class SetAudioView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Controls the volume of the", Color::light_grey()},
{{1 * 8, 2 * 16}, "tone when transmitting in", Color::light_grey()},
{{1 * 8, 3 * 16}, "Soundboard or Mic apps.", Color::light_grey()},
{{2 * 8, 5 * 16}, "Tone key mix: %", Color::light_grey()},
{{1 * 8, 1 * 16}, "Controls the volume of the", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "tone when transmitting in", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "Soundboard or Mic apps:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Tone key mix: %", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 8 * 16}, "Controls whether apps should", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 9 * 16}, "beep on speaker & headphone", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 10 * 16}, "when a packet is received", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 11 * 16}, "(not all apps support this):", Theme::getInstance()->fg_light->foreground},
};
NumberField field_tone_mix{
@@ -516,6 +529,11 @@ class SetAudioView : public View {
1,
'0'};
Checkbox checkbox_beep_on_packets{
{3 * 8, 13 * 16},
16,
"Beep on RX packets"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -536,8 +554,8 @@ class SetQRCodeView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Change the size of the QR", Color::light_grey()},
{{1 * 8, 2 * 16}, "code shown in Radiosonde.", Color::light_grey()},
{{1 * 8, 1 * 16}, "Change the size of the QR", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "code shown in Radiosonde.", Theme::getInstance()->fg_light->foreground},
};
Checkbox checkbox_bigger_qr{
@@ -568,14 +586,14 @@ class SetEncoderDialView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Adjusts sensitivity to dial", Color::light_grey()},
{{1 * 8, 2 * 16}, "rotation position (number of", Color::light_grey()},
{{1 * 8, 3 * 16}, "steps per full rotation):", Color::light_grey()},
{{2 * 8, 5 * 16}, "Dial sensitivity:", Color::light_grey()},
{{1 * 8, 8 * 16}, "Adjusts sensitivity to dial", Color::light_grey()},
{{1 * 8, 9 * 16}, "rotation rate (default 1", Color::light_grey()},
{{1 * 8, 10 * 16}, "means no rate dependency):", Color::light_grey()},
{{3 * 8, 12 * 16}, "Rate multiplier:", Color::light_grey()},
{{1 * 8, 1 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "rotation position (number of", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "steps per full rotation):", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Dial sensitivity:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 8 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 9 * 16}, "rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 10 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 12 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_encoder_dial_sensitivity{
@@ -612,9 +630,9 @@ class SetPersistentMemoryView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Save persistent memory on SD", Color::light_grey()},
{{1 * 8, 2 * 16}, "card. Needed when device has", Color::light_grey()},
{{1 * 8, 3 * 16}, "dead/missing coin battery.", Color::light_grey()},
{{1 * 8, 1 * 16}, "Save persistent memory on SD", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "card. Needed when device has", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "dead/missing coin battery.", Theme::getInstance()->fg_light->foreground},
};
Text text_pmem_status{
@@ -654,7 +672,7 @@ class AppSettingsView : public View {
NavigationView& nav_;
Labels labels{
{{0, 4}, "Select file to edit:", Color::white()}};
{{0, 4}, "Select file to edit:", Theme::getInstance()->bg_darkest->foreground}};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
@@ -671,9 +689,9 @@ class SetConfigModeView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Controls whether firmware", Color::light_grey()},
{{1 * 8, 2 * 16}, "will enter Config Mode", Color::light_grey()},
{{1 * 8, 3 * 16}, "after a boot failure.", Color::light_grey()},
{{1 * 8, 1 * 16}, "Controls whether firmware", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "will enter Config Mode", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "after a boot failure.", Theme::getInstance()->fg_light->foreground},
};
Checkbox checkbox_config_mode_enabled{
@@ -693,20 +711,20 @@ class SetConfigModeView : public View {
using portapack::persistent_memory::fake_brightness_level_options;
class SetFakeBrightnessView : public View {
class SetDisplayView : public View {
public:
SetFakeBrightnessView(NavigationView& nav);
SetDisplayView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Brightness"; };
std::string title() const override { return "Display"; };
private:
Labels labels{
{{1 * 8, 1 * 16}, "Limits screen brightness", Color::light_grey()},
{{1 * 8, 2 * 16}, "(has a small performance", Color::light_grey()},
{{1 * 8, 3 * 16}, "impact when enabled).", Color::light_grey()},
{{2 * 8, 8 * 16}, "Brightness:", Color::light_grey()},
{{1 * 8, 1 * 16}, "Limits screen brightness", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
@@ -721,6 +739,11 @@ class SetFakeBrightnessView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
23,
"Invert colors (For IPS)"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -743,10 +766,10 @@ class SetMenuColorView : public View {
void paint_sample();
Labels labels{
{{3 * 8, 1 * 16}, "Menu Button Color Scheme", Color::light_grey()},
{{2 * 8, 8 * 16}, "Red Level:", Color::light_grey()},
{{2 * 8, 9 * 16}, "Green Level:", Color::light_grey()},
{{2 * 8, 10 * 16}, "Blue Level:", Color::light_grey()},
{{3 * 8, 1 * 16}, "Menu Button Color Scheme", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Red Level:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 9 * 16}, "Green Level:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Blue Level:", Theme::getInstance()->fg_light->foreground},
};
NewButton button_sample{
@@ -779,6 +802,11 @@ class SetMenuColorView : public View {
' ',
};
Button button_reset{
{2 * 8, 13 * 16, 12 * 8, 32},
"Reset",
};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -789,11 +817,125 @@ class SetMenuColorView : public View {
};
};
class SetAutostartView : public View {
public:
SetAutostartView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Autostart"; };
private:
int32_t i = 0;
std::string autostart_app{""};
OptionsField::options_t opts{};
std::map<int32_t, std::string> full_app_list{}; // looking table
int32_t selected = 0;
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
Labels labels{
{{1 * 8, 1 * 16}, "Select app to start on boot", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "(an SD Card is required)", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{}};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SetThemeView : public View {
public:
SetThemeView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Theme"; };
private:
int32_t selected = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Select a theme.", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "Restart PP to fully apply!", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{
{"Default - Grey", 0},
{"Yellow", 1},
{"Aqua", 2},
{"Green", 3},
{"Red", 4},
}};
Checkbox checkbox_menuset{
{2 * 8, 6 * 16},
23,
"Set Menu color too"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SetBatteryView : public View {
public:
SetBatteryView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery"; };
private:
int32_t selected = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
Labels labels2{
{{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Checkbox checkbox_overridebatt{
{2 * 8, 4 * 16},
23,
"Override"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 8 * 16, 12 * 8, 32},
"Reset",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
std::string title() const override { return "Settings"; };
private:
NavigationView& nav_;
void on_populate() override;
};
} /* namespace ui */

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